当前位置:首页 > 人生 > 作品墙 > 正文内容

SwitchButton 切换开关按钮调用文档

作品墙3小时前


命名空间:CConUI
继承:System.Windows.Forms.Control
可见性:public class SwitchButton
默认尺寸:60 × 30
默认事件:CheckedChanged(设计器双击控件即生成该事件处理器)
定位:仿移动端 Switch 开关按钮——自绘跑道 + 圆滑块,单击切换开/关,支持悬停高亮与全套颜色自定义;随控件尺寸自动缩放,无需手动设置滑块大小。


一、作用

一个交互简洁、自绘实现的开关控件,用于表达“开 / 关”、“启用 / 禁用”类二元状态:

  • 跑道背景:开启时显示 OnBackColor,关闭时显示 OffBackColor;

  • 圆滑块:开启时靠右,关闭时靠左;

  • 悬停高亮:鼠标移入时滑块变为 HoverThumbColor;

  • 单击切换:左键单击即切换 Checked 状态,并依次触发 CheckedChanging → CheckedChanged;

  • 尺寸自适应:跑道、滑块直径、滑块位置都按控件宽高按比例计算,改 Size 即可整体缩放。

视觉示意

Checked跑道颜色滑块位置滑块颜色
false(关闭)OffBackColor(默认深灰)左侧OffThumbColor(默认浅灰)
true(开启)OnBackColor(默认蓝)右侧OnThumbColor(默认白)
鼠标悬停(任意状态)不变不变HoverThumbColor(默认浅白)

二、快速上手

using System.Drawing;
using System.Windows.Forms;
using CConUI;

// 1. 创建控件
var sw = new SwitchButton();

// 2. 放入容器
sw.Parent = panel1;
sw.Location = new Point(20, 20);
// 尺寸默认 60×30;可按需放大,例如:
sw.Size = new Size(80, 40);

// 3. 设置初始状态
sw.Checked = true;

// 4. 订阅状态变化事件
sw.CheckedChanged += (s, e) =>
{
    var ctrl = (SwitchButton)s;
    Console.WriteLine("当前状态:" + ctrl.Checked);
};

sw.CheckedChanging += (s, e) =>
{
    // 状态即将改变(此时 Checked 还是旧值)
    var ctrl = (SwitchButton)s;
    Console.WriteLine("即将从 " + ctrl.Checked + " 切换");
};

// 5. 自定义外观(可选)
sw.OnBackColor    = Color.FromArgb(0, 180, 120);
sw.OffBackColor   = Color.FromArgb(70, 70, 70);
sw.OnThumbColor   = Color.White;
sw.OffThumbColor  = Color.FromArgb(200, 200, 200);
sw.HoverThumbColor = Color.LightYellow;
sw.ThumbMargin    = 4;

三、构造函数

public SwitchButton()

项目说明
参数无
作用初始化开关控件
实际行为① 启用 UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer | ResizeRedraw 样式(自绘 + 双缓冲 + 尺寸变化自动重绘);
② 设置 Size = (60, 30);
③ 设置 Font = "微软雅黑", 10pt;
④ 设置 Cursor = Cursors.Hand(悬停显示手型光标)
默认尺寸60 × 30(宽高比 2:1,视觉为标准开关比例)
默认状态Checked = false、ThumbMargin = 4
var sw = new SwitchButton();
sw.Parent = panel1;

四、公开属性

4.1 public bool Checked

项目说明
类型bool
默认false
CategoryCCon
DefaultValuefalse
作用控件的选中状态:
true → 开启(滑块靠右,跑道用 OnBackColor);
false → 关闭(滑块靠左,跑道用 OffBackColor)
副作用赋值时若值有变化:
① 先触发 CheckedChanging(此时 Checked 还是旧值);
② 再写入新值;
③ 触发 CheckedChanged(此时 Checked 已经是新值);
④ Invalidate() 重绘
幂等赋相同值时直接返回,不触发任何事件,也不重绘
sw.Checked = true;    // 开启,触发 CheckedChanging + CheckedChanged
sw.Checked = true;    // 已是 true,无事发生
sw.Checked = false;   // 关闭,触发事件

4.2 颜色属性

public Color OnBackColor

项目说明
类型Color
默认Color.FromArgb(88, 166, 255)(亮蓝)
CategoryCCon
作用控件开启时跑道的填充色
副作用赋值后 Invalidate() 重绘

public Color OffBackColor

项目说明
类型Color
默认Color.FromArgb(60, 60, 60)(深灰)
CategoryCCon
作用控件关闭时跑道的填充色
副作用赋值后 Invalidate() 重绘

public Color OnThumbColor

项目说明
类型Color
默认Color.White
CategoryCCon
作用控件开启状态下、鼠标未悬停时滑块的颜色
副作用赋值后 Invalidate() 重绘

public Color OffThumbColor

项目说明
类型Color
默认Color.FromArgb(180, 180, 180)(浅灰)
CategoryCCon
作用控件关闭状态下、鼠标未悬停时滑块的颜色
副作用赋值后 Invalidate() 重绘

public Color HoverThumbColor

项目说明
类型Color
默认Color.FromArgb(220, 220, 220)
CategoryCCon
作用鼠标悬停在控件上时滑块的高亮颜色。
注意:悬停时滑块颜色统一用此色,不再区分 On / Off
副作用赋值后 Invalidate() 重绘
sw.OnBackColor    = Color.FromArgb(0, 180, 120);   // 开启 → 绿色跑道
sw.OffBackColor   = Color.FromArgb(70, 70, 70);    // 关闭 → 深灰跑道
sw.OnThumbColor   = Color.White;                    // 开启滑块白
sw.OffThumbColor  = Color.FromArgb(200, 200, 200);  // 关闭滑块浅灰
sw.HoverThumbColor = Color.LightYellow;             // 悬停滑块浅黄

4.3 public int ThumbMargin

项目说明
类型int(像素)
默认4
CategoryCCon
DefaultValue4
作用滑块与跑道边缘之间的内边距(上下左右对称)。数值越大,滑块相对跑道越小,视觉上跑道越“细”
取值范围Math.Max(0, value)——自动 clamp 到 ≥0
约束应小于 Height / 2,否则滑块直径为 ≤0,绘制时会直接 return(看不到滑块)
副作用赋值后 Invalidate() 重绘
联动滑块直径 = Height - ThumbMargin × 2。改 ThumbMargin 会同时改滑块大小
sw.ThumbMargin = 4;    // 默认
sw.ThumbMargin = 2;    // 滑块更大(跑道更"满")
sw.ThumbMargin = 6;    // 滑块更小(跑道更"粗")

五、公开事件

public event EventHandler CheckedChanged

项目说明
参数EventHandler:sender 为当前 SwitchButton,e 为 EventArgs.Empty
CategoryCCon
默认事件是([DefaultEvent("CheckedChanged")])——设计器双击即生成此事件处理器
触发时机Checked 值发生改变后,且在 Invalidate() 之前触发
不触发情况赋相同值不触发
触发顺序CheckedChanging → 更新 Checked 值 → CheckedChanged
sw.CheckedChanged += (s, e) =>
{
    var ctrl = (SwitchButton)s;
    // 此时 ctrl.Checked 已经是新值
    lblStatus.Text = ctrl.Checked ? "已开启" : "已关闭";
};

public event EventHandler CheckedChanging

项目说明
参数EventHandler
CategoryCCon
触发时机Checked 值即将改变前触发——此时 Checked 仍为旧值
不触发情况赋相同值不触发
用途可在此事件中记录旧状态;由于 EventArgs 为空,若需“取消”切换,需在 CheckedChanged 里再还原(本类未提供“取消”机制,只能在 changed 后自行回滚)
bool _lastChecked = false;

sw.CheckedChanging += (s, e) =>
{
    var ctrl = (SwitchButton)s;
    _lastChecked = ctrl.Checked;   // 记录旧值(此时 Checked 还是旧状态)
    Console.WriteLine("旧状态:" + _lastChecked);
};

sw.CheckedChanged += (s, e) =>
{
    var ctrl = (SwitchButton)s;
    Console.WriteLine("新状态:" + ctrl.Checked);
    // 如果需要“取消切换”,可以:
    // if (someCondition) ctrl.Checked = _lastChecked;
};

六、公开方法

SwitchButton 没有新增 public 方法。所有公开方法继承自 Control(如 Invalidate()、Focus()、BringToFront() 等)。

内部重写方法(protected override,外部不可直接调用)

方法可见性作用
OnPaint(PaintEventArgs e)protected override整体绘制:跑道背景 → 滑块(含悬停高亮)
OnMouseEnter(EventArgs e)protected override标记 _isHover = true 并重绘
OnMouseLeave(EventArgs e)protected override标记 _isHover = false 并重绘
OnMouseDown(MouseEventArgs e)protected override左键按下时切换 Checked = !Checked

七、公开成员速查表

成员来源类型说明
SwitchButton()本类构造函数默认尺寸 60×30,手型光标
Checked本类属性(bool)选中状态(默认 false);赋值触发事件 + 重绘
OnBackColor本类属性(Color)开启时跑道颜色
OffBackColor本类属性(Color)关闭时跑道颜色
OnThumbColor本类属性(Color)开启时滑块颜色(非悬停)
OffThumbColor本类属性(Color)关闭时滑块颜色(非悬停)
HoverThumbColor本类属性(Color)悬停时滑块颜色
ThumbMargin本类属性(int)滑块与跑道边距(默认 4,≥0)
CheckedChanged本类事件(默认事件)状态改变后触发
CheckedChanging本类事件状态改变前触发(Checked 仍是旧值)
Text / Font / ForeColor / BackColorControl属性继承但对本类绘制无影响(本类不使用这些来渲染)
Size / ClientSizeControl属性(Size)设置控件尺寸;跑道与滑块按比例自适应
CursorControl属性(Cursor)默认为 Hand;改为 Default 可去掉手型提示
Invalidate()Control方法触发重绘
OnPaint(e)本类(override)方法(protected)绘制逻辑,外部不可直接调用
OnMouseEnter / OnMouseLeave / OnMouseDown本类(override)方法(protected)悬停 / 点击交互,外部不可直接调用

八、绘制原理(深入理解)

8.1 基础矩形与半径

float w = this.Width;
float h = this.Height;

RectangleF bgRect = new RectangleF(0.5f, 0.5f, w - 1f, h - 1f);
float radius = bgRect.Height / 2f;
要点说明
0.5f, 0.5f, w-1, h-1对称内缩 0.5 像素:
· 防止抗锯齿边缘被控件边缘物理裁剪;
· 保证跑道精确居中;
· 与滑块也用 0.5 偏移,三者像素级对齐
radius = Height / 2跑道左右两端是半圆,半径 = 控件高的一半,这样跑道就是一个完美的“胶囊形”

8.2 绘制跑道(背景)

using (GraphicsPath path = new GraphicsPath())
{
    path.AddArc(bgRect.X, bgRect.Y, radius * 2f, radius * 2f, 90, 180);
    path.AddArc(bgRect.Right - radius * 2f, bgRect.Y, radius * 2f, radius * 2f, -90, 180);
    path.CloseFigure();

    Color backColor = this._checked ? this._onBackColor : this._offBackColor;
    using (Brush brush = new SolidBrush(backColor))
        g.FillPath(brush, path);
}
要点说明
左弧 (x, y, 2r, 2r, 90, 180)从 90°(正下方)开始顺时针扫 180° 到 -90°(正上方)。
原因:GDI+ 0° 在 3 点方向、顺时针为正;从 90° 扫到 270° 正好是左半圆(从底到顶)。
右弧 (Right - 2r, y, 2r, 2r, -90, 180)从 -90°(正上方)开始顺时针扫 180° 到 90°(正下方)。
正好是右半圆(从顶到底)。
两弧拼接左弧终点在顶部,右弧起点也在顶部;左弧起点在底部,右弧终点也在底部;CloseFigure 自动用直线连接。
结果就是左右两个半圆 + 上下两条直线组成的胶囊形
颜色Checked ? OnBackColor : OffBackColor

8.3 计算滑块

float thumbDiameter = h - (this._thumbMargin * 2f);
if (thumbDiameter <= 0) return;

float thumbX = this._checked
    ? (w - thumbDiameter - this._thumbMargin - 0.5f)
    : this._thumbMargin + 0.5f;

float thumbY = this._thumbMargin + 0.5f;

RectangleF thumbRect = new RectangleF(thumbX, thumbY, thumbDiameter, thumbDiameter);
要点说明
thumbDiameter = h - ThumbMargin × 2滑块直径 = 控件高 − 上下两个边距。
为什么减 2×:上下各留一个 ThumbMargin
thumbDiameter ≤ 0如果边距大到滑块直径变成 0 或负数,直接 return,不绘制滑块(只剩跑道)
开启状态 X:w - diameter - margin - 0.5右对齐。
· w - diameter 是让滑块右边缘贴到控件右边缘;
· 再 - margin 留出右边距;
· 再 - 0.5 与跑道 0.5 偏移对齐
关闭状态 X:margin + 0.5左对齐。
· margin 是左边距;
· 再 + 0.5 与跑道 0.5 偏移对齐
thumbY = margin + 0.5垂直居中,与上下边距一致

8.4 决定滑块颜色

Color thumbColor;
if (this._isHover)
    thumbColor = this._hoverThumbColor;
else
    thumbColor = this._checked ? this._onThumbColor : this._offThumbColor;
状态滑块颜色
鼠标悬停(任意 Checked)HoverThumbColor(悬停色统一)
未悬停 + Checked=trueOnThumbColor
未悬停 + Checked=falseOffThumbColor

8.5 绘制滑块

using (Brush brush = new SolidBrush(thumbColor))
    g.FillEllipse(brush, thumbRect);

简单用 FillEllipse 画圆。因 thumbRect 宽高相等(直径),画出来就是正圆。


九、交互一览

操作效果
鼠标进入控件_isHover = true,滑块变为 HoverThumbColor
鼠标离开控件_isHover = false,滑块恢复 OnThumbColor / OffThumbColor
左键按下Checked = !Checked,走 setter:触发 CheckedChanging → 更新 → CheckedChanged → 重绘
右键 / 中键按下不响应(OnMouseDown 里只判断 MouseButtons.Left)
鼠标悬停光标默认 Hand(手型),提示可点击
键盘本类未重写键盘处理:Space / Enter 不会切换状态;如需键盘支持,请自行外部处理或重写

十、完整使用示例

示例 1:基础开关

using System.Drawing;
using System.Windows.Forms;
using CConUI;

var sw = new SwitchButton
{
    Parent = panel1,
    Location = new Point(20, 20),
    Checked = true
};

sw.CheckedChanged += (s, e) =>
{
    var ctrl = (SwitchButton)s;
    MessageBox.Show(ctrl.Checked ? "开启了" : "关闭了");
};

示例 2:自定义颜色

var sw = new SwitchButton
{
    Parent = this,
    Location = new Point(20, 20),
    OnBackColor    = Color.FromArgb(0, 180, 120),   // 绿跑道
    OffBackColor   = Color.FromArgb(80, 80, 80),    // 深灰跑道
    OnThumbColor   = Color.White,
    OffThumbColor  = Color.FromArgb(200, 200, 200),
    HoverThumbColor = Color.FromArgb(255, 240, 180),
    ThumbMargin    = 3,
    Checked = false
};

示例 3:放大尺寸(自适应)

// 跑道、滑块、位置都会自动按比例缩放
var sw = new SwitchButton
{
    Parent = this,
    Size = new Size(100, 50),
    Location = new Point(20, 20),
    ThumbMargin = 6,       // 大控件时边距可适当加大
    OnBackColor = Color.DodgerBlue,
    OffBackColor = Color.Gray
};

示例 4:用 CheckedChanging 做“确认取消”

bool _previousChecked = false;

var sw = new SwitchButton { Parent = this, Location = new Point(20, 20) };

sw.CheckedChanging += (s, e) =>
{
    // 此时 Checked 还是旧值,记录一下
    _previousChecked = sw.Checked;
};

sw.CheckedChanged += (s, e) =>
{
    // 此时 Checked 已经是新值
    if (sw.Checked)
    {
        var result = MessageBox.Show("确认开启?", "确认",
            MessageBoxButtons.YesNo, MessageBoxIcon.Question);

        if (result == DialogResult.No)
        {
            // 用户在确认框里选了 No:还原到切换前的状态
            sw.Checked = _previousChecked;
        }
    }
};

注意:sw.Checked = _previousChecked 会再次触发 CheckedChanging 和 CheckedChanged——这是“回滚”时的固有副作用。为避免无限递归,建议在回滚时加标志位:

bool _reverting = false;
sw.CheckedChanged += (s, e) =>
{
    if (_reverting) return;
    if (sw.Checked)
    {
        if (MessageBox.Show("确认开启?", "确认", MessageBoxButtons.YesNo) == DialogResult.No)
        {
            _reverting = true;
            try { sw.Checked = _previousChecked; }
            finally { _reverting = false; }
        }
    }
};

示例 5:批量同步多个开关

var master = new SwitchButton { Parent = this, Location = new Point(20, 20) };
var slave1 = new SwitchButton { Parent = this, Location = new Point(20, 60) };
var slave2 = new SwitchButton { Parent = this, Location = new Point(20, 100) };

master.CheckedChanged += (s, e) =>
{
    slave1.Checked = master.Checked;
    slave2.Checked = master.Checked;
};




转载请注明出处。

本文链接:http://www.pythonopen.com/?id=16

相关文章

已经实现的一些算法

编码CConA1是一种可复原的编码算法,用于将文本或字节转换为纯字母。可用于文件名混淆,避免因包含违...

横向滚动条HScrollBar 控件调用文档

命名空间:CConUI继承:System.Windows.Forms.Control版本:基于所提供...

纵向滚动条VScrollBar 控件调用文档

命名空间:CConUI继承:System.Windows.Forms.Control版本:基于所提供...

菜单MenuItem 与 ContextMenu 控件调用文档

命名空间:CConUI依赖:System.Windows.Forms、System.Drawing、...

图片框PictureBox 控件调用文档

命名空间:CConUI继承:System.Windows.Forms.Control定位:自绘图片框...

蚂蚁线AntLine 控件调用文档

命名空间:CConUI继承:System.Windows.Forms.Control定位:虚线蚂蚁线...