一、前言
技术没有先进与落后,只有合适与不合适。
本篇的自定义控件是:进度条(ProgressBar)。
进度条的实现方式多种多样,主流的方式有:使用多张图片去实现、使用1个或2个Panel放到UserControl上去实现、重载系统进度条去实现等等。
本次所实现的进度条仍是使用GDI+去实现。当然,如果只是实现最基本的扁平化的进度条,那完全不需要再写本篇文章,因为直接小改下第一篇的LTrackBar就行了。
既然写这篇文章,就是实现不一样的、比较好玩和好看的进度条,如环形进度条、饼形进度条等等。
本篇使用的知识都是前几篇中已经讲过的,并没有新的技术。但是却附加了一些想像力,一些不拘一格、稍稍突破常规的想像力。
二、前期分析
(一)为什么需要自定义进度条?
系统自带的滚动条样式太过单调,同时不够“扁平化”,所以便去实现自己的滚动条。
(二)实现目标
1,支持三种样式
(1)横条(Bar)
(2)圆饼(Pie)
(3)圆弧(Arc)
2,支持显示和隐藏百分比
(1)横条(Bar)
(2)圆饼(Pie)
(3)圆弧(Arc)
3,支持多种进度形状
(1)连续
(2)分段块
(3)两段式:先分段块再连续
(4)螺旋
(注:只有“横条”样式时才支持螺旋形状)
4,支持Marquee样式
当进度无法确定时,便需要使用Marquee样式,同系统进度条的“Marquee”样式类似。
不过支持更多的Marquee样式。
(1)左右摆动
(2)循环穿过
(3)往复(连续)
(4)划过(连续)
(5)往复(分段块)
(6)划过(分段块)
(7)螺旋
5,支持调整进度条各元素尺寸和颜色
(1)设置边框厚度和颜色
(2)设置背景大小和颜色
(3)设置进度绘制位置和颜色
(4)设置进度文本颜色
(5)设置弧线厚度(仅样式为“圆弧(Arc)”时有效)
(6)设置“分段块”厚度(仅进度条形状为“分段块”时有效)
三、进度条拆解
看了上面的实现目标,是不是感觉眼花缭乱、无从下手?
下面我就对进度条进行拆分讲解,一步一步来看上面的效果是怎么实现的。
(一)组成元素
进度条由三个部分组成,分别是:进度、边框、背景。
下面是各种样式下,三个组成部分的拆分。
1,横条(Bar)
2,圆饼(Pie)
3,圆弧(Arc)
(二)元素属性
进度条的三个组成元素,都有着各自相关的属性。
(注:因为“颜色”属性是必备的,所以不再赘述。)
1,边框
其相关属性为:边框的厚度。
当“厚度”为0时,看起来进度条就没有边框;
当“厚度”超过控件高度的二分之一时,整个进度条就变成了边框的颜色。
(注:因为是先绘制“背景”,再绘制“边框”,所以是“整个进度条变成边框的颜色”)
A,对于 “橫条(Bar)”,其边框厚度如下图所示:
B,对于“圆饼(Pie)”、“圆弧(Arc)”,其边框厚度如下图所示:
2,背景
其相关属性为:背景的大小
背景的大小就是背景绘制的范围,这里用一个自造词“收缩宽度”来进行描述。
当“收缩宽度”为0时,背景大小=控件本身大小;
当“收缩宽度”为x时,背景宽度=控件宽度-2*x,背景高度=控件高度-2*x;
当“收缩宽度”超过控件高度的二分之一时,背景高度=控件高度-2*(控件高度/2)=0,此时进度条将没有了背景。
A,对于 “橫条(Bar)”,其收缩宽度如下图所示:
B,对于“圆饼(Pie)”、“圆弧(Arc)”,其收缩宽度如下图所示:
3,进度
其相关属性有:进度绘制范围、进度样式
A,进度绘制范围
顾名思义,就是进度可绘制的范围,这里用“绘制边框距离”来进行描述。
当“绘制边框距离”为0时,进度的绘制范围=控件本身大小;
当“绘制边框距离”为x时,进度的绘制起点:(x,x),绘制终点:(控件宽度-x,控件高度-x),所以绘制范围=(控件宽度-2*x,控件高度-2*x);
当“绘制边框距离”超过控件高度的二分之一时,进度的绘制高度将为0,也代表着没有进度。
A,对于 “橫条(Bar)”,其绘制边框距离如下图所示:
B,对于“圆饼(Pie)”,其绘制边框距离如下图所示:
C,对于“圆弧(Arc)”,其绘制边框距离如下图所示:
B,进度样式
进度样式就是前节实现目标中各种进度的样式,有连续的、有分段块的、有螺旋的等等。
这此样式大部分一看都知道如何实现的。
比如“连续”,在“橫条(Bar)”样式中,就是填充一个矩形;在“圆饼(Pie)”中,就是填充一个扇形;在“圆弧(Arc)”中,就是画一段弧线。
比如“分段块”,在“橫条(Bar)”样式中,就是等间隔填充多个矩形;在“圆饼(Pie)”中,就是等间隔填充一个扇形;在“圆弧(Arc)”中,就是等间隔画一段弧线。
在这里,我将详细讲解一个特殊的形状:“螺旋”,因为“螺旋”样式除了不太能直观想像出来之外,还有不少细节需要处理。
因为只有“橫条(Bar)”样式进度条有“螺旋”样式,所以接下来的讲解都是以“橫条(Bar)”为基础进行讲解的。
同时,为了避免干扰,前面的两个属性“边框”和“背景”都将保持默认值,即:没有边框,背景大小=控件大小。“进度绘制范围”也是默认值,即绘制范围=控件大小。
(1)“螺旋”样式实现讲解
A,进度明确时
即进度是已知且确定的,比如5%,33%等等。其外观样式如下:
其示意图如下:
其中蓝色的平行四边形就是“螺旋”,其本身是一个背景透明、上有多个蓝色平行四边形的图片。
进度的增减实质上就是这个图片在控件上的左右移动。
那么,这个图片要和控件大小相等,特别是宽度相等。但是在使用GDI+去生成这个图片时,却不能让图片宽度与控件宽度相等。
我们在往图片绘制平行四边行时,是从右往左依次绘制的,之所以从右侧开始绘制,是为了保证进度条的右侧样式是固定的,固定在一个比较美观的状态。因为在进度变化时,即图片左右移动时,我们目光的焦点是在右侧,所以一个固定的右侧样式就比较重要,否则当控件宽度变化时,右侧的样式就随之变化。
当图片宽度与控件宽度相等时,会出现下面这种情况,即进度条的最右侧下方有空白。如下图所示:
(其中上面图形是实际绘制图片,下面图形是将图片截取,和进度条宽度相等后样式)
所以我们令图片的宽度=控件的宽度+1个平行四边行的宽度。在绘制完图片后,我们从中截取出一个和控件宽度一样的图片,这样,整体的样式就比较好看。如下图所示:
(其中上面图形是实际绘制图片,下面图形是将图片截取,和进度条宽度相等后样式)
综上,就是按照下图所示的5步依次实现:
B,进度未知时
即进度是不确定的,就像系统进度条的Marquee样式那样。其外观样式如下:
同上,上方仍是一个背景透明、上有多个蓝色平行四边形的图片。上图的效果就是这个图片在不段的循环移动。
具体示意如下,图片不断向右侧移动,当右侧超过一个平等四边行时,图片恢复原位,然后不断循环。
通过上方的示意图,我们发现一个特点,就是图片的宽度不再等于(控件的宽度+1个平行四边行的宽度),而是等于(控件的宽度+2个平行四边行的宽度)。原因如下:
在画示意图时,为了方便直观查看,平行四边形正好是两个相对的直角三角行,而实际绘制时,很少会有这种样式,大多都是两个相对的钝角三角行组成的平等四边形的样子,如下图所示:
这种情况下,如果图片的宽度=控件的宽度+1个平行四边行的宽度,那么在移动到最右侧时和复位时都会出现额外的空白,如下图所示:
所以,才会令:图片的宽度=控件的宽度+2个平行四边行的宽度。
(三)其它属性
除了前面的与进度条元素直接相关的属性外,还有一些其他属性,这些属性都是在某种特定样式下才起有作用。
1,弧线宽度
在“圆弧(Arc)”样式的进度条中,“进度”就是一段圆弧,所以在其他属性外,还要有“弧线本身宽度”这样一个属性。
在默认情况下,弧线宽度=控件宽度/10,因为当进度达到100%时,弧线就变成了圆环,此时看起来有弧线的地方占控件宽度的五分之一,是一个比较常规的宽度。
通过调用弧线宽度,可以实现一些特殊的效果。