WinForm 中实现高质量自定义圆角按钮控件

156 阅读4分钟

前言

WinForm开发中,默认的Button控件外观比较单调,且圆角效果显示质量较差。为了提升界面美观度和用户体验,本文将介绍如何使用GDI+创建一个高质量的自定义圆角按钮控件。该控件参考了YouTube上一位大神的设计思路,采用双层绘制机制,实现了平滑的圆角渲染和多种自定义功能。

正文

主要特性

  • 可自定义边框大小

  • 可自定义圆角半径

  • 可自定义边框颜色

  • 支持背景色和文本颜色设置

  • 平滑的圆角渲染效果

这些特性使得控件可以灵活适应不同UI设计需求,同时保证视觉上的高品质表现。

实现步骤

创建自定义按钮类

首先,我们需要创建一个继承自Button的自定义类:

using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

public class RoundButton : Button
{
    // 字段定义
    private int borderSize = 0;
    private int borderRadius = 20;
    private Color borderColor = Color.PaleVioletRed;
}

添加属性

接下来为按钮添加可配置的属性,包括边框大小、圆角半径、边框颜色、背景色和文本颜色,并在每次修改时调用Invalidate()方法以触发重绘。

[Category("Round Button")]
public int BorderSize
{
    get => borderSize;
    set
    {
        borderSize = value;
        Invalidate();
    }
}

[Category("Round Button")]
public int BorderRadius
{
    get => borderRadius;
    set
    {
        borderRadius = value;
        Invalidate();
    }
}

[Category("Round Button")]
public Color BorderColor
{
    get => borderColor;
    set
    {
        borderColor = value;
        Invalidate();
    }
}

[Category("Round Button")]
public Color BackgroundColor
{
    get => BackColor;
    set => BackColor = value;
}

[Category("Round Button")]
public Color TextColor
{
    get => ForeColor;
    set => ForeColor = value;
}

构造函数实现

在构造函数中设置按钮的默认样式、尺寸和颜色,并绑定Resize事件以确保圆角不超过按钮高度。

public RoundButton()
{
    FlatStyle = FlatStyle.Flat;
    FlatAppearance.BorderSize = 0;
    Size = new Size(150, 40);
    BackColor = Color.MediumSlateBlue;
    ForeColor = Color.White;
    Resize += new EventHandler(Button_Resize);
}

private void Button_Resize(object sender, EventArgs e)
{
    if (borderRadius > Height)
        borderRadius = Height;
}

圆角路径生成

通过GraphicsPath对象实现圆角图形路径的生成,支持左上、右上、右下、左下四个方向的弧线绘制。

private GraphicsPath GetFigurePath(Rectangle rect, float radius)
{
    GraphicsPath path = new GraphicsPath();
    float curveSize = radius * 2F;
    path.StartFigure();
    // 左上角
    path.AddArc(rect.X, rect.Y, curveSize, curveSize, 180, 90);
    // 右上角
    path.AddArc(rect.Right - curveSize, rect.Y, curveSize, curveSize, 270, 90);
    // 右下角
    path.AddArc(rect.Right - curveSize, rect.Bottom - curveSize, curveSize, curveSize, 0, 90);
    // 左下角
    path.AddArc(rect.X, rect.Bottom - curveSize, curveSize, curveSize, 90, 90);
    path.CloseFigure();
    return path;
}

重写OnPaint方法

核心绘制逻辑位于OnPaint方法中,采用双层绘制机制:外层路径用于按钮整体形状与边缘抗锯齿处理,内层路径用于边框绘制。

protected override void OnPaint(PaintEventArgs pevent)
{
    base.OnPaint(pevent);
    Rectangle rectSurface = ClientRectangle;
    Rectangle rectBorder = Rectangle.Inflate(rectSurface, -borderSize, -borderSize);
    int smoothSize = 2;
    if (borderSize > 0)
        smoothSize = borderSize;
    if (borderRadius > 2) // 圆角按钮
    {
        using (GraphicsPath pathSurface = GetFigurePath(rectSurface, borderRadius))
        using (GraphicsPath pathBorder = GetFigurePath(rectBorder, borderRadius - borderSize))
        using (Pen penSurface = new Pen(Parent.BackColor, smoothSize))
        using (Pen penBorder = new Pen(borderColor, borderSize))
        {
            pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            Region = new Region(pathSurface);
            pevent.Graphics.DrawPath(penSurface, pathSurface);
            if (borderSize >= 1)
                pevent.Graphics.DrawPath(penBorder, pathBorder);
        }
    }
    else // 普通按钮
    {
        pevent.Graphics.SmoothingMode = SmoothingMode.None;
        Region = new Region(rectSurface);
        if (borderSize >= 1)
        {
            using (Pen penBorder = new Pen(borderColor, borderSize))
            {
                penBorder.Alignment = PenAlignment.Inset;
                pevent.Graphics.DrawRectangle(penBorder, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}

处理父容器颜色变化

为确保按钮在父容器背景颜色变化时能正确刷新,需监听BackColorChanged事件并调用Invalidate()

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    Parent.BackColorChanged += Container_BackColorChanged;
}

private void Container_BackColorChanged(object sender, EventArgs e)
{
    Invalidate();
}

双层绘制机制详解

外层路径(pathSurface):定义按钮的整体形状,并用于绘制边缘抗锯齿线条。

内层路径(pathBorder):根据边框大小计算出内部矩形区域,并用于绘制实际的边框。

代码示例:

protected override void OnPaint(PaintEventArgs pevent)
{
    base.OnPaint(pevent);
    Rectangle rectSurface = ClientRectangle;
    Rectangle rectBorder = Rectangle.Inflate(rectSurface, -borderSize, -borderSize);
    int smoothSize = 2;
    if (borderSize > 0) smoothSize = borderSize;

    if (borderRadius > 2)
    {
        using (GraphicsPath pathSurface = GetFigurePath(rectSurface, borderRadius))
        using (GraphicsPath pathBorder = GetFigurePath(rectBorder, borderRadius - borderSize))
        using (Pen penSurface = new Pen(Parent.BackColor, smoothSize))
        using (Pen penBorder = new Pen(borderColor, borderSize))
        {
            pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            Region = new Region(pathSurface);
            pevent.Graphics.DrawPath(penSurface, pathSurface);
            if (borderSize >= 1)
                pevent.Graphics.DrawPath(penBorder, pathBorder);
        }
    }
}

总结

通过继承Button类并结合GDI+绘图技术,我们成功实现了一个功能丰富、视觉效果出色的自定义圆角按钮控件

该控件不仅具备良好的扩展性,还采用了双层绘制机制,确保了圆角的平滑性和边框的准确性。

可以根据项目需求自由调整边框大小、圆角半径、颜色等参数,从而满足多样化的UI设计需求。

关键词

WinForms、GDI+、自定义控件、圆角按钮、双层绘制、抗锯齿、GraphicsPath、Pen、SmoothingMode、Region

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!