WinForm创建红蓝色分半背景居中OK按钮:代码修复与替代方案
WinForm双色按钮绘制问题修复及替代方案
一、修复定时器绘制代码(解决内容被按钮覆盖的问题)
原代码存在三个核心问题:
- 使用窗体的
CreateGraphics()绘图,内容会被按钮控件覆盖(控件层级高于窗体) - 坐标计算错误,误用了按钮相对于窗体的位置,且存在无意义的偏移值
- 调用
this.Refresh()会刷新整个窗体,导致绘制内容被清除
修复后的完整代码:
public partial class Form1 : Form { int redWidth = 0; int blueWidth = 0; int halfBtnWidth; public Form1() { InitializeComponent(); halfBtnWidth = button1.Width / 2; } private void button1_Click(object sender, EventArgs e) { // 重置进度,避免重复点击后动画混乱 redWidth = 0; blueWidth = 0; timer1.Enabled = true; } private void timer1_Tick(object sender, EventArgs e) { // 获取按钮自身的绘图上下文,确保内容显示在按钮之上 using (Graphics g = button1.CreateGraphics()) { // 清除按钮原有内容,避免绘制重叠 g.Clear(button1.BackColor); // 逐步绘制红色左半区 if (redWidth < halfBtnWidth) { g.FillRectangle(Brushes.Red, 0, 0, redWidth, button1.Height); redWidth++; } // 红色绘制完成后,逐步绘制蓝色右半区 else if (blueWidth < halfBtnWidth) { // 先填满红色区域 g.FillRectangle(Brushes.Red, 0, 0, halfBtnWidth, button1.Height); // 从半宽位置开始绘制蓝色 g.FillRectangle(Brushes.Blue, halfBtnWidth, 0, blueWidth, button1.Height); blueWidth++; } // 动画完成后停止定时器,确保最终显示完整双色背景 else { timer1.Enabled = false; g.FillRectangle(Brushes.Red, 0, 0, halfBtnWidth, button1.Height); g.FillRectangle(Brushes.Blue, halfBtnWidth, 0, halfBtnWidth, button1.Height); } // 绘制居中的"ok"文本 StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; g.DrawString("ok", button1.Font, Brushes.Black, button1.ClientRectangle, sf); } } }
关键修复点:
- 改用
button1.CreateGraphics()获取按钮的绘图上下文,确保绘制层级在按钮之上 - 使用按钮内部的客户端坐标(0,0对应按钮左上角),修正坐标偏移问题
- 移除
this.Refresh(),改为在按钮内部清除并重绘,避免窗体刷新清除内容 - 增加动画完成后的停止逻辑,以及文本居中绘制逻辑
二、实现双色按钮的其他推荐方法
方法1:自定义可复用双色按钮控件(最稳定)
这种方式符合WinForm的控件绘制机制,无闪烁、可复用,支持动画扩展:
public class DoubleColorButton : Button { protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; int halfWidth = Width / 2; // 绘制左红右蓝背景 g.FillRectangle(Brushes.Red, 0, 0, halfWidth, Height); g.FillRectangle(Brushes.Blue, halfWidth, 0, Width - halfWidth, Height); // 绘制居中文本 StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; g.DrawString("ok", Font, Brushes.Black, ClientRectangle, sf); // 保留默认按钮边框(可选) ControlPaint.DrawBorder(g, ClientRectangle, ForeColor, ButtonBorderStyle.Solid); } }
使用方式:
- 编译项目后,该控件会自动出现在Visual Studio工具箱中
- 直接拖到窗体上即可使用,无需额外代码
如果需要动画效果,可扩展为带动画的自定义控件:
public class AnimatedDoubleColorButton : Button { private int _redWidth = 0; private int _blueWidth = 0; private int _halfWidth; private readonly Timer _animationTimer; public AnimatedDoubleColorButton() { _animationTimer = new Timer { Interval = 10 }; _animationTimer.Tick += AnimationTimer_Tick; } private void AnimationTimer_Tick(object sender, EventArgs e) { _halfWidth = Width / 2; if (_redWidth < _halfWidth) { _redWidth++; Invalidate(); // 触发控件重绘 } else if (_blueWidth < _halfWidth) { _blueWidth++; Invalidate(); } else { _animationTimer.Stop(); } } // 外部调用启动动画 public void StartAnimation() { _redWidth = 0; _blueWidth = 0; _animationTimer.Start(); } protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(BackColor); // 绘制当前进度的双色背景 g.FillRectangle(Brushes.Red, 0, 0, _redWidth, Height); g.FillRectangle(Brushes.Blue, _halfWidth, 0, _blueWidth, Height); // 绘制居中文本 StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; g.DrawString("ok", Font, Brushes.Black, ClientRectangle, sf); ControlPaint.DrawBorder(g, ClientRectangle, ForeColor, ButtonBorderStyle.Solid); } }
方法2:重写现有按钮的Paint事件
如果不想自定义控件,可直接在窗体中给目标按钮注册Paint事件:
public partial class Form1 : Form { public Form1() { InitializeComponent(); // 注册按钮的Paint事件 button1.Paint += Button1_Paint; } private void Button1_Paint(object sender, PaintEventArgs e) { Button btn = sender as Button; if (btn == null) return; Graphics g = e.Graphics; int halfWidth = btn.Width / 2; // 绘制双色背景 g.FillRectangle(Brushes.Red, 0, 0, halfWidth, btn.Height); g.FillRectangle(Brushes.Blue, halfWidth, 0, btn.Width - halfWidth, btn.Height); // 绘制居中文本 StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; g.DrawString("ok", btn.Font, Brushes.Black, btn.ClientRectangle, sf); // 绘制边框(可选) ControlPaint.DrawBorder(g, btn.ClientRectangle, btn.ForeColor, ButtonBorderStyle.Solid); } }
如需动画效果,可添加定时器修改进度变量,调用button1.Invalidate()触发重绘即可。
内容的提问来源于stack exchange,提问作者Elie Makdissi
相关产品推荐
相关产品推荐

