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

CircularButtonB 圆形图片自绘按钮控件调用文档

作品墙1小时前


命名空间:CConUI
继承:System.Windows.Forms.Control
可见性:public class CircularButtonB
默认尺寸:60 × 60(正方形)
默认事件:Click(继承自 Control;[DefaultEvent("Click")],设计器双击控件即生成该事件处理器)
定位:圆形图片自绘按钮——用图片作为按钮外观,自动将图片居中裁剪为正圆;支持常态 / 悬停 / 按下三态图片切换、可自定义边框、抗锯齿开关、键盘 Space 触发 Click;背景透明,图片边缘可平滑抗锯齿。


一、作用

完全自绘的圆形图片按钮,特点:

  • 自适应正圆:以 min(宽, 高) 作为直径居中绘制正圆,非正方形控件也不会变形;

  • 图片自动裁剪为圆:先把原图按中心正方形(Center-Crop)裁剪,再通过 GraphicsPath.AddEllipse + TextureBrush 填充成圆形——图片铺满整圆不拉伸变形,也不会溢出到圆外;

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

  • 抗锯齿开关:SmoothImage = true(默认)使用临时位图 + TextureBrush 高质抗锯齿;false 使用 SetClip 硬裁剪(更快但有轻微锯齿);

  • 可自定义边框:BorderSize / BorderColor,圆边框绘制在图片边缘,可进一步掩盖边缘瑕疵;

  • 透明背景:启用 SupportsTransparentBackColor,圆外四角区域透明;

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

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

  • 文字居中:Text 在圆中央绘制,使用 TextRenderer 完美居中,超长自动省略号。

状态与图片回退规则

状态使用的图片文字颜色
禁用(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 CircularButtonB();

// 2. 放入容器
btn.Parent = panel1;
btn.Location = new Point(20, 20);
// 默认 60×60;可放大,建议保持正方形
btn.Size = new Size(72, 72);
btn.Text = "OK";

// 3. 设置三态图片(只需 ImageBack 也能用)
btn.ImageBack  = Image.FromFile("avatar_normal.png");
btn.ImageEnter = Image.FromFile("avatar_hover.png");
btn.ImageDown  = Image.FromFile("avatar_pressed.png");

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

// 5. 可选外观
btn.BorderSize  = 2;
btn.BorderColor = Color.FromArgb(139, 148, 158);
btn.SmoothImage = true;   // 抗锯齿(默认)

三、构造函数

public CircularButtonB()

项目说明
参数无
作用初始化圆形图片按钮
实际行为① 启用 UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer | ResizeRedraw | Selectable | SupportsTransparentBackColor 样式(自绘 + 双缓冲 + 尺寸变化自动重绘 + 可获取焦点 + 透明背景);
② 设置 Size = (60, 60)(正方形,默认正圆);
③ 设置 Font = new Font("微软雅黑", 10);
④ 设置 Cursor = Cursors.Hand;
⑤ 设置 ForeColor = Color.FromArgb(250, 250, 250);
⑥ 设置 BackColor = Color.Transparent
默认尺寸60 × 60(正方形)
默认外观SmoothImage = true、BorderSize = 0、BorderColor = (139, 148, 158)
默认图片三张图片均为 null(不绘制任何图片,只显示文字)
var btn = new CircularButtonB
{
    Parent = panel1,
    Text = "OK"
};

四、公开属性

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 也可以工作
btn.ImageEnter = null;
btn.ImageDown  = null;

4.2 抗锯齿与边框

public bool SmoothImage

项目说明
类型bool
默认true
CategoryCCon
DefaultValuetrue
作用是否对圆形图片边缘开启抗锯齿:
true(默认):创建临时 Bitmap,用 TextureBrush + GraphicsPath 填充,边缘极其平滑;
false:使用 Graphics.SetClip 硬裁剪,速度更快但圆边可能有轻微锯齿
副作用赋值后 Invalidate() 重绘
性能影响true 每帧会创建临时 Bitmap + Graphics(图片大时有一定开销);false 更快
btn.SmoothImage = true;    // 高质抗锯齿(默认)
btn.SmoothImage = false;   // 快速硬裁剪

public int BorderSize

项目说明
类型int(像素)
默认0
CategoryCCon
DefaultValue0
作用圆形边框粗细:
0 → 无边框;
> 0 → 用 BorderColor 绘制圆形边框,图片会被内缩半个边框宽度,让边框外边缘贴到圆的外接正方形
取值范围Math.Max(0, value)——不能为负
副作用赋值后 Invalidate() 重绘
btn.BorderSize = 0;   // 无边框(默认)
btn.BorderSize = 2;   // 2px 圆形边框

public Color BorderColor

项目说明
类型Color
默认Color.FromArgb(139, 148, 158)(浅灰)
CategoryCCon
作用圆形边框颜色;仅在 BorderSize > 0 时可见
副作用赋值后 Invalidate() 重绘
btn.BorderColor = Color.FromArgb(0, 120, 215);

4.3 文本属性(重写)

public override string Text

项目说明
类型string
CategoryCCon
作用圆中央显示的文字;空串时不绘制
副作用赋值后 Invalidate() 重绘
绘制使用 TextRenderer.DrawText 在圆的外接正方形内水平 + 垂直居中;NoPadding 保证绝对居中;超长自动省略号(EndEllipsis)

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(属性面板中隐藏)
作用对按钮外观无影响——按钮外观由图片控制。
构造时已设置为 Color.Transparent,让圆外四角区域透明(透出父容器)

五、公开事件

public event EventHandler Click(继承自 Control)

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

六、公开方法

CircularButtonB 没有新增 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)

七、公开成员速查表

成员来源类型说明
CircularButtonB()本类构造函数默认尺寸 60×60,默认字体微软雅黑 10pt,背景透明
ImageBack本类属性(Image)常态图片 / 回退图片(默认 null)
ImageEnter本类属性(Image)悬停图片(默认 null,回退到 ImageBack)
ImageDown本类属性(Image)按下图片(默认 null,回退到 ImageEnter / ImageBack)
SmoothImage本类属性(bool)是否抗锯齿(默认 true)
BorderSize本类属性(int)圆形边框粗细(默认 0,≥0)
BorderColor本类属性(Color)边框颜色
Text(重写)Control属性(string)圆中央文字
ForeColor(重写)Control属性(Color)文字颜色(禁用时变 Gray)
Font(重写)Control属性(Font)文字字体
BackColor(隐藏)Control属性(Color)Browsable(false);构造时设为 Transparent,圆外四角透明
EnabledControl属性(bool)是否启用;禁用时图片固定用 ImageBack,文字变 Gray
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)键盘交互,外部不可直接调用

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

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

8.2 计算正圆区域

float side = Math.Min(ClientSize.Width, ClientSize.Height);
if (side <= 4f) return;

float ox = (ClientSize.Width - side) / 2f;
float oy = (ClientSize.Height - side) / 2f;
RectangleF outerRect = new RectangleF(ox, oy, side, side);

float halfBorder = this._borderSize / 2f;
RectangleF imageRect = RectangleF.Inflate(outerRect, -halfBorder, -halfBorder);

if (imageRect.Width <= 0 || imageRect.Height <= 0) return;
要点说明
side = min(宽, 高)取短边作为圆的直径,不论控件是什么形状都画正圆,多余空间留白
ox / oy水平 / 垂直居中偏移
side ≤ 4控件过小时直接不绘制
imageRect 内缩 halfBorder边框 Pen 居中描边,图片需要内缩半个边框宽度,让边框外边缘正好贴到圆的外接正方形 outerRect
imageRect 无效边框过大导致内圆为空时直接返回

8.3 中心裁剪(Center-Crop)

float minSide = Math.Min(currentImage.Width, currentImage.Height);
float sx = (currentImage.Width - minSide) / 2f;
float sy = (currentImage.Height - minSide) / 2f;
RectangleF srcRect = new RectangleF(sx, sy, minSide, minSide);
要点说明
minSide = min(宽, 高)取原图短边作为正方形边长
sx / sy从原图中心裁出 minSide × minSide 的正方形;
(W - minSide) / 2 = 左侧需裁掉的宽度,保证裁剪后正方形居中于原图
效果不管原图是横图、竖图、还是方图,都以中心正方形作为后续圆形填充的源区域,不变形、不拉伸

8.4 抗锯齿路径(SmoothImage=true)

int bmpWidth = Math.Max(1, (int)Math.Ceiling(imageRect.Width));
int bmpHeight = Math.Max(1, (int)Math.Ceiling(imageRect.Height));

using (var tempBmp = new Bitmap(bmpWidth, bmpHeight))
using (var tempG = Graphics.FromImage(tempBmp))
{
    tempG.InterpolationMode = InterpolationMode.HighQualityBicubic;
    tempG.PixelOffsetMode = PixelOffsetMode.Half;

    tempG.DrawImage(currentImage, new Rectangle(0, 0, bmpWidth, bmpHeight), srcRect, GraphicsUnit.Pixel);

    using (var brush = new TextureBrush(tempBmp))
    {
        brush.TranslateTransform(imageRect.X, imageRect.Y);
        g.FillPath(brush, clipPath);
    }
}
要点说明
Math.Max(1, Ceiling(...))临时位图宽高必须 ≥1,否则 new Bitmap(0, 0) 会抛异常;用 Ceiling 保证非整数尺寸也能完全覆盖目标区域
tempG.DrawImage(..., srcRect, ...)把中心裁剪的正方形区域拉伸到整张临时位图;临时位图 = 目标圆的直径,所以拉伸后的图铺满圆
brush.TranslateTransform核心修复:TextureBrush 默认从 (0,0) 开始平铺——而圆的起点在 imageRect.X, imageRect.Y,如果不用 TranslateTransform,纹理会错位偏移
g.FillPath(brush, clipPath)clipPath 是 AddEllipse(imageRect) 生成的圆形路径;用 TextureBrush 填充,GDI+ 会自动抗锯齿边缘——比 SetClip 硬裁剪更平滑
性能每帧创建临时 Bitmap + Graphics;图片较大或控件频繁重绘时有 CPU / 内存开销

8.5 硬裁剪路径(SmoothImage=false)

GraphicsState state = g.Save();
g.SetClip(clipPath, CombineMode.Replace);
g.DrawImage(currentImage, imageRect, srcRect, GraphicsUnit.Pixel);
g.Restore(state);
要点说明
SetClip(clipPath, Replace)把绘制区域限制在圆形路径内,超出的部分被丢弃
边缘质量锯齿明显:GDI+ 的 SetClip 使用硬裁剪,SmoothingMode.AntiAlias 对裁剪边缘无效
性能比抗锯齿方案快(无需创建临时 Bitmap)

8.6 绘制边框

using (var pen = new Pen(this._borderColor, this._borderSize))
{
    g.DrawEllipse(pen, imageRect);
}
要点说明
边框绘制在 imageRect 上与图片裁剪区域相同,边框内侧贴合图片边缘,可以掩盖图片边缘的瑕疵
Pen 居中描边imageRect 已内缩 halfBorder,所以边框外边缘正好贴到 outerRect
绘制在图片之后边框覆盖图片边缘,视觉更干净

8.7 绘制文字

TextRenderer.DrawText(
    g,
    this.Text,
    this.Font,
    Rectangle.Round(outerRect),
    textColor,
    TextFormatFlags.HorizontalCenter |
    TextFormatFlags.VerticalCenter |
    TextFormatFlags.EndEllipsis |
    TextFormatFlags.NoPadding
);
要点说明
TextRenderer 而非 DrawString基于 GDI 绘制,字体渲染更清晰;NoPadding 保证垂直完美居中(避免 DrawString 偏上问题)
绘制区域为 outerRect即圆的外接正方形;文字在正方形内居中 → 视觉上在圆中央
禁用时文字变灰用 Color.Gray 替代 ForeColor

九、交互一览

操作效果
鼠标进入控件_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 CircularButtonB
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(60, 60),
    Text = "",
    ImageBack = Image.FromFile("avatar.png")
};

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

示例 2:三态图片 + 边框(推荐)

var btn = new CircularButtonB
{
    Parent = this,
    Location = new Point(20, 20),
    Size = new Size(72, 72),
    Text = "",
    ImageBack  = Image.FromFile("avatar_normal.png"),
    ImageEnter = Image.FromFile("avatar_hover.png"),
    ImageDown  = Image.FromFile("avatar_pressed.png"),
    BorderSize = 2,
    BorderColor = Color.FromArgb(139, 148, 158),
    SmoothImage = true
};

btn.Click += (s, e) =>
{
    // 打开用户资料
    ShowUserProfile();
};

示例 3:只设置常态图(无悬停/按下切换)

var btn = new CircularButtonB
{
    Parent = this,
    Text = "",
    Size = new Size(48, 48),
    ImageBack = Image.FromFile("icon.png"),
    BorderSize = 1,
    BorderColor = Color.LightGray
};

示例 4:禁用抗锯齿(性能优化)

// 图片较大或按钮数量很多时,可关闭 SmoothImage 提升性能
var btn = new CircularButtonB
{
    Parent = this,
    Size = new Size(80, 80),
    ImageBack = Image.FromFile("bigavatar.jpg"),
    SmoothImage = false,    // 硬裁剪(圆边轻微锯齿)
    BorderSize = 2          // 边框可以部分掩盖锯齿
};

示例 5:禁用状态

var btn = new CircularButtonB
{
    Parent = this,
    Size = new Size(60, 60),
    ImageBack = Image.FromFile("avatar.png"),
    BorderSize = 2,
    Enabled = false    // 禁用:图片固定 ImageBack,文字变 Gray
};

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

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

var btn = new CircularButtonB
{
    Parent = this,
    Size = new Size(60, 60),
    ImageBack = Image.FromFile("avatar.png"),
    TabStop = true      // 允许 Tab 键聚焦
};

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

示例 7:带文字叠加的图片按钮

var btn = new CircularButtonB
{
    Parent = this,
    Size = new Size(80, 80),
    Text = "点击",
    ForeColor = Color.White,
    Font = new Font("Microsoft YaHei", 9F, FontStyle.Bold),
    ImageBack = Image.FromFile("darkbg.png"),
    BorderSize = 2,
    BorderColor = Color.FromArgb(0, 120, 215)
};

示例 8:动态切换头像

void UpdateAvatar(Image newAvatar)
{
    var old = btn.ImageBack;
    btn.ImageBack = newAvatar;

    // 释放旧图片(若不与其它地方共享)
    if (old != null && old != newAvatar)
        old.Dispose();
}



转载请注明出处。

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

返回列表

上一篇:CircularButtonA 纯色圆形按钮控件调用文档

没有最新的文章了...

相关文章

已经实现的一些算法

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