Winform控件优化之圆角Panel【绘制时需要注意的几点和扩展】

4,362 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

圆角的实现(原理和绘制方法)之前基本都已经介绍,本篇主要是实现圆角Panel时介绍几点注意点和一些扩展。

新建RoundPanel.cs类文件,继承自Panle

public class RoundPanel : Panel{}

由于之前已经介绍圆角的实现,此处不再重复,只列出主要的点。

添加新窗体CustomPanelTest.cs,用于测试RoundPanel控件的使用和效果。

不要使用原控件BackColor属性的坑(BackColor应始终为Transparent)

在继承控件OnPaint方法或Paint事件中,重绘控件时有一个比较大的坑,那就是不要使用原控件的BackColor作为重绘时的背景色。

因为重绘的控件通常相对原Region长方形区域是不规则的,会有多出来的重绘之外的区域,除非完全用重绘区域内的路径范围创建新Region【但这又会产生锯齿和微不可见的白边】

在不新建Region的前提下,最正确的做法是设置扩展控件的背景色透明、无边框,然后重新绘制新背景和文字

比如,使用下面的代码在OnPaint方法中,你将永远看不到圆角效果(因为绘制的圆角图形和圆角外的其他部分各自颜色没有任何区别,看不出绘制的图形,被一样的背景色覆盖了)。

e.Graphics.DrawRoundRectAndCusp(new System.Drawing.Rectangle(0, 0, Width, Height), 15, BackColor);

DrawRoundRectAndCusp 为之前介绍的扩展方法,15为圆角半径。

基于此,可以在开启圆角绘制的情况下,实现对BackColor设置的限制,只能为Color.Transparent

半角半径属性RoundRadius大于等于0时,背景色透明。

[Category("Appearance"), Description("背景颜色,当RoundRadius>=0时取值为Color.Transparent,RoundRadius<0才可设置其他颜色")]
public override Color BackColor
{
    get => base.BackColor;
    set
    {
        if (RoundRadius >= 0)
        {
            base.BackColor = Color.Transparent;
        }
        else
        {
            base.BackColor = value;
        }
    }
}

绘制时使用圆角时的颜色属性roundNormalColor

e.Graphics.DrawRoundRectAndCusp(new System.Drawing.Rectangle(0, 0, Width, Height), 15, roundNormalColor);

Panel扩展控件实现圆角(Draw完全显示绘制出的线条)

Panel实现圆角和之前Button等介绍实现圆角的原理或过程没有任何区别。相对来说其需要的控件背景更少、也没有文字处理,反而更简单,只绘制个圆角即可。

本次实现边框是通过线条绘制(Graphics.DrawPath绘制路径线条、Graphics.DrawPolygon绘制points点坐标组成的线条)实现,在绘制路径时,默认是以路径为线条的中心,也就是路径内外个一半线条。而之前默认实现的圆角矩形路径正好是控件的范围,这就是导致路径外的一半线条超出控件,不会显示出来,产生一些问题。

if (borderPen != null)
{
    g.DrawPath(borderPen, path);
}

因此需要重新指定路径的范围,使其往内缩小半个BorderSize,从而绘制的线条完全显示(也可以修改线条绘制模式,使其在路径内层绘制)

但由于陷入了上面介绍的使用BackColor绘制背景的误区,以及,关于绘制Border的处理,将绘制路径减少roundBorderSize/2大小实现Draw完全显示线条,因此也单独做个记录。

整体减少roundBorderSize,开始位置移动roundBorderSize / 2

new Rectangle(ClientRectangle.X + roundBorderSize / 2, ClientRectangle.Y + roundBorderSize / 2, ClientRectangle.Width - roundBorderSize, ClientRectangle.Height - roundBorderSize)

Border绘制不完全显示出来的情况下,会导致不规格的圆角效果。

RoundPanel代码如下,关于扩展方法DrawRoundRectAndCusp、实现尖角效果等,可以查看之前的介绍。

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace CMControls
{
    public class RoundPanel : Panel
    {
        private Color roundNormalColor = SystemColors.Control;
        private int roundBorderSize = 0;
        private Color roundBorderColor = Color.MediumSlateBlue;
        private bool underlinedStyle = false;
        private int roundRadius = 20;

        /// <summary>
        /// 圆角按钮的半径属性
        /// </summary>
        [Category("Layout"), DefaultValue(20), Description("圆角半径,>=0时启用圆角按钮,等于0为直角,<0时使用默认控件样式")]
        public int RoundRadius
        {
            get { return roundRadius; }
            set
            {
                    roundRadius = value;
                    this.Invalidate();//Redraw control
            }
        }
        public override Color BackColor
        {
            get => base.BackColor;
            set
            {
                if (RoundRadius >= 0)
                {
                    base.BackColor = Color.Transparent;
                }
                else
                {
                    base.BackColor = value;
                }
            }
        }
        [CategoryAttribute("Appearance"),DefaultValue(typeof(Color), "SystemColors.Control"), Description("启用Radius圆角(RoundRadius>=0)时按钮标准颜色")]
        public Color RoundNormalColor
        {
            get { return roundNormalColor; }
            set
            {
                roundNormalColor = value;
                this.Invalidate();
            }
        }
        [CategoryAttribute("Appearance"), DefaultValue(0), Description("启用Radius圆角(RoundRadius>=0)时边框宽度,默认0")]
        public int RoundBorderWidth
        {
            get { return roundBorderSize; }
            set
            {
                roundBorderSize = value;
                this.Invalidate();

            }
        }
        [CategoryAttribute("Appearance"), DefaultValue(typeof(Color), "Color.MediumSlateBlue"), Description("启用Radius圆角(RoundRadius>=0)时边框颜色")]
        public Color RoundBorderColor
        {
            get => roundBorderColor;
            set
            {
                roundBorderColor = value;
                this.Invalidate();
            }
        }
        [CategoryAttribute("Appearance"), DefaultValue(false), Description("圆角边框宽度>0时,是否启用Underline,默认false")]
        public bool UnderlinedStyle
        {
            get { return underlinedStyle; }
            set
            {
                underlinedStyle = value;
                this.Invalidate();
            }
        }
        public RoundPanel()
        {
            BackColor = Color.Transparent;
        }
        //重写OnPaint
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (RoundRadius >= 0)
            {
                if (roundBorderSize > 0)
                {
                    using (var borderPen = new Pen(roundBorderColor, roundBorderSize))
                    {
                        if (underlinedStyle) //Line Style
                        {
                            e.Graphics.DrawLine(borderPen, 0, this.Height - roundBorderSize/2, this.Width, this.Height - roundBorderSize/2);
                        }
                        else
                        {
                            //borderPen.Alignment = PenAlignment.Inset;
                            e.Graphics.DrawRoundRectAndCusp(new Rectangle(ClientRectangle.X+ roundBorderSize / 2, ClientRectangle.Y+ roundBorderSize / 2, ClientRectangle.Width- roundBorderSize, ClientRectangle.Height - roundBorderSize), RoundRadius, roundNormalColor, borderPen: borderPen);
                        }
                    }
                }
                else
                {
                    e.Graphics.DrawRoundRectAndCusp(ClientRectangle, RoundRadius, roundNormalColor);
                }
            }
        }
    }
}

拖拽RoundPanel控件到窗体中,调整不同样式查看效果:

圆角模式,指定四个圆角的位置及组合

后面增加RoundMode属性,可以指定四个圆角的位置及其组合。

如下圆角Panel中的小圆角RoundPanel分别变换不同的圆角位置: