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

ProgressBarE 雷达加载进度条调用文档

作品墙13小时前


命名空间:CConUI
继承:ProgressBarBase(自定义基类) → Control
可见性:public class ProgressBarE(可外部使用)
定位:雷达加载动画——把圆环切成若干扇形“扇块”,用 Value 轮流点亮(或累积点亮),配合 Timer 循环推进,形成经典的“雷达扫描 / Loading”视觉。


一、作用

在控件区域内绘制一个居中的环形进度条,环被均分为 Max 个扇形块(块与块之间留 PieSpace 度空隙),用两种模式点亮:

  • 模式 One(单个闪烁):任一时刻只点亮 Value - 1 索引那一块(即 Value = 1 时点亮第 1 块,Value = 8 时点亮第 8 块),视觉像雷达指针在逐格扫描;

  • 模式 Two(累积点亮):从第 1 块点亮到第 Value 块,视觉像进度从 0 走到 100%;

  • 配合内部 Timer(默认 100ms/次),Value 在 0 ~ Max 之间循环递增(到顶后回到 0),实现“永不停止的加载动画”。

视觉示意

模式Value 序列(Max=8)视觉
ModeTwo.One0 → 1 → 2 → … → 8 → 0 → …Value=0 时全部块都是背景色(无点亮);Value=1~8 时第 Value-1 块点亮。视觉上是一个亮点绕圈,每轮有一次“全灭”瞬间
ModeTwo.Two0 → 1 → 2 → … → 8 → 0 → …Value=0 时全部灭;Value=N 时第 1~N 块点亮。满环瞬间后立刻重置为全灭

与 ProgressBarB / C / D 的区别:

  • ProgressBarB:扇形扫过(静态);

  • ProgressBarC:从底部展开(静态);

  • ProgressBarD:水位 + 波浪(动画);

  • ProgressBarE:环被切块、逐块点亮(动画),通常表示“无限循环加载中”,而不是“业务进度”。


二、快速上手

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

// 1. 创建控件
var bar = new ProgressBarE();

// 2. 放入容器(建议尺寸接近正方形)
bar.Parent = panel1;
bar.Size = new Size(120, 120);
bar.Location = new Point(20, 20);

// 3. 外观
bar.ColorBack = Color.FromArgb(60, 60, 64);       // 未点亮块的颜色
bar.ColorCore = Color.FromArgb(0, 200, 255);      // 点亮块的颜色
bar.BorderColor = Color.FromArgb(30, 30, 30);     // 外圆环
bar.BorderSize = 2;
bar.ForeColor = Color.White;
bar.Font = new Font("Microsoft YaHei", 10F);

// 4. 环形分块参数
bar.Max      = 8;          // ⭐ 用 Max 控制扇形数量(默认构造已是 8)
bar.PieSpace = 14;         // 块与块之间隔 14 度
bar.PieWidth = 34;         // 环形厚度
bar.Interval = 100;        // 100ms 前进一步(最小 50)

// 5. 模式
bar.ModeTow = ModeTwo.One; // 单个闪烁

// 6. 【关键】手动启动动画
// ⚠ 本控件 OnHandleCreated 里的自动启动条件实际不会成立(见“注意事项”),
//   必须显式调用 Start() 才会开始转动
bar.Start();

三、构造函数与生命周期

public ProgressBarE()

项目说明
参数无
实际行为① 调用 ProgressBarBase 无参构造;
② 设置 Min = 0、Max = 8;
③ 创建内部 Timer(Interval = 100),并挂接 Tick 处理
Timer 启动构造时不会启动;OnHandleCreated 的启动条件实际不成立(见下);必须显式 Start()
默认尺寸以 ProgressBarBase 为准;强烈建议手动设为接近正方形

生命周期方法(protected override,仅内部调用)

方法触发时机行为
OnHandleCreated(EventArgs e)控件句柄创建后若 this.Enabled() && !DesignMode 则 _timer.Start()。
⚠ 实际不会启动:this.Enabled() 检查的是“timer 当前是否 Enabled”,而构造时 timer 是 Enabled = false,所以在 OnHandleCreated 时刻条件恒为 false
OnHandleDestroyed(EventArgs e)控件句柄销毁后停止 _timer
Dispose(bool disposing)控件释放时若 disposing,_timer?.Dispose()

结论:
- 控件不会自动开始动画(与 ProgressBarD 不同),需要显式调用 bar.Start();
- 控件销毁时自动停止 Timer,无需手动 Stop();
- 隐藏控件(Visible = false)不会停止 Timer——本类未重写 OnVisibleChanged,请自行 Stop()。


四、公开属性

4.1 本类新增的属性

public CConPlus.Enum.ModeTwo ModeTow

项目说明
类型CConPlus.Enum.ModeTwo(值:One / Two)
默认ModeTwo.One
CategoryCCon
DefaultValueModeTwo.One
作用绘制模式:
One = 只点亮 Value - 1 索引对应的那一块(单个闪烁);
Two = 点亮第 1 ~ Value 的所有块(累积点亮)
副作用赋值后 Invalidate()
备注属性名拼写为 ModeTow(少了一个 o),与枚举名 ModeTwo 不一致,是源码拼写问题;请使用 ModeTow 访问
bar.ModeTow = ModeTwo.One;   // 单块闪烁
bar.ModeTow = ModeTwo.Two;   // 累积点亮

public int PieWidth

项目说明
类型int(像素)
默认34
CategoryCCon
DefaultValue34
作用环形厚度(每块扇形的“径向宽度”)
取值范围Math.Max(1, value)——自动 clamp 到 ≥1
额外保护绘制时 penWidth = min(PieWidth, d/2 - 1),保证环厚不超过半径,避免内圆消失
副作用赋值后 Invalidate()
bar.PieWidth = 20;   // 细环
bar.PieWidth = 40;   // 粗环

public int PieSpace

项目说明
类型int(角度,单位:度)
默认14
CategoryCCon
DefaultValue14
作用相邻两块扇形之间的空隙角度
取值范围Math.Max(0, value)——允许 0(无缝)
副作用赋值后 Invalidate()
额外逻辑每块扇形实际角度 pieAngle = (360 - PieSpace × Max) / Max。
若 PieSpace × Max ≥ 360,则 pieAngle ≤ 0,代码里强制为 1,此时扇形会严重重叠或叠在起点处,视觉异常
bar.PieSpace = 0;    // 无缝圆环
bar.PieSpace = 14;   // 默认
bar.PieSpace = 20;   // 更宽的块间空隙

// ⚠ 请保证 PieSpace × Max < 360
// 如 Max = 8 时,PieSpace < 45

public int Interval

项目说明
类型int(毫秒)
默认100
CategoryCCon
DefaultValue100
作用Timer 每 Interval 毫秒 Tick 一次,Value 前进一格
取值范围Math.Max(50, value)——至少 50ms(旧版是 10ms,新版提高到 50ms)
副作用若 _timer != null,立即更新 Timer.Interval;不触发重绘(速度变化不影响当前帧)
bar.Interval = 60;    // 更快(下限 50)
bar.Interval = 200;   // 更慢
bar.Interval = 20;    // 会被 clamp 到 50

4.2 继承自 ProgressBarBase 的成员(由本类绘制逻辑推断)

成员类型(推断)在本类中的作用用法示例
Minint进度下限;构造时设为 0,绘制时不参与计算(只用于基类语义)bar.Min = 0;
Maxint既是进度上限,也是“扇形数量”。构造时设为 8。
本类不再提供 PieNum 属性,改块数就改 Max
bar.Max = 12;(改为 12 块)
Valueint当前激活块索引(0 ~ Max);决定点亮哪一块bar.Value = 3;
ColorBackColor未点亮块的环形底色bar.ColorBack = Color.Gray;
ColorCoreColor点亮块的颜色bar.ColorCore = Color.Lime;
BorderColorColor外圆环边框颜色bar.BorderColor = Color.Black;
BorderSizeint外圆环边框粗细(像素)bar.BorderSize = 2;

事件(如 Change / ValueChanged)请参考 ProgressBarBase 源码。
注意:本类 _timer_Tick 里直接调用 this.Value++ / this.Value = 0,因此如果基类 Value setter 会触发事件,则每 Interval 毫秒都会触发一次——订阅时注意性能。

4.3 绘制逻辑所使用到的 Control 继承成员

成员来源说明
TextControl圆中央文字
FontControl文字字体
ForeColorControl文字颜色
ClientSizeControl用于计算圆的直径与居中
Invalidate()Control属性变化后触发重绘
DesignModeControl设计器模式下不启动 Timer

五、公开方法

public void Start()

项目说明
参数无
返回void
作用启动动画:设置 _timer.Interval = _interval 后 _timer.Start()
幂等若 _timer.Enabled == true,直接返回(不会重复设置 Interval)
典型场景必须显式调用(因为构造和 OnHandleCreated 都不会自动启动)
bar.Start();   // 开始转动

public void Stop()

项目说明
参数无
返回void
作用停止动画:_timer.Stop()
备注不会重置 Value;再次 Start() 会从当前位置继续
bar.Stop();    // 停止转动(Value 保留)

public new bool Enabled()

项目说明
参数无
返回bool:true 表示动画正在运行(Timer 已启动);false 表示已停止
作用查询动画是否运行中
⚠ 重要这是 new 隐藏了 Control.Enabled 属性的方法——两者名字相同,但语义完全不同:
bar.Enabled(属性)→ 控件是否可用(可用于禁用整个控件);
bar.Enabled()(方法)→ 动画是否正在运行。
这种写法极易混淆,编写代码时务必留意有没有括号
if (bar.Enabled())       // 调用方法 → 动画是否运行中
    Console.WriteLine("动画运行中");

if (bar.Enabled)         // 访问属性 → 控件是否启用
    bar.Start();

六、公开成员速查表

成员来源类型说明
ProgressBarE()本类构造函数初始化 Min=0、Max=8、创建 Timer(100ms)
ModeTow本类属性(ModeTwo)单块闪烁 / 累积点亮(默认 One)
PieWidth本类属性(int)环形厚度(默认 34,≥1)
PieSpace本类属性(int)块间角度(默认 14,≥0)
Interval本类属性(int)帧间隔毫秒(默认 100,≥50)
MinProgressBarBase属性(int)范围下限(构造时设 0)
MaxProgressBarBase属性(int)范围上限 + 扇形数量(构造时设 8)
ValueProgressBarBase属性(int)当前激活块索引(0 ~ Max)
ColorBack / ColorCore / BorderColor / BorderSizeProgressBarBase属性环形外观
Text / Font / ForeColorControl属性中央文字
Size / ClientSizeControl属性(Size)建议设为正方形
Start()本类方法启动动画
Stop()本类方法停止动画
Enabled()本类(new)方法查询动画是否运行中
Invalidate()Control方法触发重绘
OnPaint(e)本类(override)方法(protected)环形绘制,外部不可直接调用
OnHandleCreated / OnHandleDestroyed / Dispose本类(override)方法(protected)生命周期管理 Timer

事件(如 Change / ValueChanged)请参考 ProgressBarBase 源码。


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

以下分析帮你在调整块数、间隔、环厚、尺寸时理解 ProgressBarE 的具体表现。

7.1 外圆与环参数

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

float d = side - 1f;                          // 减 1 避免抗锯齿被裁
float ox = (ClientSize.Width - d) / 2f;
float oy = (ClientSize.Height - d) / 2f;
var outer = new RectangleF(ox, oy, d, d);

float penWidth = Math.Min(_pieWidth, d / 2f - 1f);   // 环厚上限 = 半径 - 1
if (penWidth <= 0) return;

var arcRect = RectangleF.Inflate(outer, -penWidth / 2f, -penWidth / 2f);
要点说明
d = side - 1fGDI+ 抗锯齿下直径等于 side 时最右/下边缘会被裁半像素,减 1 保证完整
penWidth 上限 d/2 - 1Pen 是居中描边,半径最多为 d/2;若 penWidth = d/2,内圆半径为 0(整块实心),不美观。限制到 d/2 - 1 保留内圆
arcRect = outer 内缩 penWidth/2因 Pen 居中描边,其外边缘会超出路径 penWidth/2。内缩 penWidth/2 后,外边缘正好贴到 outer 上,不会超出控件

7.2 扇形数量与角度

int pieNum = this.Max;
if (pieNum <= 0) return;

float pieAngle = (360f - _pieSpace * pieNum) / pieNum;
if (pieAngle <= 0) pieAngle = 1f;
要点说明
pieNum = Max新版的块数直接取自 Max;不再有独立的 PieNum 属性。改块数 = 改 Max
Max ≤ 0 保护若用户误将 Max 设为 0 或负数,直接返回(不绘制),避免除零
360 - PieSpace × Max整个环共 360°。总空隙 = 每块空隙 × 块数;剩余角度平分给每块扇形
示例(默认)Max=8,PieSpace=14 ⇒ pieAngle = (360 - 14×8) / 8 = (360-112)/8 = 31°。
每块 31°,中间隔 14°,8 块共 (31+14)×8 = 360°,完美铺满
pieAngle ≤ 0 强制 1若 PieSpace × Max ≥ 360(空隙太多或块数太多),理论 pieAngle 会 ≤0。代码兜底为 1°,但此时所有扇形会重叠在同一个起点附近,视觉混乱。
请保证 PieSpace × Max < 360

7.3 绘制背景块

using (var p = new Pen(this.ColorBack, penWidth) { StartCap = LineCap.Flat, EndCap = LineCap.Flat })
{
    for (int i = 0; i < pieNum; i++)
    {
        float startAngle = -90f + _pieSpace / 2f + i * (pieAngle + _pieSpace);
        g.DrawArc(p, arcRect, startAngle, pieAngle);
    }
}
要点说明
StartCap / EndCap = Flat平头端点,让扇形两端是直线切口,不会画成圆头/方头。视觉上每块是一个“梯形环块”
-90f12 点方向为起点(GDI+ 0° 在 3 点,顺时针正)
+ _pieSpace / 2f第一块与 12 点之间留半个空隙,使整圈视觉对称
i * (pieAngle + _pieSpace)第 i 块的起始角 = 起点 + i×(块角度 + 空隙角度)
循环 i < pieNum先画全部 Max 个未激活块(用 ColorBack)

7.4 绘制激活块

if (_modeTow == ModeTwo.One)
{
    int index = this.Value - 1;
    if (index >= 0 && index < pieNum)
    {
        float startAngle = -90f + _pieSpace / 2f + index * (pieAngle + _pieSpace);
        g.DrawArc(p, arcRect, startAngle, pieAngle);
    }
}
else if (_modeTow == ModeTwo.Two)
{
    int count = Math.Min(this.Value, pieNum);
    for (int i = 0; i < count; i++)
    {
        float startAngle = -90f + _pieSpace / 2f + i * (pieAngle + _pieSpace);
        g.DrawArc(p, arcRect, startAngle, pieAngle);
    }
}
模式行为说明
ModeTwo.One只画 Value-1 索引的那一块当 Value = 0 时,index = -1,不点亮任何块;
当 Value = Max 时,index = Max-1,点亮最后一块;
因此一轮循环 0 → Max → 0 里,有一帧是“全灭”
ModeTwo.Two画索引 0 ~ Value-1 的所有块当 Value = 0 时,count = 0,全灭;
当 Value = Max 时,count = Max,满环;
到顶后回到 0,视觉上会从“满环”跳变到“全灭”
覆盖顺序先画 ColorBack 全块 → 再画 ColorCore 激活块激活块覆盖在背景块上,颜色完全替换(无渐变/混合)

7.5 绘制文字与边框

if (!string.IsNullOrEmpty(this.Text))
{
    using (var b = new SolidBrush(this.ForeColor))
    using (var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
        g.DrawString(this.Text, this.Font, b, outer, sf);
}

int border = Math.Min(this.BorderSize, (int)(d / 2) - 1);
if (border > 0)
{
    float half = border / 2f;
    var penRect = RectangleF.Inflate(outer, -half, -half);
    using (var p = new Pen(this.BorderColor, border))
        g.DrawEllipse(p, penRect);
}
要点说明
文字绘制在 outer 内文字在圆的正中心居中
border = min(BorderSize, d/2 - 1)边框半径不超过圆半径
penRect = outer 内缩 half同 arcRect 逻辑:因 Pen 居中描边,内缩一半线宽让边框外边缘贴合 outer
边框最后画覆盖在环形之上,视觉上把环“包”在圆内

7.6 Value 循环逻辑(与旧版的关键差异)

private void _timer_Tick(object sender, EventArgs e)
{
    if (this.Value + 1 > this.Max)
        this.Value = 0;      // ⚠ 新版:到顶回到 0(旧版是回到 1)
    else
        this.Value++;        // 否则 +1
}
要点说明
循环范围Value 取值 0 ~ Max,共 Max + 1 个状态
到顶回到 0Value = Max 时下一 Tick 直接回 0,循环 0, 1, 2, …, Max, 0, 1, …
初始 Value构造时未显式设置 Value(基类默认值,通常为 0);第一次 Tick 会把 Value 从 0 推到 1(走 else 分支)
Value = 0 的视觉两种模式都不点亮任何块(One 模式 index=-1 越界;Two 模式 count=0)
因此每轮循环里有一帧“全灭”,视觉上是“点亮绕圈一圈后短暂熄灭再重来”
触发事件每次 Value = 赋值都会走基类 setter;若基类触发事件(如 Change),则每 Interval 毫秒触发一次,订阅时注意性能

八、完整使用示例

示例 1:基础雷达加载

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

var bar = new ProgressBarE
{
    Parent = panel1,
    Size = new Size(120, 120),
    Location = new Point(20, 20),
    Max = 8,              // 8 块
    PieSpace = 14,
    PieWidth = 34,
    Interval = 100,
    ModeTow = ModeTwo.One,
    ColorBack = Color.FromArgb(60, 60, 64),
    ColorCore = Color.FromArgb(0, 200, 255),
    BorderColor = Color.FromArgb(30, 30, 30),
    BorderSize = 2,
    ForeColor = Color.White,
    Font = new Font("Microsoft YaHei", 10F)
};

bar.Start();   // ⚠ 必须显式启动

示例 2:累积点亮(进度型)

var bar = new ProgressBarE
{
    Parent = this,
    Size = new Size(140, 140),
    Max = 20,              // 20 块
    PieSpace = 4,
    PieWidth = 20,
    Interval = 80,
    ModeTow = ModeTwo.Two,      // 累积点亮
    ColorBack = Color.FromArgb(240, 240, 240),
    ColorCore = Color.DodgerBlue,
    BorderColor = Color.LightGray,
    BorderSize = 2,
    Text = "加载中"
};

bar.Start();

示例 3:可暂停 / 恢复的加载指示

var bar = new ProgressBarE { Parent = this, Size = new Size(100, 100), Max = 12, PieSpace = 10 };
bar.Start();

btnPause.Click += (s, e) =>
{
    if (bar.Enabled())   // 调用方法:动画是否运行
    {
        bar.Stop();
        btnPause.Text = "继续";
    }
    else
    {
        bar.Start();
        btnPause.Text = "暂停";
    }
};

示例 4:根据业务进度提前停止动画

var bar = new ProgressBarE
{
    Parent = this,
    Size = new Size(120, 120),
    Max = 8,
    PieSpace = 14,
    ModeTow = ModeTwo.Two
};

bar.Start();

// 模拟业务完成后停止
var stopTimer = new Timer { Interval = 5000 };
stopTimer.Tick += (s, e) =>
{
    stopTimer.Stop();
    bar.Stop();

    // 设置为满进度(可选)
    bar.Value = bar.Max;

    // 显示完成文字
    bar.Text = "完成";
    bar.Invalidate();
};
stopTimer.Start();

示例 5:无缝圆环(PieSpace = 0)

// PieSpace=0 时,块与块之间无空隙,视觉上是一条完整的环
// 适合想做“跑马灯”效果但不需要可见间隔的场景
var bar = new ProgressBarE
{
    Parent = this,
    Size = new Size(100, 100),
    Max = 60,             // 60 块,每块 6°
    PieSpace = 0,
    PieWidth = 12,
    Interval = 50,
    ModeTow = ModeTwo.One, // 单块扫描
    ColorBack = Color.FromArgb(50, 50, 50),
    ColorCore = Color.Lime
};

bar.Start();

示例 6:中英文百分比显示(外部计算 Value)

var bar = new ProgressBarE
{
    Parent = this,
    Size = new Size(120, 120),
    Max = 100,             // 100 块,Value 恰好等价于百分比
    PieSpace = 1,          // 每块 1°,间隔 1°,共 200°?注意!
    // ⚠ PieSpace * Max = 1 * 100 = 100 < 360,OK
    PieWidth = 8,
    Interval = 100,
    ModeTow = ModeTwo.Two,
    ColorCore = Color.Orange
};

// 外部监听 Value 更新文字(假设基类触发 Change 事件)
bar.Change += (s, e) =>
{
    bar.Text = bar.Value + "%";
};

bar.Start();



转载请注明出处。

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

相关文章

已经实现的一些算法

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