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

ButtonB 图片自绘按钮控件调用文档

作品墙3小时前



命名空间:CConUI
继承:System.Windows.Forms.Control
可见性:public class ButtonB
默认尺寸:110 × 32
默认事件:Click(继承自 Control;[DefaultEvent("Click")],设计器双击控件即生成该事件处理器)
定位:图片自绘按钮——用图片作为按钮外观,支持常态 / 悬停 / 按下三种图片切换,图片等比缩放居中绘制;支持鼠标左键点击、键盘 Space 触发 Click;禁用时文字自动变灰。


一、作用

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

  • 三态图片切换:常态 ImageBack / 悬停 ImageEnter / 按下 ImageDown,带回退机制(未设置的图片自动回落到上一级);

  • 图片等比缩放居中:图片按原始宽高比等比缩放,不拉伸变形,居中绘制在控件内;

  • 高质量缩放:使用 InterpolationMode.HighQualityBicubic 平滑缩放;

  • 文字叠加:Text 居中绘制在图片上层,超长自动省略号;

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

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

  • 手型光标:默认 Cursors.Hand;

  • 禁用视觉:Enabled=false 时文字变 Color.Gray,图片仍按 ImageBack 绘制(不自动置灰)。

状态与图片回退规则

状态使用的图片文字颜色
禁用(Enabled=false)ImageBack(忽略悬停 / 按下)Color.Gray
按下 + 设置了 ImageDownImageDownForeColor
悬停 + 设置了 ImageEnterImageEnterForeColor
其它情况(含按下但未设 ImageDown、悬停但未设 ImageEnter)ImageBackForeColor

回退机制说明:ImageDown 或 ImageEnter 为 null 时,会自动降级使用 ImageBack。因此只设置 ImageBack 也能正常工作(相当于普通图片按钮,无悬停 / 按下切换)。


二、快速上手

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

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

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

// 3. 设置图片(只需 ImageBack 也能工作)
btn.ImageBack = Image.FromFile("btn_normal.png");
btn.ImageEnter = Image.FromFile("btn_hover.png");
btn.ImageDown = Image.FromFile("btn_pressed.png");

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

三、构造函数

public ButtonB()

项目说明
参数无
作用初始化图片自绘按钮
实际行为① 启用 UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer | ResizeRedraw | Selectable 样式(自绘 + 双缓冲 + 尺寸变化自动重绘 + 可获取焦点);
② 设置 Size = (110, 32);
③ 设置 Font = new Font("微软雅黑", 10);
④ 设置 Cursor = Cursors.Hand;
⑤ 设置 ForeColor = Color.FromArgb(250, 250, 250)
默认尺寸110 × 32
默认图片三张图片均为 null(不绘制任何图片,仅显示文字)
var btn = new ButtonB
{
    Parent = panel1,
    Text = "确定"
};

四、公开属性

4.1 图片属性

public Image ImageBack

项目说明
类型System.Drawing.Image
默认null
CategoryCCon
作用按钮常态下显示的背景图片;同时也是禁用状态、以及未设置悬停 / 按下图片时的回退图片
副作用赋值后 Invalidate() 重绘
备注控件不负责 Dispose 传入的 Image;图片资源由调用方管理

public Image ImageEnter

项目说明
类型Image
默认null
CategoryCCon
作用按钮悬停时显示的背景图片;null 时回退到 ImageBack
副作用赋值后 Invalidate() 重绘

public Image ImageDown

项目说明
类型Image
默认null
CategoryCCon
作用按钮按下时显示的背景图片;null 时回退到 ImageEnter(若也未设置则到 ImageBack)
副作用赋值后 Invalidate() 重绘
btn.ImageBack  = Image.FromFile("normal.png");   // 常态
btn.ImageEnter = Image.FromFile("hover.png");    // 悬停
btn.ImageDown  = Image.FromFile("pressed.png");  // 按下

// 只设置 ImageBack 也可以工作(悬停/按下会自动用 ImageBack)
btn.ImageEnter = null;
btn.ImageDown  = null;

4.2 文本属性(重写)

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
默认"微软雅黑", 10(构造时设置)
CategoryCCon
作用文字字体

public override Color BackColor(隐藏)

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

五、公开事件

public event EventHandler Click(继承自 Control)

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

六、公开方法

ButtonB 没有新增 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)
DrawImageStretchedAndCentered(Graphics, Image, Rectangle)private将图片按等比缩放并居中绘制到目标区域的辅助方法

七、公开成员速查表

成员来源类型说明
ButtonB()本类构造函数默认尺寸 110×32,默认字体微软雅黑 10pt
ImageBack本类属性(Image)常态图片 / 回退图片(默认 null)
ImageEnter本类属性(Image)悬停图片(默认 null,回退到 ImageBack)
ImageDown本类属性(Image)按下图片(默认 null,回退到 ImageEnter / ImageBack)
Text(重写)Control属性(string)文字(居中绘制在图片上方)
ForeColor(重写)Control属性(Color)文字颜色(禁用时变 Gray)
Font(重写)Control属性(Font)文字字体
BackColor(隐藏)Control属性(Color)Browsable(false);对按钮外观无影响
EnabledControl属性(bool)是否启用;禁用时文字变灰,图片固定用 ImageBack
CursorControl属性默认 Hand;可自行改为 Default
TabStopControl属性(bool)默认 false;要支持 Tab 聚焦 + Space 触发需设为 true
Size / ClientSizeControl属性(Size)控件尺寸;决定图片绘制区域与文字区域
ClickControl事件(默认事件)鼠标左键或 Space 松开时触发
Invalidate()Control方法触发重绘
Focus()Control方法让按钮获取焦点(鼠标按下时已自动调用)
OnPaint(e)本类(override)方法(protected)绘制逻辑,外部不可直接调用
OnMouseEnter / OnMouseLeave / OnMouseDown / OnMouseUp本类(override)方法(protected)鼠标交互,外部不可直接调用
OnKeyDown / OnKeyUp本类(override)方法(protected)键盘交互,外部不可直接调用
DrawImageStretchedAndCentered本类方法(private)等比缩放居中绘制辅助方法,外部不可直接调用

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

8.1 选择当前图片(多级回退)

Image currentImage = null;
if (!this.Enabled)
    currentImage = this._imageBack;
else if (this._isPressed && this._imageDown != null)
    currentImage = this._imageDown;
else if (this._isHover && this._imageEnter != null)
    currentImage = this._imageEnter;
else
    currentImage = this._imageBack;
优先级条件使用的图片
1(最高)禁用(!Enabled)ImageBack(忽略悬停/按下)
2按下 + 设置了 ImageDownImageDown
3悬停 + 设置了 ImageEnterImageEnter
4(兜底)其它情况ImageBack

按下但未设 ImageDown:会落到“悬停”分支,如果设置了 ImageEnter 就显示悬停图;否则显示 ImageBack。这是合理的“按下回退”。

8.2 等比缩放居中绘制

float imgRatio = (float)img.Width / img.Height;
float rectRatio = (float)destRect.Width / destRect.Height;

int drawWidth, drawHeight;
if (imgRatio > rectRatio)
{
    drawWidth = destRect.Width;
    drawHeight = (int)(drawWidth / imgRatio);
}
else
{
    drawHeight = destRect.Height;
    drawWidth = (int)(drawHeight * imgRatio);
}

int drawX = destRect.X + (destRect.Width - drawWidth) / 2;
int drawY = destRect.Y + (destRect.Height - drawHeight) / 2;
要点说明
imgRatio / rectRatio图片原始宽高比 / 目标区域宽高比
imgRatio > rectRatio图片相对更“扁”,以宽度为基准缩放;高度按比例算
imgRatio <= rectRatio图片相对更“高”,以高度为基准缩放;宽度按比例算
效果始终在区域内“完整显示”图片(类似 PictureBoxSizeMode.Zoom),不拉伸不变形
drawX / drawY居中偏移:(destRect.Width - drawWidth) / 2 是左右多余半宽
缩放质量InterpolationMode.HighQualityBicubic,比默认的低质量缩放平滑很多

8.3 绘制文字

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
    })
    {
        g.DrawString(this.Text, this.Font, brush, this.ClientRectangle, sf);
    }
}
要点说明
禁用时文字变灰用 Color.Gray 替代 ForeColor
居中 + 省略号Center / Center 居中,超长自动 EllipsisCharacter
绘制在整个 ClientRectangle 上不需要考虑图片的内缩;文字浮在图片上方居中

九、交互一览

操作效果
鼠标进入控件_isHover = true,显示 ImageEnter(若设置),否则 ImageBack
鼠标离开控件_isHover = false、_isPressed = false,恢复 ImageBack
左键按下_isPressed = true,Focus() 获取焦点,显示 ImageDown(若设置),否则回退
左键松开(在控件内)_isPressed = false,触发 Click,显示悬停图或常态图
左键松开(移出控件)_isPressed = false,不触发 Click
Space 按下(有焦点)_isPressed = true,显示 ImageDown
Space 松开(有焦点)_isPressed = false,触发 Click,显示悬停 / 常态图
Enter / 其它键不响应(本类只处理 Space)
右键 / 中键不响应,不触发 Click,也不改变图片
禁用(Enabled=false)固定显示 ImageBack;文字变 Color.Gray;鼠标键盘事件不派发

十、完整使用示例

示例 1:基础图片按钮(仅常态图)

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

var btn = new ButtonB
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(120, 40),
    Text = "确定",
    ImageBack = Image.FromFile("btn.png")
};

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

示例 2:三态图片按钮(推荐)

var btn = new ButtonB
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(140, 44),
    Text = "提交",
    ImageBack  = Image.FromFile("btn_normal.png"),
    ImageEnter = Image.FromFile("btn_hover.png"),
    ImageDown  = Image.FromFile("btn_pressed.png")
};

btn.Click += (s, e) =>
{
    MessageBox.Show("提交成功!");
};

示例 3:只设置悬停图(按下时回退到悬停图)

var btn = new ButtonB
{
    Parent = this,
    Text = "只设置悬停图",
    ImageBack  = Image.FromFile("btn_normal.png"),
    ImageEnter = Image.FromFile("btn_hover.png")
    // ImageDown 不设置 → 按下时也会显示 ImageEnter
};

示例 4:图标按钮(小尺寸)

var btn = new ButtonB
{
    Parent = this,
    Size = new Size(32, 32),      // 正方形,适合图标
    Text = "",                     // 不需要文字
    ImageBack  = Image.FromFile("icon_normal.png"),
    ImageEnter = Image.FromFile("icon_hover.png"),
    ImageDown  = Image.FromFile("icon_pressed.png")
};

示例 5:禁用按钮

var btn = new ButtonB
{
    Parent = this,
    Text = "禁用按钮",
    ImageBack = Image.FromFile("btn.png"),
    Enabled = false               // 禁用:固定显示 ImageBack,文字变 Gray
};

// 业务需要时启用
btn.Enabled = true;

示例 6:键盘操作(Tab 聚焦 + Space 点击)

var btn = new ButtonB
{
    Parent = this,
    Text = "Tab 聚焦 + Space 点击",
    ImageBack = Image.FromFile("btn.png"),
    TabStop = true                // 允许 Tab 键聚焦
};

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

示例 7:动态切换主题图片

void ApplyDarkTheme(ButtonB btn)
{
    btn.ImageBack  = Image.FromFile("btn_dark_normal.png");
    btn.ImageEnter = Image.FromFile("btn_dark_hover.png");
    btn.ImageDown  = Image.FromFile("btn_dark_pressed.png");
    btn.ForeColor  = Color.White;
}

void ApplyLightTheme(ButtonB btn)
{
    btn.ImageBack  = Image.FromFile("btn_light_normal.png");
    btn.ImageEnter = Image.FromFile("btn_light_hover.png");
    btn.ImageDown  = Image.FromFile("btn_light_pressed.png");
    btn.ForeColor  = Color.Black;
}

ApplyDarkTheme(btnSave);
ApplyLightTheme(btnCancel);

示例 8:异步提交按钮(加载中禁用)

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. 图片资源需要调用方自行管理:控件不负责 Dispose 传入的 Image。更换图片时,请自行释放旧图片;否则会内存泄漏。

  8. ImageDown / ImageEnter 为 null 时自动回退:因此你可以只设置 ImageBack,也能作为一个可点击的图片按钮使用。

    • 按下但未设 ImageDown:落到悬停分支(ImageEnter)或 ImageBack;

    • 悬停但未设 ImageEnter:ImageBack。

  9. BackColor 对按钮无效:已被 Browsable(false) 隐藏,代码设置也不会改变绘制。按钮外观完全由图片控制。

  10. 图片按等比缩放居中,不拉伸填满:若图片宽高比与控件宽高比不一致,会出现留白(顶部/底部 或 左侧/右侧)。
    如果需要“拉伸铺满”,需要修改源码 DrawImageStretchedAndCentered 用 g.DrawImage(img, destRect)。

  11. 禁用状态不会自动置灰图片:本类在禁用时仍按 ImageBack 原色绘制图片,只把文字变 Color.Gray。若需要“禁用图片变灰”,需要在设计阶段准备一张“禁用态”图片,并在外部手动切换 ImageBack。

  12. 文字始终叠加在图片之上:无论图片是否设置,Text 都会在控件中心绘制。若图片本身已包含文字,建议将 Text 设为空串。

  13. 控件尺寸极小(如 < 8×8)时图片可能缩到不足 1px:绘制时用 int 计算 drawWidth / drawHeight,若原图比区域更“扁”,小尺寸可能让 drawHeight 被截断为 0。本类未做最小 1px 保护,建议 Size 不要设得太小。

  14. DrawImageStretchedAndCentered 未使用 ImageAttributes:如果图片是带 Alpha 通道的 PNG,透明部分会透出 Control 默认背景(通常是 SystemColors.Control)。
    如需真正的透明按钮背景,需要把 BackColor 设为父控件颜色,或开启 SupportsTransparentBackColor(本类未开启)。

  15. 与原生 Button 的差异:

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

    • 无 ImageList / ImageIndex / ImageKey(图片需要直接赋 Image 对象);

    • 文字始终居中,不支持图片 + 文字左右排布。

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

  17. 图片加载方式:Image.FromFile(...) 加载的图片会锁住文件句柄,建议改用 using (var fs = File.OpenRead(...)) { image = Image.FromStream(fs); } 或 new Bitmap(path),避免后续无法修改文件。

  18. 默认字体 “微软雅黑” 通过代码指定:若目标系统没有该字体,WinForms 会自动回退到默认字体,不会抛异常;但对字体有严格要求时建议改换。

  19. 本类未重写 Dispose:其持有的 Image 对象不会在控件销毁时自动释放(因为图片资源可能被其它地方共享),需要调用方自行管理。

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

转载请注明出处。

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

返回列表

上一篇:ButtonA 纯色自绘按钮控件调用文档

没有最新的文章了...

相关文章

已经实现的一些算法

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