C# WinForm 如何合并四色自定义Label与圆角Label类
问题根因与实现思路
- 原
RoundedLabel类本身定义残缺:没有继承System.Windows.Forms.Label,代码中用到的OnPaint重写、DoubleBuffered、ClientRectangle等成员,都需要从Label基类继承才能正常使用,原代码本身无法独立运行。 - 绘制逻辑没有统一裁剪边界:四色填充是直接向整个控件矩形区域绘制,而原圆角效果仅作用于单独的纯色路径,两者没有共享同一个圆角裁剪范围,导致圆角无法约束四色分段的填充内容。
正确的实现逻辑不需要对每段颜色单独做圆角计算:先基于控件大小生成完整的圆角路径,将其设置为Graphics的全局裁剪区域,之后所有的四色分段填充操作都在这个裁剪范围内执行——所有超出圆角边界的填充内容会被自动裁切,从视觉上实现“四色填充的背景整体带四个圆角”的效果。
完整可运行的合并类实现
using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; public class RoundedFourColorLabel : Label { // 四色相关字段,从原CustomLabel迁移 private Color m_color1 = Color.LightGreen; private Color m_color2 = Color.DarkBlue; private Color m_color3 = Color.Gray; private Color m_color4 = Color.LightYellow; private int m_percentage1 = 30; private int m_percentage2 = 20; private int m_percentage3 = 50; private int m_percentage4 = 0; // 圆角相关配置 private int m_cornerRadius = 15; public RoundedFourColorLabel() { // 开启双缓冲,避免自绘闪烁 DoubleBuffered = true; // 配置透明背景支持,避免基类绘制流程覆盖自定义效果 SetStyle(ControlStyles.SupportsTransparentBackColor, true); BackColor = Color.Transparent; } #region 四色填充相关属性(完全保留原CustomLabel的使用逻辑) [Browsable(true)] [Category("自定义外观")] public Color Color1 { get => m_color1; set { m_color1 = value; Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public Color Color2 { get => m_color2; set { m_color2 = value; Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public Color Color3 { get => m_color3; set { m_color3 = value; Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public Color Color4 { get => m_color4; set { m_color4 = value; Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public int Percentage1 { get => m_percentage1; set { m_percentage1 = Math.Max(0, value); Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public int Percentage2 { get => m_percentage2; set { m_percentage2 = Math.Max(0, value); Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public int Percentage3 { get => m_percentage3; set { m_percentage3 = Math.Max(0, value); Invalidate(); } } [Browsable(true)] [Category("自定义外观")] public int Percentage4 { get => m_percentage4; set { m_percentage4 = Math.Max(0, value); Invalidate(); } } #endregion #region 圆角相关属性 [Browsable(true)] [Category("自定义外观")] public int CornerRadius { get => m_cornerRadius; set { m_cornerRadius = Math.Max(0, Math.Min(value, Height/2)); Invalidate(); } } #endregion // 生成圆角路径的逻辑,从原RoundedLabel迁移 private GraphicsPath GetRoundRectanglePath(Rectangle rectangle) { int cornerRadius = m_cornerRadius; int diminisher = 1; GraphicsPath path = new GraphicsPath(); if (cornerRadius <= 0) { path.AddRectangle(rectangle); path.CloseAllFigures(); return path; } path.AddArc(rectangle.X, rectangle.Y, cornerRadius, cornerRadius, 180, 90); path.AddArc(rectangle.Right - cornerRadius - diminisher, rectangle.Y, cornerRadius, cornerRadius, 270, 90); path.AddArc(rectangle.Right - cornerRadius - diminisher, rectangle.Bottom - cornerRadius - diminisher, cornerRadius, cornerRadius, 0, 90); path.AddArc(rectangle.X, rectangle.Bottom - cornerRadius - diminisher, cornerRadius, cornerRadius, 90, 90); path.CloseAllFigures(); return path; } protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.SmoothingMode = SmoothingMode.AntiAlias; Rectangle clientRect = this.ClientRectangle; using (GraphicsPath roundPath = GetRoundRectanglePath(clientRect)) { // 应用裁剪:之后所有绘制操作都不会超出圆角边界 g.SetClip(roundPath); // 计算每一段的宽度,自动对齐控件右边缘,避免百分比加总不等于100%时出现白边 int totalWidth = clientRect.Width; int seg1Width = (int)(totalWidth * Percentage1 / 100.0); int seg2Width = (int)(totalWidth * Percentage2 / 100.0); int seg3Width = (int)(totalWidth * Percentage3 / 100.0); int seg4Width = totalWidth - seg1Width - seg2Width - seg3Width; // 绘制四色分段填充,通过using自动释放绘图资源 using (Brush brush1 = new SolidBrush(Color1)) { g.FillRectangle(brush1, 0, 0, seg1Width, clientRect.Height); } using (Brush brush2 = new SolidBrush(Color2)) { g.FillRectangle(brush2, seg1Width, 0, seg2Width, clientRect.Height); } using (Brush brush3 = new SolidBrush(Color3)) { g.FillRectangle(brush3, seg1Width + seg2Width, 0, seg3Width, clientRect.Height); } using (Brush brush4 = new SolidBrush(Color4)) { g.FillRectangle(brush4, seg1Width + seg2Width + seg3Width, 0, seg4Width, clientRect.Height); } // 移除裁剪区域,消除圆角边缘的锯齿偏差 g.ResetClip(); using (Pen borderPen = new Pen(ForeColor, 1.0f)) { g.DrawPath(borderPen, roundPath); } } // 最后调用基类逻辑绘制文本,保证文本样式和原生Label完全一致 base.OnPaint(e); } }
关键实现细节
- 采用单一基类继承结构:直接从
System.Windows.Forms.Label派生,避免分层继承导致的OnPaint调用顺序混乱(原分层写法中,四色填充会在圆角绘制之后执行,直接覆盖之前的圆角效果)。 - 全局裁剪方案:不需要为每段颜色单独计算圆角路径,仅通过Graphics的裁剪机制约束所有填充内容的边界,最终视觉效果为整体圆角的四色背景。
- 资源泄漏修复:所有GDI+绘图对象(Brush、GraphicsPath)均通过
using块自动释放,避免长期运行时出现资源占用问题。 - 边界对齐优化:第四段填充宽度根据前面三段的实际占用宽度自动计算,无需用户手动保证四个百分比值累加为100%,不会出现边缘白边。
- 可配置圆角大小:将圆角半径提取为公开属性,可在设计器中直接调整,无需修改代码重新编译。
内容的提问来源于stack exchange,提问作者Yohan Trens
相关产品推荐
相关产品推荐

