zoukankan      html  css  js  c++  java
  • [C#] (原创)一步一步教你自定义控件——04,ProgressBar(进度条)

    一、前言

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

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

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

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

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

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

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

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


    二、前期分析

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

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

    (二)实现目标

    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%时,弧线就变成了圆环,此时看起来有弧线的地方占控件宽度的五分之一,是一个比较常规的宽度。

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

     

    2,分段块宽度

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

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

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

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

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


    四、开始实现

    (一)前期准备。

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

    新建类:LProgressBar.cs

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

    修改可访问性为:public

    (二)添加属性

    1,进度值

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

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

    2,边框厚度

    详细见前节中的讲解。

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

    3,边框颜色

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

    4,背景收缩宽度

    详细见前节中的讲解。

    5,背景颜色

    6,进度绘制范围边缘

    详细见前节中的讲解。

    7,弧线厚度

    详细见前节中的讲解。

    8, 进度颜色

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

    9,分段块宽度

    详细见前节中的讲解。

    10,进度样式

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

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

    11,进度条类型

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

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

    12,进度条模式

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

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

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

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

    13,Marquee循环一遍时间

     Marquee样式时动画的快慢。

     

    14,Marquees样式

    Marquee样式动画。

     

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

     

    15,Marquee类型

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

     

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

     

    16,进度文本类型

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

     

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

     

    17,进度文本颜色

    18,构造函数

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

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

    (三)重写方法

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

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

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

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
    
        //进度实际绘制宽度、高度
        float fRealWidth = Width - _DrawMargin * 2;
        float fRealHeight = Height - _DrawMargin * 2;
    
        float fRealLeftTop = _DrawMargin - 0.5f;
        if (fRealLeftTop < 0) fRealLeftTop = 0;
    
        //上下左右各留0.5个像素,防止切边
        bool bDrawSurroundLine = _SurroundLineSize > 0 ? true : false;
        bool bDrawInner = _ShrinkSize * 2 < Math.Min(Width, Height) ? true : false;
        float fSurroundLineSizeF = Convert.ToSingle(_SurroundLineSize);
        RectangleF rectInner = new RectangleF(_ShrinkSize, _ShrinkSize, (Width - 1) - _ShrinkSize * 2, (Height - 1) - _ShrinkSize * 2);
        RectangleF retcSurrondLine = new RectangleF(fSurroundLineSizeF / 2, fSurroundLineSizeF / 2, (Width - 1) - fSurroundLineSizeF, (Height - 1) - fSurroundLineSizeF);
        //弧线宽度为总宽度的十分之一,因为是圆弧,所以最终看起来是五分之一
        float fSurroundArcSize = _ArcSize > 0 ? _ArcSize : Width / 10;
        RectangleF retcSurrondArc = new RectangleF(fSurroundArcSize / 2 + fRealLeftTop, fSurroundArcSize / 2 + fRealLeftTop, (fRealWidth - 1) - fSurroundArcSize, (fRealHeight - 1) - fSurroundArcSize);
    
        SolidBrush sbInner = new SolidBrush(_ShrinkColor);
        SolidBrush sbFore = new SolidBrush(L_SliderColor);
        SolidBrush sbText = new SolidBrush(_TextColor);
        Pen penSurrondLine = new Pen(_SurroundLineColor, fSurroundLineSizeF);
        Pen penSurroundArc = new Pen(L_SliderColor, fSurroundArcSize);
    
        //滑块宽度为总宽度的五分之一
        float fSlider = Convert.ToSingle(fRealWidth) / 5;
        if (_ProgressType != ProgressType.Bar)
        {
            //360/5
            fSlider = 72;
        }
    
        float fRange = 0;
        if (L_MarqueeStyle == MarqueeStyle.Swing) fRange = fRealWidth - fSlider;
        else if (L_MarqueeStyle == MarqueeStyle.Cross) fRange = fRealWidth + fSlider;
        else fRange = fRealWidth;
    
        float fBlockSize = _BlockSize;
        if (fBlockSize < 3) fBlockSize = 3.0f;
        if (_ProgressType == ProgressType.Bar)
        {
            if (fBlockSize > fRealWidth) fBlockSize = fRealWidth;
        }
        else
        {
            if (fBlockSize > 360.0f) fBlockSize = 360.0f;
        }
    
        #region Bar
        if (_ProgressType == ProgressType.Bar)
        {
            //画内部
            if (bDrawInner)
            {
                e.Graphics.FillRectangle(sbInner, rectInner);
            }
    
            //画线
            if (bDrawSurroundLine)
            {
                e.Graphics.DrawRectangle(penSurrondLine, retcSurrondLine.X, retcSurrondLine.Y, retcSurrondLine.Width, retcSurrondLine.Height);
            }
    
            if (_ProgressMode == ProgressMode.Known)
            {
    
                #region Continuous
                if (_ProgressStyle == ProgressStyle.Continuous)
                {
                    //画进度
                    float fProgress = Convert.ToSingle(_Value) * fRealWidth / 100;
                    e.Graphics.FillRectangle(sbFore, _DrawMargin - 0.5f, fRealLeftTop, fProgress, fRealHeight);
                }
                #endregion
                #region Blocks
                else if (_ProgressStyle == ProgressStyle.Blocks)
                {
                    //获取方块数
                    float fBlocks = Convert.ToSingle(fRealWidth) / fBlockSize;
                    int iShowBlocks = Convert.ToInt32(fBlocks * _Value / 100);
                    //画进度
                    for (int i = 0; i < iShowBlocks; i++)
                    {
                        e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                    }
                }
                #endregion
                #region Helical
                else if (_ProgressStyle == ProgressStyle.Helical)
                {
                    float fProgress = Convert.ToSingle(_Value) * fRealWidth / 100;
    
                    //这里为了获得更好看的效果,要多加1个Block
                    float fBmpWidth = fRealWidth + fBlockSize;
                    float fBlocks = fBmpWidth / fBlockSize;
    
                    Bitmap bmp = new Bitmap(Convert.ToInt32(fBmpWidth), Convert.ToInt32(fRealHeight));
                    GraphicsPath gp = new GraphicsPath();
                    for (int i = 0; i < fBlocks; i++)
                    {
                        gp.AddPolygon(new PointF[]
                        {
                        //top-right
                        new PointF(fBmpWidth - i * fBlockSize ,0),
                        //bottom-right
                        new PointF(fBmpWidth - i * fBlockSize - fBlockSize,fRealHeight),
                        //bottom-left
                        new PointF(fBmpWidth - i * fBlockSize - fBlockSize*5/3,fRealHeight),
                        //top-left
                        new PointF(fBmpWidth - i * fBlockSize - fBlockSize*2/3,0),
    
                        });
                        gp.CloseFigure();
                    }
                    gp.CloseAllFigures();
    
                    Graphics g = Graphics.FromImage(bmp);
                    g.SmoothingMode = SmoothingMode.HighQuality;
                    g.FillPath(sbFore, gp);
                    gp.Dispose();
                    g.Dispose();
                    e.Graphics.DrawImage(bmp, new RectangleF(fRealLeftTop, fRealLeftTop, fProgress, fRealHeight), new RectangleF(bmp.Width - fProgress - fBlockSize, 0, fProgress, bmp.Height), GraphicsUnit.Pixel);
                    bmp.Dispose();
                }
                #endregion
                #region BlocksToContinuous
                else if (_ProgressStyle == ProgressStyle.BlocksToContinuous)
                {
                    float fBlocks = Convert.ToSingle(fRealWidth) / fBlockSize;
                    if (_Value <= 50)
                    {
                        int iShowBlocks = Convert.ToInt32(fBlocks * _Value * 2 / 100);
                        //画进度
                        for (int i = 0; i < iShowBlocks; i++)
                        {
                            e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                        }
                    }
                    else
                    {
                        int iShowBlocks = Convert.ToInt32(fBlocks * ((_Value - 50f) * 2 / 100));
                        for (int i = iShowBlocks; i < Convert.ToInt32(fBlocks); i++)
                        {
                            e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                        }
    
                        //画进度
                        float fProgress = Convert.ToSingle((_Value - 50) * 2) * fRealWidth / 100;
                        e.Graphics.FillRectangle(sbFore, fRealLeftTop, fRealLeftTop, fProgress, fRealHeight);
    
                    }
                }
                #endregion
            }
            else
            {
                #region Marquee
                if (_ProgressStyle == ProgressStyle.Marquee)
                {
                    if (L_MarqueeType == MarqueeType.SlowInSlowOut)
                    {
                        if (L_MarqueeStyle == MarqueeStyle.Swing)
                        {
                            double fz = fRange * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                            e.Graphics.FillRectangle(sbFore, Convert.ToSingle(fz) + fRealLeftTop, fRealLeftTop, fSlider, fRealHeight);
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Cross)
                        {
                            double fz = fRange * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2 - fSlider;
                            float fz2 = Convert.ToSingle(fz) + fRealLeftTop;
                            float fW = fSlider;
                            if (fz2 < fRealLeftTop)
                            {
                                fW = fz2 + fSlider - (fRealLeftTop);
                                fz2 = fRealLeftTop;
                            }
                            if (fz2 + fSlider > Width - (fRealLeftTop))
                            {
                                fW = Width - (fRealLeftTop) - fz2;
                            }
                            e.Graphics.FillRectangle(sbFore, fz2, fRealLeftTop, fW, fRealHeight);
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                        {
                            double fz = fRange * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                            e.Graphics.FillRectangle(sbFore, fRealLeftTop, fRealLeftTop, Convert.ToSingle(fz), fRealHeight);
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                        {
                            double fz = fRange * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                            if (!bAcrossed)
                            {
                                e.Graphics.FillRectangle(sbFore, fRealLeftTop, fRealLeftTop, Convert.ToSingle(fz), fRealHeight);
                            }
                            else
                            {
                                e.Graphics.FillRectangle(sbFore, Convert.ToSingle(fz) + fRealLeftTop, fRealLeftTop, fRealWidth - Convert.ToSingle(fz), fRealHeight);
                            }
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Blocks)
                        {
                            double fz = fRange * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                            float fBlocks = Convert.ToSingle(fRealWidth) / fBlockSize;
                            float fNowBlocks = Convert.ToSingle(fz) / fBlockSize;
    
                            for (int i = 0; i < fNowBlocks; i++)
                            {
                                e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                            }
                        }
                        else
                        {
                            double fz = fRange * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                            float fBlocks = Convert.ToSingle(fRealWidth) / fBlockSize;
                            float fNowBlocks = Convert.ToSingle(fz) / fBlockSize;
                            if (!bAcrossed)
                            {
                                for (int i = 0; i < fNowBlocks; i++)
                                {
                                    e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                                }
                            }
                            else
                            {
                                for (int i = Convert.ToInt32(fNowBlocks); i < fBlocks; i++)
                                {
                                    e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                                }
                            }
                        }
                    }
                    else
                    {
                        if (L_MarqueeStyle == MarqueeStyle.Swing)
                        {
                            e.Graphics.FillRectangle(sbFore, Convert.ToSingle(dMarqueeValue) + fRealLeftTop, fRealLeftTop, fSlider, fRealHeight);
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Cross)
                        {
                            float fz2 = Convert.ToSingle(dMarqueeValue) + fRealLeftTop;
                            float fW = fSlider;
                            if (fz2 < fRealLeftTop)
                            {
                                fW = fz2 + fSlider - (fRealLeftTop);
                                fz2 = fRealLeftTop;
                            }
                            if (fz2 + fSlider > Width - (fRealLeftTop))
                            {
                                fW = Width - (fRealLeftTop) - fz2;
                            }
                            e.Graphics.FillRectangle(sbFore, fz2, fRealLeftTop, fW, fRealHeight);
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                        {
                            e.Graphics.FillRectangle(sbFore, fRealLeftTop, fRealLeftTop, Convert.ToSingle(dMarqueeValue), fRealHeight);
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                        {
                            if (!bAcrossed)
                            {
                                e.Graphics.FillRectangle(sbFore, fRealLeftTop, fRealLeftTop, Convert.ToSingle(dMarqueeValue), fRealHeight);
                            }
                            else
                            {
                                e.Graphics.FillRectangle(sbFore, Convert.ToSingle(dMarqueeValue) + fRealLeftTop, fRealLeftTop, fRealWidth - Convert.ToSingle(dMarqueeValue), fRealHeight);
                            }
                        }
                        else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Blocks)
                        {
                            float fBlocks = Convert.ToSingle(fRealWidth) / fBlockSize;
                            float fNowBlocks = Convert.ToSingle(dMarqueeValue) / fBlockSize;
    
                            for (int i = 0; i < fNowBlocks; i++)
                            {
                                e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                            }
                        }
                        else
                        {
                            float fBlocks = Convert.ToSingle(fRealWidth) / fBlockSize;
                            float fNowBlocks = Convert.ToSingle(dMarqueeValue) / fBlockSize;
                            if (!bAcrossed)
                            {
                                for (int i = 0; i < fNowBlocks; i++)
                                {
                                    e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                                }
                            }
                            else
                            {
                                for (int i = Convert.ToInt32(fNowBlocks); i < fBlocks; i++)
                                {
                                    e.Graphics.FillRectangle(sbFore, i * fBlockSize + fRealLeftTop, fRealLeftTop, fBlockSize * 2 / 3, fRealHeight);
                                }
                            }
                        }
                    }
                }
                #endregion
                #region Helical
                else if (_ProgressStyle == ProgressStyle.Helical)
                {                        
                    //这里为了获得更好看的效果,要多加2个Block
                    float fBmpWidth = fRealWidth + fBlockSize*2;
                    float fBlocks = fBmpWidth / fBlockSize;
    
                    Bitmap bmp = new Bitmap(Convert.ToInt32(fBmpWidth), Convert.ToInt32(fRealHeight));
                    GraphicsPath gp = new GraphicsPath();
                    for (int i = 0; i < fBlocks; i++)
                    {
                        gp.AddPolygon(new PointF[]
                        {
                        //top-right
                        new PointF(fBmpWidth - i * fBlockSize ,0),
                        //bottom-right
                        new PointF(fBmpWidth - i * fBlockSize - fBlockSize,fRealHeight),
                        //bottom-left
                        new PointF(fBmpWidth - i * fBlockSize - fBlockSize*5/3,fRealHeight),
                        //top-left
                        new PointF(fBmpWidth - i * fBlockSize - fBlockSize*2/3,0),
    
                        });
                        gp.CloseFigure();
                    }
                    gp.CloseAllFigures();
    
                    Graphics g = Graphics.FromImage(bmp);
                    g.SmoothingMode = SmoothingMode.HighQuality;
                    g.FillPath(sbFore, gp);
                    gp.Dispose();
                    g.Dispose();
                    e.Graphics.DrawImage(bmp, new RectangleF(fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight), new RectangleF(Convert.ToSingle(dMarqueeValue) + fBlockSize, 0, fRealWidth, bmp.Height), GraphicsUnit.Pixel);
                    bmp.Dispose();
                }
                #endregion
            }
        }
        #endregion
        #region Arc
        else if (_ProgressType == ProgressType.Arc)
        {
            //画内部
            if (bDrawInner)
            {
                e.Graphics.FillEllipse(sbInner, rectInner);
            }
    
            //画线
            if (bDrawSurroundLine)
            {
                e.Graphics.DrawEllipse(penSurrondLine, retcSurrondLine.X, retcSurrondLine.Y, retcSurrondLine.Width, retcSurrondLine.Height);
            }
    
            #region Continuous
            if (_ProgressStyle == ProgressStyle.Continuous)
            {
                float fProgress = Convert.ToSingle(_Value * 3.6);
                if (fProgress > 0)
                {
                    e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270, fProgress);
                }
            }
            #endregion
            #region Blocks
            else if (_ProgressStyle == ProgressStyle.Blocks)
            {
                //获取方块数
                float fBlocks = 360.0f / fBlockSize;
                int iShowBlocks = Convert.ToInt32(fBlocks * _Value / 100);
                //画进度
                for (int i = 0; i < iShowBlocks; i++)
                {
                    //对于DrawArc而言,会产生在超过360度而导致重叠,所以需要判断,同时使用360-1,以防止相接。
                    //后续可以再进行优化:如果所分配的不正好是3的倍数,则取一相近值,使其能被3整除。
                    e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                }
            }
            #endregion
            #region BlocksToContinuous
            else if (_ProgressStyle == ProgressStyle.BlocksToContinuous)
            {
                //获取方块数
                float fBlocks = 360.0f / fBlockSize;
                if (_Value <= 50)
                {
                    int iShowBlocks = Convert.ToInt32(fBlocks * _Value * 2 / 100);
                    //画进度
                    for (int i = 0; i < iShowBlocks; i++)
                    {
                        //对于DrawArc而言,会产生在超过360度而导致重叠,所以需要判断,同时使用360-1,以防止相接。
                        //后续可以再进行优化:如果所分配的不正好是3的倍数,则取一相近值,使其能被3整除。
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                    }
                }
                else
                {
                    int iShowBlocks = Convert.ToInt32(fBlocks * (_Value - 50f) * 2 / 100);
                    //画进度
                    for (int i = iShowBlocks; i < Convert.ToInt32(fBlocks); i++)
                    {
                        //对于DrawArc而言,会产生在超过360度而导致重叠,所以需要判断,同时使用360-1,以防止相接。
                        //后续可以再进行优化:如果所分配的不正好是3的倍数,则取一相近值,使其能被3整除。
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                    }
    
                    float fProgress = (_Value - 50f) * 2 * 3.6f;
                    if (fProgress > 0)
                    {
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270, fProgress);
                    }
                }
            }
            #endregion
            #region Marquee
            else if (_ProgressStyle == ProgressStyle.Marquee)
            {
                if (L_MarqueeType == MarqueeType.SlowInSlowOut)
                {
                    if (L_MarqueeStyle == MarqueeStyle.Cross || L_MarqueeStyle == MarqueeStyle.Swing)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2 + 270 - fSlider / 2;
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, Convert.ToSingle(fz), fSlider);
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        if (!bAcrossed)
                        {
                            e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270, Convert.ToSingle(fz));
                        }
                        else
                        {
                            e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + Convert.ToSingle(fz), Convert.ToSingle(360 - fz));
                        }
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270, Convert.ToSingle(fz));
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Blocks)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        float fBlocks = Convert.ToSingle(fz / fBlockSize);
                        float fAllBlocks = 360.0f / fBlockSize;
                        if (!bAcrossed)
                        {
                            for (int i = 0; i < fBlocks; i++)
                            {
                                e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                        else
                        {
                            for (float i = fAllBlocks - 1; i >= fBlocks; i--)
                            {
                                e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                    }
                    else
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        float fBlocks = Convert.ToSingle(fz / fBlockSize);
    
                        for (int i = 0; i < fBlocks; i++)
                        {
                            e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                        }
    
                    }
    
                }
                else
                {
                    if (L_MarqueeStyle == MarqueeStyle.Cross || L_MarqueeStyle == MarqueeStyle.Swing)
                    {
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, Convert.ToSingle(dMarqueeValue), fSlider);
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                    {
                        if (!bAcrossed)
                        {
                            e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270, Convert.ToSingle(dMarqueeValue));
                        }
                        else
                        {
                            e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + Convert.ToSingle(dMarqueeValue), Convert.ToSingle(360 - dMarqueeValue));
                        }
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                    {
                        e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270, Convert.ToSingle(dMarqueeValue));
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Blocks)
                    {
                        float fBlocks = Convert.ToSingle(dMarqueeValue / fBlockSize);
                        float fAllBlocks = 360.0f / fBlockSize;
                        if (!bAcrossed)
                        {
                            for (int i = 0; i < fBlocks; i++)
                            {
                                e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                        else
                        {
                            for (float i = fAllBlocks - 1; i >= fBlocks; i--)
                            {
                                e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                    }
                    else
                    {
                        float fBlocks = Convert.ToSingle(dMarqueeValue / fBlockSize);
                        for (int i = 0; i < fBlocks; i++)
                        {
                            e.Graphics.DrawArc(penSurroundArc, retcSurrondArc, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                        }
                    }
                }
    
            }
            #endregion
        }
        #endregion
        #region Pie
        else if (_ProgressType == ProgressType.Pie)
        {
            //画内部
            if (bDrawInner)
            {
                e.Graphics.FillEllipse(sbInner, rectInner);
            }
    
            //画线
            if (bDrawSurroundLine)
            {
                e.Graphics.DrawEllipse(penSurrondLine, retcSurrondLine.X, retcSurrondLine.Y, retcSurrondLine.Width, retcSurrondLine.Height);
            }
    
            #region Continuous
            if (_ProgressStyle == ProgressStyle.Continuous)
            {
                //画进度
                //360/100;
                float fProgress = Convert.ToSingle(_Value * 3.6);
                if (fProgress > 0)
                {
                    //如果不进行fProgress>0的判断,在设计器调整大小时如果处于其它控件上方时会出来异常,导致绘制失败,虽然仍可以进行编译和使用。
                    e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270, fProgress);
                }
            }
            #endregion
            #region Blocks
            else if (_ProgressStyle == ProgressStyle.Blocks)
            {
                //获取方块数
                float fBlocks = 360.0f / fBlockSize;
                int iShowBlocks = Convert.ToInt32(fBlocks * _Value / 100);
                //画进度
                for (int i = 0; i < iShowBlocks; i++)
                {
                    //对于DrawArc而言,会产生在超过360度而导致重叠,所以需要判断,同时使用360-1,以防止相接。
                    //后续可以再进行优化:如果所分配的不正好是3的倍数,则取一相近值,使其能被3整除。
                    e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                }
            }
            #endregion
            #region 
            else if (_ProgressStyle == ProgressStyle.BlocksToContinuous)
            {
                //获取方块数
                float fBlocks = 360.0f / fBlockSize;
                if (_Value <= 50)
                {
                    int iShowBlocks = Convert.ToInt32(fBlocks * _Value * 2 / 100);
                    //画进度
                    for (int i = 0; i < iShowBlocks; i++)
                    {
                        //对于DrawArc而言,会产生在超过360度而导致重叠,所以需要判断,同时使用360-1,以防止相接。
                        //后续可以再进行优化:如果所分配的不正好是3的倍数,则取一相近值,使其能被3整除。
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                    }
                }
                else
                {
                    int iShowBlocks = Convert.ToInt32(fBlocks * (_Value - 50f) * 2 / 100);
                    //画进度
                    for (int i = iShowBlocks; i < Convert.ToInt32(fBlocks); i++)
                    {
                        //对于DrawArc而言,会产生在超过360度而导致重叠,所以需要判断,同时使用360-1,以防止相接。
                        //后续可以再进行优化:如果所分配的不正好是3的倍数,则取一相近值,使其能被3整除。
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                    }
    
                    //画进度
                    //360/100;
                    float fProgress = (_Value - 50f) * 2 * 3.6f;
                    if (fProgress > 0)
                    {
                        //如果不进行fProgress>0的判断,在设计器调整大小时如果处于其它控件上方时会出来异常,导致绘制失败,虽然仍可以进行编译和使用。
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270, fProgress);
                    }
                }
            }
            #endregion
            #region Marquee
            else if (_ProgressStyle == ProgressStyle.Marquee)
            {
                if (L_MarqueeType == MarqueeType.SlowInSlowOut)
                {
                    if (L_MarqueeStyle == MarqueeStyle.Cross || L_MarqueeStyle == MarqueeStyle.Swing)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2 + 270 - fSlider / 2;
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, Convert.ToSingle(fz), fSlider);
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        if (!bAcrossed)
                        {
                            e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270, Convert.ToSingle(fz));
                        }
                        else
                        {
                            e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + Convert.ToSingle(fz), Convert.ToSingle(360 - fz));
                        }
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270, Convert.ToSingle(fz));
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Blocks)
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        float fBlocks = Convert.ToSingle(fz / fBlockSize);
                        float fAllBlocks = 360.0f / fBlockSize;
                        if (!bAcrossed)
                        {
                            for (int i = 0; i < fBlocks; i++)
                            {
                                e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                        else
                        {
                            for (float i = fAllBlocks - 1; i >= fBlocks; i--)
                            {
                                e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                    }
                    else
                    {
                        double fz = 360 * (Math.Sin((dMarqueeValue - 90) * Math.PI / 180) + 1) / 2;
                        float fBlocks = Convert.ToSingle(fz / fBlockSize);
    
                        for (int i = 0; i < fBlocks; i++)
                        {
                            e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                        }
    
                    }
                }
                else
                {
                    if (L_MarqueeStyle == MarqueeStyle.Cross || L_MarqueeStyle == MarqueeStyle.Swing)
                    {
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, Convert.ToSingle(dMarqueeValue), fSlider);
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                    {
                        if (!bAcrossed)
                        {
                            e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270, Convert.ToSingle(dMarqueeValue));
                        }
                        else
                        {
                            e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + Convert.ToSingle(dMarqueeValue), Convert.ToSingle(360 - dMarqueeValue));
                        }
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                    {
                        e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270, Convert.ToSingle(dMarqueeValue));
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Blocks)
                    {
                        float fBlocks = Convert.ToSingle(dMarqueeValue / fBlockSize);
                        float fAllBlocks = 360.0f / fBlockSize;
                        if (!bAcrossed)
                        {
                            for (int i = 0; i < fBlocks; i++)
                            {
                                e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                        else
                        {
                            for (float i = fAllBlocks - 1; i >= fBlocks; i--)
                            {
                                e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                            }
                        }
                    }
                    else
                    {
                        float fBlocks = Convert.ToSingle(dMarqueeValue / fBlockSize);
                        for (int i = 0; i < fBlocks; i++)
                        {
                            e.Graphics.FillPie(sbFore, fRealLeftTop, fRealLeftTop, fRealWidth, fRealHeight, 270 + i * fBlockSize, fBlockSize * 2 / 3 > (359.0f - i * fBlockSize) ? (359.0f - i * fBlockSize) : fBlockSize * 2 / 3);
                        }
                    }
                }
    
            }
            #endregion
        }
        #endregion
    
        #region Text
    
        if (_ProgressText != ProgressText.None)
        {
            if (_ProgressStyle != ProgressStyle.Marquee)
            {
                string sText = _ProgressText == ProgressText.Number ? _Value.ToString() : _Value.ToString() + "%";
                SizeF sizeText = e.Graphics.MeasureString(sText, Font);
                float fx = (Width - sizeText.Width) / 2f;
                float fy = (Height - sizeText.Height) / 2f;
                e.Graphics.DrawString(sText, Font, _TextColor == Color.Empty ? sbFore : sbText, fx, fy);
            }
        }
    
        #endregion
    
        sbInner.Dispose();
        sbFore.Dispose();
        sbText.Dispose();
    }
    OnPaint
    private void TTimer_Tick(object sender, EventArgs e)
    {
        //进度实际绘制宽度、高度
        float fRealWidth = Width - _DrawMargin * 2;
        float fRealHeight = Height - _DrawMargin * 2;
    
        float fSlider = Convert.ToSingle(fRealWidth) / 5;
        if (_ProgressType != ProgressType.Bar)
        {
            //360/5
            fSlider = 72;
        }
    
        float fRange = 0;
        if (L_MarqueeStyle == MarqueeStyle.Swing) fRange = fRealWidth - fSlider;
        else if (L_MarqueeStyle == MarqueeStyle.Cross) fRange = fRealWidth + fSlider;
        else fRange = fRealWidth;
    
        double dStep = Convert.ToDouble(L_MarqueeTime) / 10;
        double dMarqueePer = 180.0 / dStep;
        if (L_MarqueeType == MarqueeType.Even)
        {
            if (_ProgressType == ProgressType.Bar)
            {
                dMarqueePer = fRange / dStep;
            }
            else
            {
                dMarqueePer = 360.0 / dStep;
            }
        }
    
        double dPeriod = 180;
        if (L_MarqueeStyle == MarqueeStyle.Swing || L_MarqueeStyle == MarqueeStyle.Reciprocation_Blocks || L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous) dPeriod = 360;
    
        if (_ProgressType == ProgressType.Bar)
        {
            if (_ProgressStyle == ProgressStyle.Marquee)
            {
                if (L_MarqueeType == MarqueeType.SlowInSlowOut)
                {
                    if (dMarqueeValue > dPeriod)
                    {
                        bAcrossed = !bAcrossed;
                        dMarqueeValue = 0;
                    }
                    dMarqueeValue += dMarqueePer;
                    Invalidate();
                }
                else
                {
                    if (L_MarqueeStyle == MarqueeStyle.Cross)
                    {
                        bToRight = true;
                        if (dMarqueeValue > fRange) dMarqueeValue = -fSlider;
                    }
                    else if (L_MarqueeStyle == MarqueeStyle.Across_Blocks || L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                    {
                        bToRight = true;
                        if (dMarqueeValue > fRange)
                        {
                            bAcrossed = !bAcrossed;
                            dMarqueeValue = 0;
                        }
                    }
                    else
                    {
                        if (dMarqueeValue > fRange) bToRight = false;
                        if (dMarqueeValue < 0) bToRight = true;
                    }
                    dMarqueeValue = bToRight ? dMarqueeValue + dMarqueePer : dMarqueeValue - dMarqueePer;
                    Invalidate();
                }
            }
            else
            {
                if (dMarqueeValue > 0) dMarqueeValue = 0;
                dMarqueeValue -= 1;
                if (dMarqueeValue <-_BlockSize) dMarqueeValue = 0;                   
                Invalidate();
            }
        }
        else if (_ProgressType == ProgressType.Arc || _ProgressType == ProgressType.Pie)
        {
            //为了更符合直观认识,即:转到顶部时速度最变到最慢,到最底部时速度加到最快,所以需要从270度开始。
            if (L_MarqueeType == MarqueeType.SlowInSlowOut)
            {
                if (dMarqueeValue > dPeriod)
                {
                    bAcrossed = !bAcrossed;
                    dMarqueeValue = 0;
                }
                dMarqueeValue += dMarqueePer;
                Invalidate();
            }
            else
            {
                if (L_MarqueeStyle == MarqueeStyle.Cross)
                {
                    bToRight = true;
                    if (dMarqueeValue > 360) dMarqueeValue = 0;
                }
                else if (L_MarqueeStyle == MarqueeStyle.Across_Blocks || L_MarqueeStyle == MarqueeStyle.Across_Continuous)
                {
                    bToRight = true;
                    if (dMarqueeValue > 360)
                    {
                        bAcrossed = !bAcrossed;
                        dMarqueeValue = 0;
                    }
                }
                else if (L_MarqueeStyle == MarqueeStyle.Reciprocation_Blocks || L_MarqueeStyle == MarqueeStyle.Reciprocation_Continuous)
                {
                    if (dMarqueeValue > 360) bToRight = false;
                    if (dMarqueeValue < 0) bToRight = true;
                }
                else
                {
                    if (dMarqueeValue > 270 - fSlider / 2) bToRight = false;
                    if (dMarqueeValue < -90 - fSlider / 2) bToRight = true;
                }
    
                dMarqueeValue = bToRight ? dMarqueeValue + dMarqueePer : dMarqueeValue - dMarqueePer;
                Invalidate();
            }
        }
    
    }
    Timer_Tick

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

    1,背景

    (1)横条(Bar)

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

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

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

    2,边框

    (1)横条(Bar)

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

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

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

    3,进度

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

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

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

    (1),进度明确

    1.1,连续

    (1)横条(Bar)

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

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

    (2)圆饼(Pie)

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

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

    (3)圆弧(Arc)

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

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

    1.2,分段块

    (1)横条(Bar)

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

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

    (2)圆饼(Pie)

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

    (3)圆弧(Arc)

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

    1.3,两段式


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

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

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

    1.4,螺旋

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

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

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

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

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

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

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

    (1)位置变化

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

      

    (2)范围变化

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

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

     

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

     (四)其它说明

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

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


    五、效果演示

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

     

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

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


     六、结束语

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

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

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

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


    七、源代码及工程下载

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

  • 相关阅读:
    OpenLDAP与Apache
    OpenLDAP双主
    OpenLDAP主从
    LDAP与禅道
    LDAP与jenkins
    LDAP与Samba
    LDAP与SSH
    LDAP客户端
    LDAP与migrationtools 导入系统账号
    OpenLDAP与phpldapadmin的搭建
  • 原文地址:https://www.cnblogs.com/lesliexin/p/13575517.html
Copyright © 2011-2022 走看看