如何使 RadioButton 文本和附加标签保持对齐?

How can I keep the RadioButton text and an additional Label in alignment?

我正在使用 .NET 3.5 和 Windows Forms 开发一个 C# 项目。我需要设计一个包含多个选项的决策步骤,需要一些解释性文字。为此,我想要一组 RadioButtons 来选择一个选项,然后是一个附加标签,每个标签都包含解释。

我想让单选按钮的标签和包含解释性文本的标签保持对齐 - 我在图像中添加了红线来说明这一点。我可能会调整第二个标签上的一些边距或其他设置,但一旦用户选择不同的主题或更改一些其他设置,这可能会开始看起来很奇怪。执行此操作的规范(也是最可靠)方法是什么?

最简单的开箱即用解决方案(在我看来)是使用 3 个控件而不是 2 个:一个单选按钮(文本设置为“”),一个标签(放在旁边单选按钮)和另一个标签(在它们下面)。这将使您更容易在设计器中进行配置,但(更重要的是)更简单的 运行 时间评估和调整,如有必要,以便在样式更改时保持对齐。

我知道这会带走单击标签以 select 单选按钮的好处,但如果需要,您可以在标签的 Click 事件中添加该行为。

或者,您可以创建一个包含无文本单选按钮和标签的 UserControl,并在公开标签位置的同时处理 UserControl 中的行为。

您可以添加一个不可见的虚拟标签,其文本与单选按钮相同。然后,获取该标签的长度,并计算解释标签的正确位置。

labelDummy.Text = radioButton1.Text;
labelExplanation.Left = radioButton1.Right - labelDummy.Width;

然而,这似乎仍然有一些像素偏差,即使我将标签的边距设置为 0,也许一些额外的调整可以解决这个问题。这是显示我的意思的屏幕截图。标签的背景是绿色的,以便能够看到额外的边距。

如果您不关心单选按钮的文本是粗体,您可以将其标签设置为多行字符串,并将 CheckAlign 设置为 TopLeft:

radioButton2.CheckAlign = ContentAlignment.TopLeft;
radioButton2.Text = @"Radiobutton

Explanation text";

不知道为什么我没有早点想到这个,但下面的方法似乎有效:

  1. 使用具有两列的 TableLayoutPanel,并设置为自动调整宽度。
  2. 将所有 RadioButton 放在第一列并将它们设置为跨越两列。
  3. 将所有标签放在第二列中,将所有边距设置为 0。
  4. 在布局末尾的附加行中添加一个不带文本的禁用但可见 (!) "spacer" RadioButton。
  5. 显示表格时,将第一列转换为固定大小并隐藏"spacer"。

关键点似乎是 "spacer" 最初必须可见 - 否则该列的大小将为 0。

这是我在设计器中的测试表单:

为了更改布局,我使用了以下加载处理程序:

    private void TestForm_Load(object sender, EventArgs e)
    {
        // find the column with the spacer and back up its width
        int column = tableLayoutPanel.GetColumn(radioButtonSpacer);
        int width = tableLayoutPanel.GetColumnWidths()[column];
        // hide the spacer
        radioButtonSpacer.Visible = false;
        // set the column to the fixed width retrieved before
        tableLayoutPanel.ColumnStyles[column].SizeType = SizeType.Absolute;
        tableLayoutPanel.ColumnStyles[column].Width = width;
    }

这是运行时的结果:

您的问题归结为两个部分问题:

  • RadioButton(或提前考虑时的 CheckBox)有多大..
  • 字形和文本之间的差距有多大。

第一个问题很简单:

Size  s = RadioButtonRenderer.GetGlyphSize(graphics, 
          System.Windows.Forms.VisualStyles.RadioButtonState.CheckedNormal);

..使用合适的图形对象。请注意,我使用 RadioButtonState CheckedNormal,因为我不希望标签在选中或未选中按钮时以不同方式对齐..

第二个绝非微不足道。间隙可能是常数,也可能不是常数,并且在字形的左侧还有另一个间隙!如果我真的想把它弄好,我想我会写一个例程来 测量 启动时的文本偏移量:

public Form1()
{
    InitializeComponent();

    int gapRB = getXOffset(radioButton1);
    int gapLB = getXOffset(label1);
    label1.Left = radioButton1.Left + gapRB - gapLB;
}

这里是测量函数。请注意,它甚至不使用 Glyph 度量。另请注意,仅测量 RadioButton 的文本偏移量是不够的。您还需要测量Label的偏移量!

int getXOffset(Control ctl)
{
    int offset = -1;
    string save = ctl.Text; Color saveC = ctl.ForeColor; Size saveSize = ctl.Size;
    ContentAlignment saveCA = ContentAlignment.MiddleLeft;
    if  (ctl is Label)
    {
        saveCA = ((Label)ctl).TextAlign;
        ((Label)ctl).TextAlign = ContentAlignment.BottomLeft;
    }

    using (Bitmap bmp = new Bitmap(ctl.ClientSize.Width, ctl.ClientSize.Height))
    using (Graphics G = ctl.CreateGraphics() )
    {
        ctl.Text = "_";
        ctl.ForeColor = Color.Red;
        ctl.DrawToBitmap(bmp, ctl.ClientRectangle);
        int x = 0; 
        while (offset < 0 && x < bmp.Width - 1)
        {
            for (int y = bmp.Height-1; y > bmp.Height / 2; y--)
            {
                Color c = bmp.GetPixel(x, y);
                if (c.R > 128 && c.G == 0)     { offset = x; break; }
            }
            x++;
        }
    }
    ctl.Text = save;  ctl.ForeColor = saveC;   ctl.Size = saveSize;
    if (ctl is Label)  { ((Label)ctl).TextAlign =  saveCA;   }

    return offset;
}

现在文本可以完美对齐像素..:[=​​26=]

请注意,我使用了我的表单中的两个原始控件。因此,大部分代码只是简单地存储和恢复我需要为测量操作的属性;你可以通过使用两个假人来节省几行。另请注意,我编写了例程,以便它可以测量 RadioButtonsLabels,也可能测量 CheckBoxes..

值得吗?你决定..!

PS:您还可以将 RadioButtonLabel 文本合二为一。这会产生有趣的副作用,即整个文本将是可点击..:[=​​26=]

这是所有者绘制 CheckBox 的快速但粗略的实现:通过设置 AutoSize = false 并将真实文本与额外文本一起添加到标签中并用例如分隔符分隔来准备它。 “§”。随意更改此设置,也许使用标签控件..

我清除了文本以防止它绘制它,然后我决定了一个偏移量。要测量它,您可以使用上面的 GetGlyphSize。请注意 DrawString 方法如何支持嵌入的 '\n' 字符。

标签包含此字符串:

A Rose is a Rose is a Rose..§A Rose is a rose is a rose is a rose is / A rose is what Moses supposes his toes is / Couldn't be a lily or a taffy daphi dilli / It's gotta be a rose cuz it rhymes with mose!

而我的屏幕截图我实际上使用了这一行:

e.Graphics.DrawString(texts[1].Replace("/ ", "\n"), ...

这里是 Paint 事件:

private void checkBox1_Paint(object sender, PaintEventArgs e)
{
    checkBox1.Text = "";
    string[] texts =  checkBox1.Tag.ToString().Split('§');

    Font font1 = new Font(checkBox1.Font, FontStyle.Regular);

    e.Graphics.DrawString(texts[0], checkBox1.Font, Brushes.Black, 25, 3);
    if (texts.Length > 0)
    {
        SizeF s = e.Graphics.MeasureString(texts[1], checkBox1.Font, checkBox1.Width - 25);
        checkBox1.Height = (int) s.Height + 30;
        e.Graphics.DrawString(texts[1], font1, Brushes.Black, 
                              new RectangleF(new PointF(25, 25), s));
    }
}