ProgressBarC 圆形进度条调用文档
命名空间:CConUI
继承:ProgressBarBase(自定义基类) → Control
可见性:public class ProgressBarC(可外部使用)
定位:圆形进度条(另一种风格)——以「从底部两侧对称向上展开」的方式显示进度,与 ProgressBarB(从 12 点顺时针扇形展开)视觉风格不同,两者 API 相同,可互换使用。
一、作用
在控件区域内绘制一个居中的圆形进度环,进度视觉是「从底部隆起,向两侧对称展开,逐渐扩张至整个圆」:
底层:填满的背景圆(
ColorBack);上层:进度填充区域(
ColorCore),从底部两侧向上生长;最外层:圆环边框(
BorderColor/BorderSize);中央可显示文本
Text(ForeColor/Font)。
视觉进度示意
| 进度 | 视觉效果 |
|---|---|
Value = Min 或 0 | 不绘制进度,只剩背景圆 + 边框 + 文字 |
| 0 < 进度 < 50% | 底部出现一个小小的"碗形/月牙"填充,随进度增高 |
| 进度 = 50% | 填充恰好填满下半圆(从底部到水平直径) |
| 50% < 进度 < 100% | "碗形"继续向上隆起,弦从水平直径向上抬升 |
| 进度 = 100% | 填满整个圆 |
与
ProgressBarB的区别:ProgressBarB是「从 12 点顺时针扫过扇形」,ProgressBarC是「从底部向两侧对称展开」。API 相同,可根据 UI 风格需要二选一。
二、快速上手
using System.Drawing;
using System.Windows.Forms;
using CConUI;
// 1. 创建控件
var bar = new ProgressBarC();
// 2. 放入容器(建议尺寸接近正方形)
bar.Parent = panel1;
bar.Size = new Size(120, 120);
bar.Location = new Point(20, 20);
// 3. 设置范围与当前值
bar.Min = 0;
bar.Max = 100;
bar.Value = 45;
bar.Text = "45%";
// 4. 外观
bar.ColorBack = Color.FromArgb(45, 45, 48);
bar.ColorCore = Color.FromArgb(0, 200, 120);
bar.BorderColor = Color.FromArgb(30, 30, 30);
bar.BorderSize = 4;
bar.ForeColor = Color.White;
bar.Font = new Font("Microsoft YaHei", 10F, FontStyle.Bold);三、构造函数
public ProgressBarC()
| 项目 | 说明 |
|---|---|
| 参数 | 无(本类未定义构造函数,使用 C# 隐式默认构造函数) |
| 实际行为 | 调用 ProgressBarBase 的无参构造函数进行初始化 |
| 默认尺寸 | 以 ProgressBarBase 为准;使用本控件时建议手动设置接近正方形的 Size(如 100×100、120×120),否则圆形会被压缩成椭圆 |
var bar = new ProgressBarC
{
Parent = panel1,
Size = new Size(120, 120)
};四、公开成员
4.1 本类新增/重写的成员
| 成员 | 类型 | 可见性 | 说明 |
|---|---|---|---|
OnPaint(PaintEventArgs e) | 方法(override) | protected override | 重写绘制逻辑,改为圆形进度绘制。外部无法直接调用,仅在控件需要重绘时由 WinForms 自动调用 |
ProgressBarC本身没有新增 public 属性、方法或事件,它的所有对外接口完全继承自ProgressBarBase。
4.2 继承自 ProgressBarBase 的成员(由本类绘制逻辑推断)
| 成员 | 类型(推断) | 在本类中的作用 | 用法示例 |
|---|---|---|---|
Min | int | 进度下限;小于此值的 Value 会按 Min 显示 | bar.Min = 0; |
Max | int | 进度上限;进度映射的分母。注意为 0 时不绘制进度 | bar.Max = 100; |
Value | int | 当前进度;决定填充区的高度 | bar.Value = 45; |
ColorBack | Color | 背景圆的填充色 | bar.ColorBack = Color.Gray; |
ColorCore | Color | 进度填充区的颜色 | bar.ColorCore = Color.Lime; |
BorderColor | Color | 圆环边框颜色 | bar.BorderColor = Color.Black; |
BorderSize | int | 圆环边框粗细(像素) | bar.BorderSize = 4; |
事件(如
Change/ValueChanged)与其它方法请参考ProgressBarBase源码。
4.3 绘制逻辑所使用到的 Control 继承成员
| 成员 | 来源 | 说明 |
|---|---|---|
Text | Control | 控件文本;本类在圆形中央居中绘制 |
Font | Control | 文本字体 |
ForeColor | Control | 文本颜色 |
ClientSize | Control | 用于计算圆的直径和位置 |
Invalidate() | Control | 属性变化后触发重绘 |
五、公开方法
ProgressBarC没有定义任何 public 方法,全部公开方法由ProgressBarBase与Control继承。若
ProgressBarBase中定义了类似LoadStart()/LoadPause()/LoadEnd()等「等待特效」方法,在ProgressBarC中不会被重写——ProgressBarC只重写了OnPaint,所以调用这些方法仍然有效,但由于绘制逻辑已被替换为「圆形进度」,等待特效的视觉效果不会体现在ProgressBarC的绘制里(除非ProgressBarBase.OnPaint里本身有相关逻辑被base.OnPaint(e)调用——实际上ProgressBarC第一行就调用了base.OnPaint(e),所以基类的绘制会被执行,但ProgressBarC会在其之上再次绘制圆形,可能造成视觉重叠)。
六、绘制原理(深入理解)
以下分析帮助你在调整尺寸、边框、颜色、进度范围时理解
ProgressBarC的具体表现。
6.1 计算外圆与内圆
float side = Math.Min(ClientSize.Width, ClientSize.Height); if (side
| 要点 | 说明 |
|---|---|
| 正方形判断 | 以 min(宽, 高) 作为直径,保证在非正方形控件中也是正圆(多出的空间留白) |
d = side - 1f | 减 1 像素:GDI+ 抗锯齿模式下,若直径等于 side,最右/最下边缘会被裁掉半像素。减 1 让外圆完整落在控件内 |
ox / oy | 让外接正方形在客户区内居中 |
side ≤ 4 | 太小不绘制,避免异常半径 |
border 的上限 d/2 - 1 | 边框半径不能超过圆半径,否则内圆消失。减 1 是安全余量。 负值兜底为 0( Math.Max(0, ...)) |
inner 计算 | 内圆 = 外圆向内缩 border 像素。所有进度填充都画在 inner 内,边框最后画在外圆上覆盖边缘 |
inner.Width ≤ 0 检查 | 如果边框异常粗导致内圆为空,直接返回,避免绘制错误 |
6.2 绘制背景圆
using (var brush = new SolidBrush(this.ColorBack)) g.FillPie(brush, inner.X, inner.Y, inner.Width, inner.Height, -90f, 360f);
| 要点 | 说明 |
|---|---|
用 FillPie 而不是 FillEllipse | 为了让背景圆与进度扇形走同一条 GDI+ 路径族,两者弧线像素级重合,避免露出 1px 背景缝 |
-90f, 360f | 从 12 点方向开始,扫过 360°,即完整圆。GDI+ 角度单位是度,0° 在 3 点方向,顺时针为正 |
6.3 绘制进度填充(核心:半圆对称展开)
int v = this.Value > this.Min ? this.Value : this.Min;
if (this.Max > 0 && v > 0)
{
float a = 180f / this.Max * v; // 0~Max 映射到 0~180°
if (a > 0f)
{
using (var path = new GraphicsPath())
{
path.AddArc(inner, 90f, a); // 从正下方顺时针画 a
path.AddArc(inner, 90f - a, a); // 从对称位置顺时针画 a
using (var brush = new SolidBrush(this.ColorCore))
g.FillPath(brush, path);
}
}
}| 要点 | 说明 |
|---|---|
v 下限保护 | Math.Max(Value, Min):小于 Min 时按 Min 处理,保证不会出现「比下限还低」的进度 |
Max > 0 保护 | 避免 Max = 0 时除以 0 得到 NaN/Infinity |
v > 0 保护 | 如果 Min < 0 且 Value <= Min,v 可能为负数或 0,直接跳过(避免画反) |
a = 180f / Max * v | 核心映射公式:0 ~ Max 线性映射到 0° ~ 180°。注意:分母是 Max 而不是 (Max - Min),因此当 Min > 0 时,Value = Min 不会得到 0°,而是 180° × Min / Max。若需「Min 时刻 0 进度」的严格语义,请自行保证 Min = 0,或在外部把 Value / Min / Max 转成 0 ~ Max 的等价区间 |
path.AddArc(inner, 90f, a) | 从 90°(正下方)开始,顺时针画弧扫过 a 度:a=0 → 无弧;a=90 → 从底部到右侧(90°→180°);a=180 → 从底部绕左半到顶部 |
path.AddArc(inner, 90f - a, a) | 从对称位置(90° - a)开始,顺时针画弧扫过 a 度:a=90 → 从 0°(右侧)扫到 90°(底部);a=180 → 从 -90°(顶部)扫到 90°(底部) |
| 两段弧组合 | GraphicsPath 会把第一段弧的终点与第二段弧的起点自动用直线连接,形成封闭区域。这产生「底部圆弧 + 顶部弦线」的填充效果,即从底部对称向上的「碗形/月牙」 |
| 不同进度的表现 | a=45°(25%):底部小碗形。 a=90°(50%):弦恰好在水平直径处,填满下半圆。 a=135°(75%):弦向上抬起,填充超出下半圆。 a=180°(100%):两段弧端点重合,形成完整圆。 |
6.4 绘制文本
if (!string.IsNullOrEmpty(this.Text))
{
using (var brush = new SolidBrush(this.ForeColor))
using (var sf = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
})
g.DrawString(this.Text, this.Font, brush, outer, sf);
}| 要点 | 说明 |
|---|---|
绘制在 outer 内 | 即外接正方形,文字在圆的中心居中 |
Center / Center | 水平居中 + 垂直居中 |
6.5 绘制圆环边框
if (border > 0)
{
float half = border / 2f;
var penRect = new RectangleF(outer.X + half, outer.Y + half,
outer.Width - border,
outer.Height - border);
using (var pen = new Pen(this.BorderColor, border))
g.DrawEllipse(pen, penRect);
}| 要点 | 说明 |
|---|---|
手算内缩 half | GDI+ 的 Pen 默认居中描边:会以绘制路径为中轴,向内外各扩展 border/2。若直接 DrawEllipse(outer),一半边框画到外面。不使用 PenAlignment.Inset:在 DrawEllipse 上,PenAlignment 的行为在不同 GDI+ 版本中表现不一致(有时无效或部分无效),因此本类采用「手动内缩半个线宽」的稳妥方案 |
| 内缩后矩形 | X/Y 各 + half,W/H 各 - border(因为两侧各缩 half,合计 border) |
| 边框最后绘制 | 放在最后,可以覆盖进度填充在内外边界上可能出现的抗锯齿半像素溢出,保证边缘干净 |
七、公开成员速查表
| 成员 | 来源 | 类型 | 说明 |
|---|---|---|---|
ProgressBarC() | 本类(隐式) | 构造函数 | 默认无参构造,调用基类初始化 |
Min | ProgressBarBase | 属性(int) | 进度下限 |
Max | ProgressBarBase | 属性(int) | 进度上限(用于比例映射;为 0 时不绘制进度) |
Value | ProgressBarBase | 属性(int) | 当前进度 |
ColorBack | ProgressBarBase | 属性(Color) | 背景圆颜色 |
ColorCore | ProgressBarBase | 属性(Color) | 进度填充色 |
BorderColor | ProgressBarBase | 属性(Color) | 圆环颜色 |
BorderSize | ProgressBarBase | 属性(int) | 圆环粗细 |
Text | Control | 属性(string) | 圆中央文字 |
Font | Control | 属性(Font) | 文字字体 |
ForeColor | Control | 属性(Color) | 文字颜色 |
Size / ClientSize | Control | 属性(Size) | 建议设为正方形 |
Invalidate() | Control | 方法 | 触发重绘 |
OnPaint(e) | 本类(override) | 方法(protected) | 圆形绘制逻辑,外部不可直接调用 |
事件(如
Change/ValueChanged)与其它方法请参考ProgressBarBase源码。
八、完整使用示例
示例 1:基础圆形进度条
using System.Drawing;
using System.Windows.Forms;
using CConUI;
var bar = new ProgressBarC
{
Parent = panel1,
Size = new Size(120, 120),
Location = new Point(20, 20),
Min = 0,
Max = 100,
Value = 45,
Text = "45%",
ColorBack = Color.FromArgb(45, 45, 48),
ColorCore = Color.FromArgb(0, 200, 120),
BorderColor = Color.FromArgb(30, 30, 30),
BorderSize = 4,
ForeColor = Color.White,
Font = new Font("Microsoft YaHei", 10F, FontStyle.Bold)
};示例 2:配合计时器动态变化
var bar = new ProgressBarC
{
Parent = this,
Size = new Size(150, 150),
Min = 0,
Max = 100,
BorderSize = 6,
ColorBack = Color.FromArgb(50, 50, 50),
ColorCore = Color.OrangeRed,
BorderColor = Color.FromArgb(20, 20, 20),
Font = new Font("Segoe UI", 12F, FontStyle.Bold)
};
var timer = new Timer { Interval = 50 };
timer.Tick += (s, e) =>
{
if (bar.Value >= bar.Max)
{
timer.Stop();
bar.Text = "完成";
return;
}
bar.Value += 1;
bar.Text = bar.Value + "%";
bar.Invalidate(); // 若基类 Value 不自动刷新,需手动 Invalidate
};
timer.Start();示例 3:与 ProgressBarB 互换(同一套 API)
// 两者仅视觉风格不同,API 相同。如果要切换风格:
ProgressBarBase bar = someCondition
? new ProgressBarB { /* B 配置 */ }
: new ProgressBarC { /* C 配置 */ };
bar.Parent = this;
bar.Size = new Size(120, 120);
bar.Min = 0;
bar.Max = 100;
bar.Value = 60;
bar.ColorBack = Color.FromArgb(45, 45, 48);
bar.ColorCore = Color.DodgerBlue;
bar.BorderSize = 4;
bar.Text = "60%";示例 4:非正方形控件下的正圆(居中 + 留白)
// 即使控件是长方形,进度条仍会绘制一个居中的正圆
var bar = new ProgressBarC
{
Parent = this,
Size = new Size(240, 100), // 长方形,实际圆直径 = min(240, 100) = 100 - 1 = 99
BorderSize = 3,
ColorCore = Color.Lime,
BorderColor = Color.Black,
Text = "Loading",
Font = new Font("Consolas", 9F)
};转载请注明出处。