Windows 11下所有控件已经默认采用圆角,其效果更好、相对有着更好的优化,只是这是默认的行为,无法进一步自定义。
圆角按钮实现【重写OnPaint实现圆角绘制】
控件自定义绘制的关键在于:重写OnPaint
方法,其参数提供了用于GDI+绘图的Graphics
对象,由此实现绘制需要的图形效果。
为了更好的显示绘制的图形,通常必须设置控件背景透明(图形外的控件区域透明,便于正确显示绘制的图形),虽然Winform的背景透明有着一定缺陷,但总体来说这是必须的。
此外,Paint
事件方法中,也可以进行控件的绘制(重绘),与继承重写OnPaint
没有本质区别。
代码主要关键点或思路、优化
- 半径Radius、Color、TextAlign属性的赋值,都调用
Invalidate()
方法使控件画面无效并重绘控件。 - 添加文本位置的属性TextAlign,并在属性赋值时调用
Invalidate()
重绘控件,实现修改文本位置的布局。 - 有一个bug问题,就是在点击按钮鼠标抬起方法
OnMouseUp
中,实现了修改鼠标状态,对应的背景颜色值也修改了,控件重绘时也修改了颜色(debug),但绝大多数情况下,鼠标抬起背景颜色未变化。原因在重写的OnMouseUp(MouseEventArgs e)
中先调用的控件基类base.OnMouseUp(e);
,后修改的状态颜色,将base.OnMouseUp(e);
改为最后调用即可。 - 修改和优化圆角部分圆弧的绘制,原实现圆弧半径处理不合理。
- 【尽可能高质量绘制】图形部分的几个模式必须指定,怎么明显看出显示的文本、边框等不清晰、锯齿验证等问题。
- 其他一些小修改和调整,比如抗锯齿、高质量绘图、使用控件字体、文本颜色默认白色、设置字体方向等
- Radius 属性修改边角半径大小(即圆角的大小、圆弧的大小)
- NormalColor、HoverColor、PressedColor 属性设置按钮正常状态、鼠标悬停、鼠标按下时的背景颜色,通常设置为一致即可。
- 指定Size的Width、Height的大小相同,Radius为正方向边长的一半,可以实现圆形按钮。
StringFormat
对象,可以提供对字符串文本的颜色、布局、方向等各种格式的设置,用于渲染文本效果。
Control.DesignMode
属性可以判断当前代码的执行环境是否是设计器模式,在某些条件下可以通过此判断,决定是否在设计器下执行某段代码(如果不涉及样式效果,就可以不需要在设计器下执行)
使用圆角按钮
编译后,直接从工具箱中拖动RoundButtons
圆角按钮控件到窗体即可。
代码如下,关键部分都有相关注释,可以直接过一遍代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace CMControls.RoundButtons
{public enum ControlState { Hover, Normal, Pressed }public class RoundButton : Button{private int radius;//半径 //private Color _borderColor = Color.FromArgb(51, 161, 224);//边框颜色private Color _hoverColor = Color.FromArgb(220, 80, 80);//基颜色private Color _normalColor = Color.FromArgb(51, 161, 224);//基颜色private Color _pressedColor = Color.FromArgb(251, 161, 0);//基颜色private ContentAlignment _textAlign = ContentAlignment.MiddleCenter;public override ContentAlignment TextAlign{set{_textAlign = value;this.Invalidate();}get{return _textAlign;}}/// <summary>/// 圆角按钮的半径属性/// </summary>[CategoryAttribute("Layout"), BrowsableAttribute(true), ReadOnlyAttribute(false)]public int Radius{set{radius = value;// 使控件的整个画面无效并重绘控件this.Invalidate();}get{return radius;}}[CategoryAttribute("Appearance"), DefaultValue(typeof(Color), "51, 161, 224")]public Color NormalColor{get{return this._normalColor;}set{this._normalColor = value;this.Invalidate();}}[CategoryAttribute("Appearance"), DefaultValue(typeof(Color), "220, 80, 80")]public Color HoverColor{get{return this._hoverColor;}set{this._hoverColor = value;this.Invalidate();}}[CategoryAttribute("Appearance"), DefaultValue(typeof(Color), "251, 161, 0")]public Color PressedColor{get{return this._pressedColor;}set{this._pressedColor = value;this.Invalidate();}}public ControlState ControlState { get; set; }protected override void OnMouseEnter(EventArgs e)//鼠标进入时{ControlState = ControlState.Hover;//Hoverbase.OnMouseEnter(e);}protected override void OnMouseLeave(EventArgs e)//鼠标离开{ControlState = ControlState.Normal;//正常base.OnMouseLeave(e);}protected override void OnMouseDown(MouseEventArgs e)//鼠标按下{if (e.Button == MouseButtons.Left && e.Clicks == 1)//鼠标左键且点击次数为1{ControlState = ControlState.Pressed;//按下的状态}base.OnMouseDown(e);}protected override void OnMouseUp(MouseEventArgs e)//鼠标弹起{if (e.Button == MouseButtons.Left && e.Clicks == 1){if (ClientRectangle.Contains(e.Location))//控件区域包含鼠标的位置{ControlState = ControlState.Hover;}else{ControlState = ControlState.Normal;}}base.OnMouseUp(e);}public RoundButton(){ForeColor = Color.White;Radius = 20;this.FlatStyle = FlatStyle.Flat;this.FlatAppearance.BorderSize = 0;this.ControlState = ControlState.Normal;this.SetStyle(ControlStyles.UserPaint | //控件自行绘制,而不使用操作系统的绘制ControlStyles.AllPaintingInWmPaint | //忽略背景擦除的Windows消息,减少闪烁,只有UserPaint设为true时才能使用。ControlStyles.OptimizedDoubleBuffer |//在缓冲区上绘制,不直接绘制到屏幕上,减少闪烁。ControlStyles.ResizeRedraw | //控件大小发生变化时,重绘。 ControlStyles.SupportsTransparentBackColor, //支持透明背景颜色true);}//重写OnPaintprotected override void OnPaint(System.Windows.Forms.PaintEventArgs e){base.OnPaint(e);// base.OnPaintBackground(e);// 尽可能高质量绘制e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;e.Graphics.CompositingQuality = CompositingQuality.HighQuality;e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);var path = GetRoundedRectPath(rect, radius);this.Region = new Region(path);Color baseColor;//Color borderColor;//Color innerBorderColor = this._baseColor;//Color.FromArgb(200, 255, 255, 255); ;switch (ControlState){case ControlState.Hover:baseColor = this._hoverColor;break;case ControlState.Pressed:baseColor = this._pressedColor;break;case ControlState.Normal:baseColor = this._normalColor;break;default:baseColor = this._normalColor;break;}using (SolidBrush b = new SolidBrush(baseColor)){e.Graphics.FillPath(b, path); // 填充路径,而不是DrawPathusing (Brush brush = new SolidBrush(this.ForeColor)){// 文本布局对象using (StringFormat gs = new StringFormat()){// 文字布局switch (_textAlign){case ContentAlignment.TopLeft:gs.Alignment = StringAlignment.Near; gs.LineAlignment = StringAlignment.Near;break;case ContentAlignment.TopCenter:gs.Alignment = StringAlignment.Center;gs.LineAlignment = StringAlignment.Near;break;case ContentAlignment.TopRight:gs.Alignment = StringAlignment.Far;gs.LineAlignment = StringAlignment.Near;break;case ContentAlignment.MiddleLeft:gs.Alignment = StringAlignment.Near;gs.LineAlignment = StringAlignment.Center;break;case ContentAlignment.MiddleCenter:gs.Alignment = StringAlignment.Center; //居中gs.LineAlignment = StringAlignment.Center;//垂直居中break;case ContentAlignment.MiddleRight:gs.Alignment = StringAlignment.Far;gs.LineAlignment = StringAlignment.Center;break;case ContentAlignment.BottomLeft:gs.Alignment = StringAlignment.Near;gs.LineAlignment = StringAlignment.Far;break;case ContentAlignment.BottomCenter:gs.Alignment = StringAlignment.Center;gs.LineAlignment = StringAlignment.Far;break;case ContentAlignment.BottomRight:gs.Alignment = StringAlignment.Far;gs.LineAlignment = StringAlignment.Far;break;default:gs.Alignment = StringAlignment.Center; //居中gs.LineAlignment = StringAlignment.Center;//垂直居中break;}// if (this.RightToLeft== RightToLeft.Yes)// {// gs.FormatFlags = StringFormatFlags.DirectionRightToLeft;// } e.Graphics.DrawString(this.Text, this.Font, brush, rect, gs);} }}}/// <summary>/// 根据矩形区域rect,计算呈现radius圆角的Graphics路径/// </summary>/// <param name="rect"></param>///