[C#]自定义控件:进度条(ProgressBar)

共 6849字,需浏览 14分钟

 ·

2020-11-06 16:19

一、前言

技术没有先进与落后,只有合适与不合适。

本篇的自定义控件是:进度条(ProgressBar)。

进度条的实现方式多种多样,主流的方式有:使用多张图片去实现、使用1个或2个Panel放到UserControl上去实现、重载系统进度条去实现等等。

本次所实现的进度条仍是使用GDI+去实现。当然,如果只是实现最基本的扁平化的进度条,那完全不需要再写本篇文章,因为直接小改下第一篇的LTrackBar就行了。

既然写这篇文章,就是实现不一样的、比较好玩和好看的进度条,如环形进度条、饼形进度条等等。

本篇使用的知识都是前几篇中已经讲过的,并没有新的技术。但是却附加了一些想像力,一些不拘一格、稍稍突破常规的想像力。

 

相信看完的你,一定会有所收获。

本文地址:https://www.cnblogs.com/lesliexin/p/13575517.html


 

二、前期分析

(一)为什么需要自定义进度条?

系统自带的滚动条样式太过单调,同时不够“扁平化”,所以便去实现自己的滚动条。

941750ff21d2ccd60a72d3301aa3d9fc.webp

 

(二)实现目标

1,支持三种样式

(1)横条(Bar)

 a2ae316bd8f13862417be7903652f0f4.webp

(2)圆饼(Pie)

 dad0be2377bddf0eb8d090ee69a554d7.webp

(3)圆弧(Arc)

 18eae7fe8600c20aff6c0faafb979c8f.webp

 

2,支持显示和隐藏百分比

(1)横条(Bar)

 b0d001fe924fa905a8409a939ba5c65b.webp

(2)圆饼(Pie)

 6441c7b2947881bfec5e021b8696c9d5.webp

(3)圆弧(Arc)

 09e1cd784ca76c458e956a52638c0566.webp

 

3,支持多种进度形状

(1)连续

 51c10c4460d8af6e0b25bfc0f0cd2278.webp

(2)分段块

 3a68d6922c4e247ef26cb43066f6a362.webp

(3)两段式:先分段块再连续

 48fe2d7859e8084ac27d8ea41dda9bc9.webp

(4)螺旋

(注:只有“横条”样式时才支持螺旋形状)

 26aab097a638712e7eb6dbe02c6c57b8.webp

 

4,支持Marquee样式

当进度无法确定时,便需要使用Marquee样式,同系统进度条的“Marquee”样式类似。

不过支持更多的Marquee样式。

(1)左右摆动

 07b3726a46f0b11f75df467199552a12.webp

(2)循环穿过

 44fdf5b5a73c9e0f884004efd71c35c4.webp

(3)往复(连续)

 b14007299c06d08f9033bde27e228528.webp

(4)划过(连续)

 53f1b22d9ae3733aa63fce7ed365cac2.webp

(5)往复(分段块)

 d21323226ce682fc83a477ccec564ee0.webp

(6)划过(分段块)

 954cb07ad7039e264b7aa0528db7bd69.webp

(7)螺旋

 4cb9e8b1ac0753e88dd315b3d58f2db5.webp

 

5,支持调整进度条各元素尺寸和颜色

(1)设置边框厚度和颜色

 f3d1e92c7c701a45575724362c5b9d01.webp

(2)设置背景大小和颜色

 5b6ace36da5551e8be1fe8aa4d489681.webp

(3)设置进度绘制位置和颜色

 565431222db8b869bf543d22bf85f51d.webp

(4)设置进度文本颜色

 277f44d95e8f3310025c7e19f3bd1a04.webp

(5)设置弧线厚度(仅样式为“圆弧(Arc)”时有效)

 50239346ca18e56d1bea647c3aee2fb5.webp

(6)设置“分段块”厚度(仅进度条形状为“分段块”时有效)

 0e30617143e9386c9def33fb94d1d976.webp

 


 

三、进度条拆解

看了上面的实现目标,是不是感觉眼花缭乱、无从下手?

下面我就对进度条进行拆分讲解,一步一步来看上面的效果是怎么实现的。

 

(一)组成元素

进度条由三个部分组成,分别是:进度边框、背景

下面是各种样式下,三个组成部分的拆分。

1,横条(Bar)

 5737586d6fc6aaadab8714074de2a548.webp

2,圆饼(Pie)

 bbe18955c5d5792b5f4d3b1759a1e005.webp

3,圆弧(Arc)

 ed2917aaca72194d99164e9ad08638c7.webp

 

(二)元素属性

进度条的三个组成元素,都有着各自相关的属性。

(注:因为“颜色”属性是必备的,所以不再赘述。)

1,边框

其相关属性为:边框的厚度

当“厚度”为0时,看起来进度条就没有边框;

当“厚度”超过控件高度的二分之一时,整个进度条就变成了边框的颜色。

(注:因为是先绘制“背景”,再绘制“边框”,所以是“整个进度条变成边框的颜色”)

A,对于 “橫条(Bar)”,其边框厚度如下图所示:

abf4eff4f5688b2a641c3d5a633f9886.webp

B,对于“圆饼(Pie)”、“圆弧(Arc)”,其边框厚度如下图所示:

44d91149e584aa67867ccc0e93f1ef8e.webp

2,背景

其相关属性为:背景的大小

背景的大小就是背景绘制的范围,这里用一个自造词“收缩宽度”来进行描述。

当“收缩宽度”为0时,背景大小=控件本身大小;

当“收缩宽度”为x时,背景宽度=控件宽度-2*x,背景高度=控件高度-2*x;

当“收缩宽度”超过控件高度的二分之一时,背景高度=控件高度-2*(控件高度/2)=0,此时进度条将没有了背景。

A,对于 “橫条(Bar)”,其收缩宽度如下图所示:

4dd44da519b79b0c79b8a20eb0b7e5f8.webp

B,对于“圆饼(Pie)”、“圆弧(Arc)”,其收缩宽度如下图所示:

2ffeef7a1b01898296befaa516bad6f9.webp

3,进度

其相关属性有:进度绘制范围、进度样式

A,进度绘制范围

顾名思义,就是进度可绘制的范围,这里用“绘制边框距离”来进行描述。

当“绘制边框距离”为0时,进度的绘制范围=控件本身大小;

当“绘制边框距离”为x时,进度的绘制起点:(x,x),绘制终点:(控件宽度-x,控件高度-x),所以绘制范围=(控件宽度-2*x,控件高度-2*x);

当“绘制边框距离”超过控件高度的二分之一时,进度的绘制高度将为0,也代表着没有进度。

A,对于 “橫条(Bar)”,其绘制边框距离如下图所示:

c70efa0e7ad9660206416a010212d000.webp

B,对于“圆饼(Pie)”,其绘制边框距离如下图所示:

6c03b0c127f3ef431303f9563c582f24.webp

C,对于“圆弧(Arc)”,其绘制边框距离如下图所示:

f606d607cffd990eef4b681169571c97.webp

 

B,进度样式

进度样式就是前节实现目标中各种进度的样式,有连续的、有分段块的、有螺旋的等等。

这此样式大部分一看都知道如何实现的。

比如“连续”,在“橫条(Bar)”样式中,就是填充一个矩形;在“圆饼(Pie)”中,就是填充一个扇形;在“圆弧(Arc)”中,就是画一段弧线。

比如“分段块”,在“橫条(Bar)”样式中,就是等间隔填充多个矩形;在“圆饼(Pie)”中,就是等间隔填充一个扇形;在“圆弧(Arc)”中,就是等间隔画一段弧线。

在这里,我将详细讲解一个特殊的形状:“螺旋”,因为“螺旋”样式除了不太能直观想像出来之外,还有不少细节需要处理。

因为只有“橫条(Bar)”样式进度条有“螺旋”样式,所以接下来的讲解都是以“橫条(Bar)”为基础进行讲解的。

同时,为了避免干扰,前面的两个属性“边框”和“背景”都将保持默认值,即:没有边框,背景大小=控件大小。“进度绘制范围”也是默认值,即绘制范围=控件大小。

(1)“螺旋”样式实现讲解

A,进度明确时

即进度是已知且确定的,比如5%,33%等等。其外观样式如下:

 66237fd489ec2d06b3a18de88e681f5d.webp

其示意图如下:

 a10c338218e6e811c74d389d29c7c7a0.webp

其中蓝色的平行四边形就是“螺旋”,其本身是一个背景透明、上有多个蓝色平行四边形的图片。

进度的增减实质上就是这个图片在控件上的左右移动。

那么,这个图片要和控件大小相等,特别是宽度相等。但是在使用GDI+去生成这个图片时,却不能让图片宽度与控件宽度相等。

我们在往图片绘制平行四边行时,是从右往左依次绘制的,之所以从右侧开始绘制,是为了保证进度条的右侧样式是固定的,固定在一个比较美观的状态。因为在进度变化时,即图片左右移动时,我们目光的焦点是在右侧,所以一个固定的右侧样式就比较重要,否则当控件宽度变化时,右侧的样式就随之变化。

当图片宽度与控件宽度相等时,会出现下面这种情况,即进度条的最右侧下方有空白。如下图所示:

 (其中上面图形是实际绘制图片,下面图形是将图片截取,和进度条宽度相等后样式)

c73545d2bf6f775e80539ce27946a519.webp

所以我们令图片的宽度=控件的宽度+1个平行四边行的宽度。在绘制完图片后,我们从中截取出一个和控件宽度一样的图片,这样,整体的样式就比较好看。如下图所示:
 (其中上面图形是实际绘制图片,下面图形是将图片截取,和进度条宽度相等后样式)

3a5d5b6e3ca832d939685994a5da454b.webp 

综上,就是按照下图所示的5步依次实现:

 06ad5a9707b6bedd84c41ee872ac765e.webp

 

B,进度未知时

即进度是不确定的,就像系统进度条的Marquee样式那样。其外观样式如下:

 4cb9e8b1ac0753e88dd315b3d58f2db5.webp

同上,上方仍是一个背景透明、上有多个蓝色平行四边形的图片。上图的效果就是这个图片在不段的循环移动。

具体示意如下,图片不断向右侧移动,当右侧超过一个平等四边行时,图片恢复原位,然后不断循环。

 869dc1ef9e4802ad1b314b2eb117f5e8.webp

通过上方的示意图,我们发现一个特点,就是图片的宽度不再等于(控件的宽度+1个平行四边行的宽度),而是等于(控件的宽度+2个平行四边行的宽度)。原因如下:

在画示意图时,为了方便直观查看,平行四边形正好是两个相对的直角三角行,而实际绘制时,很少会有这种样式,大多都是两个相对的钝角三角行组成的平等四边形的样子,如下图所示:

 40272cf544c2237f2a41b62178517b07.webp

这种情况下,如果图片的宽度=控件的宽度+1个平行四边行的宽度,那么在移动到最右侧时和复位时都会出现额外的空白,如下图所示:

 c299c9ebbc97781c4f12eddbff781d6a.webp

所以,才会令:图片的宽度=控件的宽度+2个平行四边行的宽度。

 

(三)其它属性

除了前面的与进度条元素直接相关的属性外,还有一些其他属性,这些属性都是在某种特定样式下才起有作用。

1,弧线宽度

在“圆弧(Arc)”样式的进度条中,“进度”就是一段圆弧,所以在其他属性外,还要有“弧线本身宽度”这样一个属性。

在默认情况下,弧线宽度=控件宽度/10,因为当进度达到100%时,弧线就变成了圆环,此时看起来有弧线的地方占控件宽度的五分之一,是一个比较常规的宽度。

通过调用弧线宽度,可以实现一些特殊的效果。

fbd04149f8393ab043b9b63ad102962b.webp 

 

2,分段块宽度

在进度样式为“分段块”时,为了支持不同的分段宽度,所以要有本属性。

为了绘制出更好的效果,“分段块宽度”不仅仅是有颜色的那部分的宽度,而是为:有颜色部分的宽度+两个颜色间隔。

经过调试,发现当(颜色:间隔=2:1)时,外观比较好看和自然。

所以,一个“分段块宽度”=2/3有颜色宽度+1/3无颜色宽度。

因为“橫条(Bar)”、“圆饼(Pie)”、“圆弧(Arc)”都支持“分段块”。所以在“橫条(Bar)”中,“分段块宽度”指的就是宽度;而在“圆饼(Pie)”、“圆弧(Arc)”中,“分段块宽度”指的是角度。

1d94cd1175d012a044808595f2dd972a.webp

 


 

四、开始实现

(一)前期准备。

此处仅作提纲,具体操作见前篇。

新建类:LProgressBar.cs

添加继承:Control(需要添加引用:System.Windows.Forms.dll)

修改可访问性为:public

(二)添加属性

1,进度值

进度值指的是当前的进度,这里将进度范围固定为:0~100

这样的好处是方便计算和绘制,使用时也更加直观。

6e9b60c8bcaeafd7568de1dc472d775f.webp

2,边框厚度

详细见前节中的讲解。

(注:我是先写完的代码,然后再写文章,有时在写文章时会发现属性的命名不太正规,比如“边框厚度”这个属性,用“BorderSize”更好,但是我在写代码时我想的是“环绕一圈的线”,所以就成了下面图中的“SurroundLineSize”,但是在写文章时发现不好描述,就改成了“边框厚度”,不过再去修改代码意义不大,重要的是技术思想本身。下同。)

a440e6366e669d5f217f4bda36896acc.webp

3,边框颜色

顾名思义,就是边框的颜色。

a616c5926b14d7f5397990b8fcdc707e.webp

4,背景收缩宽度

详细见前节中的讲解。

5c559821ef67fee68b5c04abc5b920e1.webp

5,背景颜色

118fdc1aec35c75a03a9f6c3126e87c2.webp

6,进度绘制范围边缘

详细见前节中的讲解。

ad1d759cb40a00709efe8ef876a5507c.webp

7,弧线厚度

详细见前节中的讲解。

7e793056830ca2c918fbf0b1d527c900.webp

8, 进度颜色

进度颜色是进度本身的颜色。

595d74eec31e85a92e3f0cd998c3d7d7.webp

9,分段块宽度

详细见前节中的讲解。

6e29665597f4145aa225aef79ea9a17d.webp

10,进度样式

进度样式描述的是进度本身的外观,如连续、分段块、螺旋等等。

d5bd94884e26a83866976c9ce3d84435.webp

进度样式是一个枚举,明细如下:

02914f8540067d25b6ea5cc73137169e.webp

11,进度条类型

进度条类型指的是进度条的样式外观,如横条、圆饼、圆弧等。

54deb4b98e8606383a4bf14a951705d0.webp

进度条类型是一个枚举,明细如下:

091d1b956d67f0a68e341b1f877d9cb9.webp

12,进度条模式

进度模式指的进度是明确的还是不明确的。

进度明确,比如当前进度是1%、45%等等。

进度不明确,比如加载某个文件时一直在加载,但不知道加载了多少。也就是系统进度条的Marquee样式。

fab522755df50c748725c5eae21613bc.webp

进度条模式是一个枚举,明细如下:

75b5698053ab28bd461a3576238b7a3f.webp

13,Marquee循环一遍时间

 Marquee样式时动画的快慢。

f0697e84f4c8b4c38d9140fec5f52fe1.webp 

14,Marquees样式

Marquee样式动画。

 a7ade5fb1fd1dd1b5e4b1a241ee27aca.webp

该属性是一个枚举,明细如下:

 038de18b1e766b71b5ff394c4d111127.webp

15,Marquee类型

Marquee样式同样是动画,所以支持匀速和缓入缓出效果。

 3cf4db5966d0670007cad7612d816636.webp

该属性是一个枚举,明细如下:

 de9cb94de92d1f9a9245daa3f1ac3690.webp

16,进度文本类型

可以设置进度条是否显示文本,以及是否显示百分比。

 793cf19b38e2dce05674f2efdf8d6750.webp

该属性是一个枚举,明细如下:

 9a44fb6f7d41d6d06dcddc9edc43bd01.webp

17,进度文本颜色

f830ad539d538c23cbdf144efc9271f7.webp

18,构造函数

这里的构造函数中,除了使用了之前文章中固定的“双缓冲”外,还额外设置了几个属性,这几个属性是为了让控件支持透明,所以下面才可以令背景色和前景色为透明色。

这种情况下,整个控件就相当于一个完全透明的画布,可以任由我们绘制。

168da9480455e40e353e1140cd80bc2a.webp

(三)重写方法

对于本进度条而言,只是用来指示进度信息,所以不需要事件。只需要重写最基本的OnPaint方法就行了。

不过,由于在OnPaint中要绘制的样式以及类型太多,所以代码量较大,但是代码难度不大。我会先将OnPaint代码全部放上,然后着重讲一下实现的原理。

而Marquee样式本质上就是一个动画效果,循环的绘制一些图形或改变图形的大小位置,所以我们使用一个定时器来进行触发,定时的改变图形或图形大小位置。 

8be819f081754db0c2881002f7bf00c8.webpOnPaint

8be819f081754db0c2881002f7bf00c8.webpTimer_Tick

 

画进度条,其实就是将进度条的三个组成部分——背景、边框、进度——分别绘制出来。

1,背景

(1)横条(Bar)

填充一个矩形,这里的矩形范围是控件范围减去“背景收缩宽度”后的范围。

(2)圆饼(Pie)和圆弧(Arc)

填充一个圆形,同样范围是控件范围减去“背景收缩宽度”后的范围。

2,边框

(1)横条(Bar)

绘制一个矩形框,画笔宽度就是边框宽度,注意绘制的范围,包含起点和宽高,宽高是要分别减去一半的边框宽度的,因为绘制画笔时,是从画笔宽度中间作画。

(2)圆饼(Pie)和圆弧(Arc)

绘制一个圆,画笔宽度就是边框宽度。同样需要注意绘制的范围,具体同上。

3,进度

这是实现时最复杂的地方,难度并不大,主要是要绘制的效果太多了。

在画进度时要分两种情况,一种是进度明确的,一种是进度不明确的(Marquee样式)。

在每种情况中,进度都有多种样式,比如连续、分段块等等。

(1),进度明确

1.1,连续

5d056b0930854f716c439c2f5105cf04.webp

(1)横条(Bar)

填充一个矩形,注意绘制的起点为属性“进度绘制范边缘”的值。而矩形宽度为当前进度值除以100再乘以可绘制范围。

简而言之就是按百分比绘制矩形。

(2)圆饼(Pie)

填充一个扇形,除了绘制范围起点外,需要注意的是起始角度。对于方法 FillPie 而言,以水平向右方向为0度。而我们实现的起始角度是在垂直向上方向,所以启始角度要为270。

同上,绘制的解雇就是按百分比计算:当前值/100*360。

(3)圆弧(Arc)

画一段弧线,同上需要注意启始角度要为270。同时还要注意的还有“弧线厚度”这一属性,所以计算绘制范围时要减去相应的值。

同上,绘制的解雇就是按百分比计算:当前值/100*360。

 

1.2,分段块

87a488e701484bd83c4e033fe722a6f6.webp

(1)横条(Bar)

 首先计算出当前可绘制的宽度范围,然后除以“分段块宽度”,计算出一共可画多少个“块”,这里需要注意“分段块宽度”是包含2/3的有颜色部分和1/3无颜色部分(充当间隔),在前一节中有详细讲解。

然后依次将这些“块”绘制即可。

(2)圆饼(Pie)

 同上,不过这里的“分段块宽度”指的是角度,同样计算出需要多少个“小扇形”,然后依次绘制即可。

(3)圆弧(Arc)

  同上,不过这里的“分段块宽度”指的是角度,同样计算出需要多少个“小段弧线”,然后依次绘制即可。

 

1.3,两段式

4d0992883b970123fd492b82039c7a1f.webp

通过上图我们可发现,两段式就是将“连续”和“分段块”结合起来,先绘制一遍“分段块”再绘制一遍“连续”。

这里需要注意的一点是每一遍都只占50%,所以在计算相应的范围时要乘以2。

因为“连续”和“分段块”前面都以讲过,此处便不再赘述。

 

1.4,螺旋

2009da731cacc48d76376f2684b09f0c.webp

螺旋只有“横条(Bar)”才支持,其本身是“分段块”的变形,所以会使用“分段块宽度”这一属性。

在前节进行过详细讲解,所以此处不再赘述。

 

2,进度不明确(Marquee样式)

虽然有多种Marquee样式,但归到最后无非三大类:位置变化、范围变化、螺旋。

其中,“螺旋”在前节中详细讲解过,所以此处不再赘述。

无论“位置变化”还是“范围变化”,都是通过定时器去按间隔循环改变相应的值。而“匀速”效果和“缓动”效果则是在每个间隔变化的值是否相等而已。

关于匀速和缓动我前几篇文章都有详细讲过,此处不再赘述。

 

(1)位置变化

fde17ef1c794f96f5d93b1e88c3dbdb9.webp

88481c926b8507ccd7a2ca46021e6ab6.webp

 

通过上面这些图,一目了然,位置变化,对于横条(Bar)而言,就是改变起始位置;对于圆饼(Pie)和圆弧(Arc)而言,则是改变起始角度。

  

(2)范围变化

fa26030cb49f623f3c3576d17d9b54e3.webp

6f75f3bd1fb205b4655184a86ac0e208.webp

通过上面这些图,一目了然,范围变化,对于横条(Bar)而言,就是使绘制范围发生变化,准确的说是宽度的变化;对于圆饼(Pie)和圆弧(Arc)而言,则是绘制角度的大小变化。

 

(3)位置和范围都发生变化

 

 974f6a63d5485c2fc23b9db9f87be1bb.webp

f55a741a7498246bf3d16d690948233b.webp

通过上面这些图,一目了然,属于前两者的结合体。

 

 (四)其它说明

在实现时,我并没有去限制圆饼(Pie)和圆弧(Arc)一定得是正圆,也就是令其宽度和高度必须相等。

因为这样可以实现一些比较好玩的效果,如下:

7ae55751fe9c11c270fbd884621ee16d.webp

 


 

五、效果演示

为了更好的展示出进度条的特点,所以我们按下图这样构造一个演示程序。

 04c6172d3a4e159ce45330073edc43c4.webp

 

本文中大部分图片和动图都是使用本演示程序录制的。

具体演示程序及源代码工程在文末有下载,请下载后自动体验。

daa97bbc265868ca29f759f28e578def.webp

 


 

 六、结束语

我们会发现Marquee样式的动画和我们常见的加载(Loading)动画有些类似,实际上,加载动画的实现方式正是本文中实现Marquee样式时所使用的方式,所以后面我们会去实现“加载(Loading)控件”。

 

本文只是起一个抛砖引玉的作用,读者不要被我的思路所限制,你可以尽情的去实现你想要的效果。

 

技术并没有先进和落后,只有合适与不合适。

所以,对自己掌握的知识多抱有一些信心,尽情释放自己的想像力,并在实践中提升自己。

 


 

七、源代码及工程下载

https://files.cnblogs.com/files/lesliexin/04,LProgressBar.7z

出处:https://www.cnblogs.com/lesliexin/p/13575517.html


浏览 59
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报