如何在自定义的WinForms按钮中使用OnPaint绘制可缩放的勾号?

编程语言 2026-07-12

我在WinForms中创建一个自定义的圆形按钮。我可以使用 DrawEllipse 成功绘制圆形。然而,我不知道如何计算正确的等比例坐标,在按钮内部绘制一个可缩放的勾号,以便它能正确调整大小。如何计算勾号的等比例点,使其与按钮的大小正确缩放?

public class MyButton : Button
{
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Graphics graphics = e.Graphics;
        graphics.SmoothingMode = SmoothingMode.AntiAlias;
        Rectangle rectangle = new Rectangle();
        rectangle.Height = 111;
        rectangle.Width = 100;
        graphics.FillEllipse(Brushes.White, rectangle);
        graphics.DrawEllipse(Pens.White, rectangle);
    }
}

解决方案

以下是在按钮内绘制可缩放文本的做法:

if (Text is { Length: > 0 }) {
    SizeF textSize = e.Graphics.MeasureString(Text, Font);
    float scale = Math.Min(Width / textSize.Width, Height / textSize.Height);
    e.Graphics.TranslateTransform((Width - textSize.Width * scale) / 2, (Height - textSize.Height * scale) / 2);
    e.Graphics.ScaleTransform(scale, scale);
    using var textBrush = new SolidBrush(ForeColor);
    e.Graphics.DrawString(Text, Font, textBrush, (RectangleF)ClientRectangle);
}

然后你可以在按钮的 Text 属性中使用以下其中一个Unicode勾号:

  • ✓ U+2713(勾号)
  • ✔ U+2714(粗勾号)
  • ✅ U+2705(白色粗勾号)
  • ☑ U+2611(带勾的选票框)
  • 🗸 U+1F5F8(轻勾号)
  • 🗹 U+1F5F9(带粗勾的选票框)

并非所有字体都包含这些字符。此外,某些字体可能需要对缩放系数和定位进行一些调整。


下面是我的椭圆形按钮的完整代码。请注意,它暴露了一些属性,允许你在属性编辑器中更改按钮的外观:

public class EllipseButton : Control
{
    private Color _borderColor = Color.DimGray;
    [Category("Appearance"), DefaultValue(typeof(Color), "DimGray")]
    public Color BorderColor
    {
        get => _borderColor;
        set {
            _borderColor = value;
            Invalidate();
        }
    }

    private Color _buttonBackColor = Color.LightCoral;
    [Category("Appearance"), DefaultValue(typeof(Color), "LightCoral")]
    public Color ButtonBackColor
    {
        get => _buttonBackColor;
        set {
            _buttonBackColor = value;
            Invalidate();
        }
    }

    private float _borderWidth = 2.5f;
    [Category("Appearance"), DefaultValue(2.5f)]
    public float BorderWidth
    {
        get => _borderWidth;
        set {
            _borderWidth = value;
            Invalidate();
        }
    }

    [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override Color BackColor { get => base.BackColor; set => base.BackColor = value; }

    protected override void OnParentChanged(EventArgs e)
    {
        if (Parent is not null) {
            BackColor = Parent.BackColor;
            Parent.BackColorChanged += (s, e) => BackColor = Parent.BackColor;
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        e.Graphics.Clear(BackColor);

        RectangleF rect = ClientRectangle;
        float halfBorder = -0.5f * BorderWidth;
        rect.Inflate(halfBorder, halfBorder);
        rect.Width -= 1;
        rect.Height -= 1;

        using var buttonBrush = new SolidBrush(ButtonBackColor);
        e.Graphics.FillEllipse(buttonBrush, rect);

        using var borderPen = new Pen(BorderColor, BorderWidth);
        e.Graphics.DrawEllipse(borderPen, rect);

        if (Text is { Length: > 0 }) {
            SizeF textSize = e.Graphics.MeasureString(Text, Font);
            float scale = Math.Min(
                (Width - BorderWidth) / textSize.Width,
                (Height - BorderWidth) / textSize.Height);
            e.Graphics.TranslateTransform((Width - textSize.Width * scale) / 2, (Height - textSize.Height * scale) / 2);
            e.Graphics.ScaleTransform(scale, scale);
            using var textBrush = new SolidBrush(ForeColor);
            e.Graphics.DrawString(Text, Font, textBrush, (RectangleF)ClientRectangle);
        }
    }
}

BackColor 在属性窗口中是隐藏的,并且会自动设置以匹配表单或容器控件的颜色。相反,暴露了一个 ButtonBackColor 属性,以及 BorderColorBorderWidth

按钮可以具有任意尺寸,并且不是硬编码的。ForeColor 用作文本颜色。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章