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

ButtonA 纯色自绘按钮控件调用文档

作品墙2小时前


命名空间:CConUI
继承:System.Windows.Forms.Control
可见性:public class ButtonA
默认尺寸:110 × 32
默认事件:Click(继承自 Control;[DefaultEvent("Click")],设计器双击控件即生成该事件处理器)
定位:纯色自绘按钮——支持圆角、边框、三态背景色(常态 / 悬停 / 按下),支持鼠标左键点击、键盘 Space 触发 Click,禁用时自动变暗。


一、作用

一个完全自绘的按钮控件,具备如下特性:

  • 三态背景色:常态 ColorBack / 悬停 ColorEnter / 按下 ColorDown;

  • 圆角按钮:Radius 控制四角半径,0 为直角;

  • 可自定义边框:BorderColor / BorderSize,BorderSize=0 时无边框;

  • 禁用变暗:Enabled=false 时背景色、边框色、文字色都自动变暗(ControlPaint.Dark(..., 0.3f));

  • 鼠标点击:左键按下 → 松开(松开时仍在控件内)触发 Click;

  • 键盘支持:控件获得焦点后,Space 按下 / 松开触发 Click(无 Enter 支持);

  • 手型光标:默认 Cursors.Hand,提示可点击;

  • 文字居中:Text 在按钮中央绘制,超长自动省略号。

状态与视觉对照

状态背景色边框色文字色
常态ColorBackBorderColorForeColor
悬停ColorEnterBorderColorForeColor
按下(鼠标 / Space)ColorDownBorderColorForeColor
禁用Dark(ColorBack, 0.3)Dark(BorderColor, 0.3)Color.Gray

二、快速上手

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

// 1. 创建控件
var btn = new ButtonA();

// 2. 放入容器
btn.Parent = panel1;
btn.Location = new Point(20, 20);
btn.Size = new Size(120, 36);
btn.Text = "确定";

// 3. 订阅点击
btn.Click += (s, e) =>
{
    MessageBox.Show("点击了按钮!");
};

// 4. 外观自定义(可选)
btn.ColorBack    = Color.FromArgb(90, 100, 115);
btn.ColorEnter   = Color.FromArgb(110, 125, 140);
btn.ColorDown    = Color.FromArgb(80, 90, 100);
btn.BorderColor  = Color.FromArgb(110, 125, 140);
btn.BorderSize   = 1;
btn.Radius       = 6;
btn.ForeColor    = Color.White;
btn.Font         = new Font("Microsoft YaHei", 10F);

三、构造函数

public ButtonA()

项目说明
参数无
作用初始化纯色自绘按钮
实际行为① 启用 UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer | ResizeRedraw | Selectable 样式(自绘 + 双缓冲 + 尺寸变化自动重绘 + 可获取焦点);
② 设置 ForeColor = Color.FromArgb(250, 250, 250);
③ 设置 Size = (110, 32);
④ 设置 Cursor = Cursors.Hand
默认尺寸110 × 32
默认外观ColorBack = (90,100,115)、ColorEnter = (110,125,140)、ColorDown = (80,90,100)、BorderColor = (110,125,140)、BorderSize = 0、Radius = 0
var btn = new ButtonA
{
    Parent = panel1,
    Text = "确定"
};

四、公开属性

4.1 形状属性

public int Radius

项目说明
类型int(像素)
默认0
CategoryCCon
DefaultValue0
作用按钮四角的圆角半径。
0 → 直角矩形;
> 0 → 圆角矩形(值越大越圆)
取值范围Math.Max(0, value)——不能为负
绘制保护内部会再限制到 min(Radius, min(width, height) / 2),保证圆角不会超出按钮
副作用赋值后 Invalidate() 重绘
btn.Radius = 0;    // 直角
btn.Radius = 6;    // 小圆角
btn.Radius = 16;   // 大圆角(胶囊形的一半高度左右)

public int BorderSize

项目说明
类型int(像素)
默认0
CategoryCCon
DefaultValue0
作用边框粗细。
0 → 不绘制边框;
> 0 → 用 BorderColor 绘制相应粗细的边框
取值范围Math.Max(0, value)——不能为负
绘制处理Pen 居中描边,因此路径矩形向内缩 BorderSize / 2,让边框外边缘贴到按钮边界
副作用赋值后 Invalidate() 重绘
btn.BorderSize = 0;   // 无边框(默认)
btn.BorderSize = 1;   // 1px 边框
btn.BorderSize = 2;   // 2px 边框

4.2 颜色属性

public Color ColorBack

项目说明
类型Color
默认Color.FromArgb(90, 100, 115)(暗蓝灰)
CategoryCCon
作用按钮常态(未悬停、未按下、未禁用)下的背景色
副作用赋值后 Invalidate() 重绘

public Color ColorEnter

项目说明
类型Color
默认Color.FromArgb(110, 125, 140)(中亮蓝灰)
CategoryCCon
作用鼠标悬停(进入控件)时的背景色,通常比 ColorBack 亮一些作为高亮反馈
副作用赋值后 Invalidate() 重绘

public Color ColorDown

项目说明
类型Color
默认Color.FromArgb(80, 90, 100)(较暗的蓝灰)
CategoryCCon
作用按钮按下(鼠标左键按下 / Space 按下)时的背景色,形成“下沉”视觉效果
副作用赋值后 Invalidate() 重绘

public Color BorderColor

项目说明
类型Color
默认Color.FromArgb(110, 125, 140)
CategoryCCon
作用按钮边框的颜色;仅在 BorderSize > 0 时可见;禁用时自动变暗
副作用赋值后 Invalidate() 重绘
btn.ColorBack   = Color.FromArgb(0, 120, 215);   // 常态:蓝
btn.ColorEnter  = Color.FromArgb(0, 150, 255);   // 悬停:亮蓝
btn.ColorDown   = Color.FromArgb(0, 90, 180);    // 按下:暗蓝
btn.BorderColor = Color.FromArgb(0, 80, 150);    // 边框:深蓝

4.3 重写 / 隐藏的属性

public override string Text

项目说明
类型string
CategoryCCon
作用按钮中央显示的文字;空串时不绘制
副作用赋值后 Invalidate() 重绘
绘制居中(Center / Center),超长自动省略号(EllipsisCharacter)

public override Color ForeColor

项目说明
类型Color
默认Color.FromArgb(250, 250, 250)(构造时设置)
CategoryCCon
作用文字颜色;禁用时自动变为 Color.Gray

public override Font Font

项目说明
类型Font
CategoryCCon
作用文字字体

public override Color BackColor(隐藏)

项目说明
类型Color
Browsablefalse(属性面板中隐藏)
作用对按钮外观无影响——按钮背景由 ColorBack 控制。
此属性仅为兼容 Control 基类而保留,读写不会改变绘制效果

五、公开事件

public event EventHandler Click(继承自 Control)

项目说明
参数EventHandler:sender 为当前 ButtonA,e 为 EventArgs.Empty
默认事件是([DefaultEvent("Click")])——设计器双击即生成
触发时机① 鼠标左键:按下时在控件内 → 松开时仍在控件内(ClientRectangle.Contains(e.Location));
② 键盘 Space:OnKeyUp 里按 Space 松开时触发
不触发情况① 鼠标左键按下后移出控件再松开;
② 右键 / 中键;
③ 控件禁用(Enabled = false)——Control 基类不派发鼠标 / 键盘事件
备注本类内部通过 base.OnClick(EventArgs.Empty) 触发;如需在 Click 之前拦截,可重写 OnClick(当前未重写,即 Click 直接由基类派发)
btn.Click += (s, e) =>
{
    MessageBox.Show("点击了!");
};

六、公开方法

ButtonA 没有新增 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 与 _isPressed 并重绘(防止按住拖出后卡在“按下色”)
OnMouseDown(MouseEventArgs e)protected override左键按下时 _isPressed = true,并调用 Focus() 获取焦点
OnMouseUp(MouseEventArgs e)protected override左键松开时清 _isPressed;若松开点在客户区内则调用 OnClick(EventArgs.Empty)
OnKeyDown(KeyEventArgs e)protected overrideSpace 按下 → _isPressed = true(视觉上按下去)
OnKeyUp(KeyEventArgs e)protected overrideSpace 松开 → _isPressed = false + 触发 OnClick(EventArgs.Empty)
GetRoundedRectPath(RectangleF, float)private生成圆角矩形路径的辅助方法

七、公开成员速查表

成员来源类型说明
ButtonA()本类构造函数默认尺寸 110×32,默认无边框、直角
Radius本类属性(int)圆角半径(默认 0,≥0)
BorderSize本类属性(int)边框粗细(默认 0,≥0)
ColorBack本类属性(Color)常态背景色
ColorEnter本类属性(Color)悬停背景色
ColorDown本类属性(Color)按下背景色
BorderColor本类属性(Color)边框颜色(BorderSize > 0 时可见)
Text(重写)Control属性(string)按钮文字
ForeColor(重写)Control属性(Color)文字颜色(禁用时变 Gray)
Font(重写)Control属性(Font)文字字体
BackColor(隐藏)Control属性(Color)Browsable(false);对按钮外观无影响
EnabledControl属性(bool)是否启用;禁用时背景 / 边框 / 文字自动变暗,且鼠标键盘事件不派发
CursorControl属性默认 Hand;可自行改为 Default
Size / ClientSizeControl属性(Size)控件尺寸;影响圆角与文字区域
ClickControl事件(默认事件)鼠标左键或 Space 松开时触发
Invalidate()Control方法触发重绘
Focus()Control方法让按钮获取焦点(鼠标按下时已自动调用)
OnPaint(e)本类(override)方法(protected)绘制逻辑,外部不可直接调用
OnMouseEnter / OnMouseLeave / OnMouseDown / OnMouseUp本类(override)方法(protected)鼠标交互,外部不可直接调用
OnKeyDown / OnKeyUp本类(override)方法(protected)键盘交互,外部不可直接调用

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

8.1 确定背景色(优先级)

Color currentBackColor;
if (!this.Enabled)
    currentBackColor = ControlPaint.Dark(this._colorBack, 0.3f);
else if (this._isPressed)
    currentBackColor = this._colorDown;
else if (this._isHover)
    currentBackColor = this._colorEnter;
else
    currentBackColor = this._colorBack;
优先级状态背景色
1(最高)禁用ControlPaint.Dark(ColorBack, 0.3f)——把原色变暗 30%
2按下ColorDown
3悬停ColorEnter
4常态ColorBack

禁用优先于按下 / 悬停:即使 Enabled = false 时鼠标仍可悬停,也会显示禁用色。

8.2 计算绘制矩形

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

float halfBorder = bs / 2f;
RectangleF rect = new RectangleF(halfBorder, halfBorder, w - bs, h - bs);
要点说明
rect 内缩 halfBorderPen 居中描边时,外边缘会超出路径 BorderSize / 2。若直接 DrawPath 在 (0, 0, W, H) 上,边框一半会画到控件外被裁剪。内缩半个线宽后,边框外边缘正好贴到控件边界
bs = 0 时halfBorder = 0,rect 就是整个 (0, 0, W, H) 区域,圆角填充铺满整个控件

8.3 绘制背景与边框

using (var path = GetRoundedRectPath(rect, this._radius))
{
    using (var brush = new SolidBrush(currentBackColor))
        g.FillPath(brush, path);

    if (bs > 0)
    {
        Color currentBorderColor = this.Enabled
            ? this._borderColor
            : ControlPaint.Dark(this._borderColor, 0.3f);
        using (var pen = new Pen(currentBorderColor, bs))
            g.DrawPath(pen, path);
    }
}
要点说明
先填背景再画边框填充路径与边框路径一致,边框会覆盖在填充区域边缘上
禁用时边框也变暗用 ControlPaint.Dark(BorderColor, 0.3f) 统一处理
边框只画一次Pen 是变量,bs > 0 才创建;bs = 0 时不画,避免无谓开销

8.4 绘制文字

if (!string.IsNullOrEmpty(this.Text))
{
    Color textColor = this.Enabled ? this.ForeColor : Color.Gray;
    using (var brush = new SolidBrush(textColor))
    using (var sf = new StringFormat
    {
        Alignment = StringAlignment.Center,
        LineAlignment = StringAlignment.Center,
        Trimming = StringTrimming.EllipsisCharacter
    })
    {
        RectangleF textRect = new RectangleF(bs, bs, w - bs * 2f, h - bs * 2f);
        g.DrawString(this.Text, this.Font, brush, textRect, sf);
    }
}
要点说明
禁用时文字变灰用 Color.Gray 替代 ForeColor
textRect 内缩 bs与边框内区域对齐,避免边框过粗时文字被边框遮挡;宽度用 w - bs*2、高度用 h - bs*2,与 rect 的“内缩半线宽”不同——文字需要在边框内侧完整区域
Trimming = EllipsisCharacter文字超长自动省略号,避免溢出

8.5 圆角路径生成

private GraphicsPath GetRoundedRectPath(RectangleF rect, float radius)
{
    var path = new GraphicsPath();
    float r = Math.Min(radius, Math.Min(rect.Width, rect.Height) / 2f);
    float d = r * 2f;

    if (r <= 0)
    {
        path.AddRectangle(rect);
        return path;
    }

    path.AddArc(rect.X, rect.Y, d, d, 180, 90);                     // 左上角
    path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);             // 右上角
    path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90);      // 右下角
    path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);             // 左下角
    path.CloseFigure();
    return path;
}
要点说明
r = min(radius, min(width, height) / 2)圆角半径不能超过边长度的一半,否则四角会互相穿透变形
四段圆弧左上 180→270、右上 270→360、右下 0→90、左下 90→180,组成圆角矩形
r ≤ 0直角时直接 AddRectangle

九、交互一览

操作效果
鼠标进入控件_isHover = true,背景变 ColorEnter
鼠标离开控件_isHover = false、_isPressed = false,背景回到常态
左键按下_isPressed = true,Focus() 获取焦点,背景变 ColorDown
左键松开(在控件内)_isPressed = false,触发 Click,背景回到悬停或常态
左键松开(移出控件)_isPressed = false,不触发 Click
Space 按下(有焦点)_isPressed = true,背景变 ColorDown
Space 松开(有焦点)_isPressed = false,触发 Click,背景恢复
Enter / 其它键不响应(本类只处理 Space)
右键 / 中键不响应,不触发 Click,也不改变视觉状态
禁用(Enabled=false)背景 / 边框 / 文字自动变暗;鼠标键盘事件不派发;不响应悬停 / 按下

十、完整使用示例

示例 1:基础按钮

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

var btn = new ButtonA
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(120, 36),
    Text = "确定"
};

btn.Click += (s, e) =>
{
    MessageBox.Show("点击了确定!");
};

示例 2:圆角 + 边框按钮

var btn = new ButtonA
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(140, 40),
    Text = "圆角按钮",
    Radius = 12,
    BorderSize = 1,
    ColorBack   = Color.FromArgb(60, 120, 200),
    ColorEnter  = Color.FromArgb(80, 140, 220),
    ColorDown   = Color.FromArgb(40, 90, 160),
    BorderColor = Color.FromArgb(40, 90, 160),
    ForeColor   = Color.White,
    Font        = new Font("Microsoft YaHei", 10F, FontStyle.Bold)
};

示例 3:胶囊形按钮(圆角 ≈ 高度一半)

var btn = new ButtonA
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(180, 40),
    Text = "胶囊按钮",
    Radius = 20,               // = 高度 / 2
    ColorBack = Color.FromArgb(0, 180, 120),
    ColorEnter = Color.FromArgb(0, 210, 150),
    ColorDown = Color.FromArgb(0, 150, 100),
    BorderSize = 0,
    ForeColor = Color.White,
    Font = new Font("Microsoft YaHei", 10F)
};

示例 4:禁用按钮

var btn = new ButtonA
{
    Parent = this,
    Location = new Point(20, 20),
    Text = "禁用按钮",
    ColorBack = Color.Orange
};

// 未满足条件时禁用
btn.Enabled = false;

// 条件满足后启用
btn.Enabled = true;

示例 5:键盘操作按钮

var btn = new ButtonA
{
    Parent = this,
    Location = new Point(20, 20),
    Text = "Tab 聚焦 + Space 点击",
    TabStop = true      // 允许 Tab 键聚焦
};

btn.Click += (s, e) => MessageBox.Show("Space 触发");

说明:本类已通过 ControlStyles.Selectable 允许获取焦点。用户按 Tab 切到按钮后,按 Space 会先显示“按下”视觉,松开 Space 触发 Click。

示例 6:动态修改外观(例如“主要 / 次要”切换)

void SetPrimary(ButtonA btn)
{
    btn.ColorBack   = Color.FromArgb(0, 120, 215);
    btn.ColorEnter  = Color.FromArgb(0, 150, 255);
    btn.ColorDown   = Color.FromArgb(0, 90, 180);
    btn.ForeColor   = Color.White;
}

void SetSecondary(ButtonA btn)
{
    btn.ColorBack   = Color.FromArgb(230, 230, 230);
    btn.ColorEnter  = Color.FromArgb(240, 240, 240);
    btn.ColorDown   = Color.FromArgb(200, 200, 200);
    btn.ForeColor   = Color.Black;
}

SetPrimary(btnOk);
SetSecondary(btnCancel);

示例 7:状态机控制(例如加载中禁用)

async void BtnSubmit_Click(object sender, EventArgs e)
{
    btnSubmit.Enabled = false;
    btnSubmit.Text = "提交中…";

    try
    {
        await DoSubmitAsync();
        btnSubmit.Text = "已提交";
    }
    finally
    {
        btnSubmit.Enabled = true;
        btnSubmit.Text = "提交";
    }
}

十一、注意事项

  1. Click 需要“按下与松开都在控件内”:用户按下左键后把鼠标拖到控件外再松开不会触发 Click。这是本类的设计,避免误点击。

  2. 右键 / 中键不触发 Click:OnMouseDown / OnMouseUp 都只判断 MouseButtons.Left。

  3. Space 松开时触发,Space 按下时只是视觉反馈:OnKeyDown 只改 _isPressed,OnKeyUp 才派发 Click。

  4. 不支持 Enter 键触发:本类只处理 Keys.Space。如需 Enter 触发,需要自行扩展 OnKeyDown / OnKeyUp。

  5. Tab 聚焦需要 TabStop = true:Control 的 TabStop 默认是 false,本类未显式设置。要让键盘 Space 在无鼠标点击的情况下生效,请在创建时设置 TabStop = true。

  6. 鼠标按下会自动 Focus():点击按钮后按钮获取焦点,即可继续用 Space 模拟点击。

  7. 禁用时视觉变暗但不会自动暂停动画:本类无动画,所以无影响。但如果外部有定时器定时改 Text,禁用后文字仍是 Color.Gray 绘制,不会因禁用而停止更新。

  8. ColorBack / ColorEnter / ColorDown / BorderColor 都支持 ARGB:可以设置带透明度的颜色;由于本类未开启 SupportsTransparentBackColor,背景的实际表现取决于 Control 默认行为——OnPaint 里用 SolidBrush 填充,透明部分会透出 Control 默认背景,可能不符合预期,建议保持 A=255。

  9. BackColor 对按钮无效:已被 Browsable(false) 隐藏,代码设置也不会改变绘制。要改按钮颜色请用 ColorBack。

  10. BorderSize 增大时,可用文字区域会相应缩小:textRect 会内缩 BorderSize,所以大边框可能让文字显示区变小,甚至文字被省略号截断。建议 BorderSize 不要超过控件高度的 1/4。

  11. Radius 上限会被内部限制:即使设为 100,实际效果也只会是 min(100, min(width, height) / 2)——即“胶囊形”是圆角的最大值。

  12. 控件高度极小时圆角可能绘制异常:GetRoundedRectPath 里对 r 有 clamp 保护,但高度 < 6 时视觉会非常小,建议 Size.Height ≥ 20。

  13. Cursor 默认为 Hand:如果不需要“可点击”提示,可改为 Cursors.Default。禁用时 Control 仍会显示 Hand(不会自动切换),如需在禁用时显示默认光标,请自行在 EnabledChanged 事件中处理。

  14. 本类未重写 OnClick:Click 事件由 base.OnClick(EventArgs.Empty) 派发。若需要“点击前拦截”,可以继承并重写 OnClick,但本类本身不提供该能力。

  15. 与原生 Button 的差异:

    • 无 FlatStyle / FlatAppearance / Image / ImageAlign / TextAlign 等属性;

    • 无 DialogResult 属性(如果用于对话框,需要外部处理 Click 后手动设置 DialogResult);

    • 无 UseVisualStyleBackColor / AutoSize 等;

    • 文字始终居中,不支持图片。

  16. DoubleBuffered 通过 OptimizedDoubleBuffer 开启:绘制平滑无闪烁;如果在 MouseMove 中频繁改状态,也不会造成可见的撕裂。

  17. EnabledChanged 事件不影响悬停状态:本类在 OnPaint 里通过 !Enabled 判断,不依赖事件;从禁用恢复启用时,如果鼠标仍在控件内,会直接显示悬停色(_isHover 字段保留),无需额外处理。

  18. 大小改变立即重绘:ResizeRedraw 样式已开启,无需手动 Invalidate。

  19. 文字使用 ClearTypeGridFit 渲染:在透明 / 深色背景下可能有轻微彩色边缘,如需更干净的文字,可改用 AntiAliasGridFit(需在源码中修改 TextRenderingHint)。

  20. 不能作为容器:本类是 Control 的直接子类,不建议在其上 Controls.Add(...) 叠加子控件,绘制会被遮挡或布局混乱。


转载请注明出处。

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

相关文章

已经实现的一些算法

编码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定位:虚线蚂蚁线...