You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForm创建红蓝色分半背景居中OK按钮:代码修复与替代方案

WinForm双色按钮绘制问题修复及替代方案

一、修复定时器绘制代码(解决内容被按钮覆盖的问题)

原代码存在三个核心问题:

  1. 使用窗体的CreateGraphics()绘图,内容会被按钮控件覆盖(控件层级高于窗体)
  2. 坐标计算错误,误用了按钮相对于窗体的位置,且存在无意义的偏移值
  3. 调用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);
    }
}

使用方式:

  1. 编译项目后,该控件会自动出现在Visual Studio工具箱中
  2. 直接拖到窗体上即可使用,无需额外代码

如果需要动画效果,可扩展为带动画的自定义控件:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 11:45:27