代码改变世界

对进度条的通用封装实现

2013-12-29 18:23  Johnnie Zhang  阅读(2499)  评论(4编辑  收藏  举报

          一直想写点啥对最近的工作做个总结,由于项目比较忙,可能还有自己的各种理由推脱有点懈怠,零碎的总结过一些,都没有动笔写下来过。眼看2013都要过去了,该写点啥来总结下。先从自己对进度封装的一点学习经验写出来,供大家交流,欢迎园子里的朋友不吝啬的拍砖。

         首先定义对进度表示的契约,定义进度行为(IProgressor)、进度信息(IStepProgress)和中断处理(ITrackCancel)的接口如下:

    /// <summary>
    /// 进度行为接口
    /// </summary>
    public interface IProgressor
    {
        string Message { get; set; }
        void Show();
        void Hide();
        void Step();
    }
     /// <summary>
    /// 进度展示接口
    /// </summary>
    public interface IStepProgress : IProgressor
    {
        int MaxRange { get; set; }
        int MinRange { get; set; }
        int StepValue { get; set; }
        i
    /// <summary>
    /// 中断接口
    /// </summary>
    public interface ITrackCancel
    {
        void Cancel();
        bool Continue();
        bool CancelOnClick { get; set; }
        bool CancelOnKeyPress { get; set; }

        IProgressor Progressor { get; set; }
    }

          实现进度信息接口,实现类(ProgressBarDialog)如下:

    /// <summary>
    /// 进度实现类
    /// </summary>
    public class ProgressBarDialog:IStepProgress
    {
        
        private int maxRange;
        private int minRange;
        private int stepValue;
        private int position;
        private string message;
        private Task task;
        private ProgressBarForm progressBarForm;
        private CancellationTokenSource cancellationTokenSource;
        private ITrackCancel trackCancel;


        public Form ParentForm { get; set; }


        public ITrackCancel TrackCancel
        {
            get { return trackCancel; }
            set 
            {
                if (value != null)
                {
                    trackCancel = value;
                    trackCancel.Progressor = this;
                }
                if(progressBarForm != null)
                    progressBarForm.TrackCancel = value;
                
            }
        }

        public int MaxRange
        {
            get { return maxRange; }
            set
            {
                if (maxRange != value)
                {
                    if (progressBarForm != null)
                        progressBarForm.MaxRange = value;
                    maxRange = value;
                }
            }
        }


        public int MinRange
        {
            get { return minRange; }
            set
            {
                if (minRange != value)
                {
                    if(progressBarForm != null)
                        progressBarForm.MinRange = value;
                    minRange = value;
                }
            }
        }

        public int StepValue
        {
            get { return stepValue; }
            set
            {
                if (stepValue != value)
                {
                    if (progressBarForm != null)
                        progressBarForm.StepValue = value;
                    stepValue = value;
                }
            }
        }

        public int Position
        {
            get { return position; }
            set 
            {
                if (progressBarForm != null)
                    progressBarForm.Position = value;
                position = value;
            }
        }

        public string Message
        {
            get { return message; }
            set
            {
                if (message != value)
                {
                    if (progressBarForm != null)
                        progressBarForm.Message = value;
                    message = value;
                }
            }
        }

        private void InitDialog()
        {
            if (progressBarForm == null)
                progressBarForm = new ProgressBarForm(ParentForm);
            progressBarForm.TopMost = true;
            progressBarForm.MaxRange = this.MaxRange;
            progressBarForm.MinRange = this.MinRange;
            progressBarForm.StepValue = this.StepValue;
            progressBarForm.Position = this.Position;
            if (trackCancel != null)
                progressBarForm.TrackCancel = this.TrackCancel;
            if (!cancellationTokenSource.IsCancellationRequested)//此处的取消方法不是强制取消,CLR线程管理会在响应后合适的时候取消
            {                                                     //这种线程取消的方式,一个目的是为了解决.NET4.0以前线程强制关闭的异常问题
                if (progressBarForm.ShowDialog() == DialogResult.OK)
                {
                    
                }
            }
        }

        public void Show()
        {
            ShowDialog();
        }

        public void ShowDialog()
        {
            try
            {
                cancellationTokenSource = new CancellationTokenSource();
                task = new Task(InitDialog, cancellationTokenSource.Token);
                task.Start();
                //此处创建的延续任务,为确保线程取消的时候,出现异常情况(如:进度UI还没有展示,主方法就已经走完)的保险处理
                Task cancelTask = task.ContinueWith(
                    (cancellatinTask) =>
                    {
                        if (progressBarForm != null && progressBarForm.Visible)
                            progressBarForm.Hide();
                    }, TaskContinuationOptions.OnlyOnCanceled);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public void HideDialog()
        {
            try
            {
                if (progressBarForm == null)//当进度窗体还没有显示出来,进度线程监视的方法已经走完,取消task线程
                    cancellationTokenSource.Cancel();
                else
                {
                    MethodInvoker invoker = () =>
                        {
                            if (progressBarForm.Visible)
                                progressBarForm.Hide();
                        };
                    if (progressBarForm.InvokeRequired)
                    {
                        progressBarForm.Invoke(invoker);
                    }
                    else
                    {
                        invoker();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public void Hide()
        {
            HideDialog();
        }

        public void Step()
        {
            if (progressBarForm != null)
                progressBarForm.Step();
        }
    }

       实现进度展示的UI类(ProgressBarForm)如下:

public partial class ProgressBarForm : Form
    {
        public ProgressBarForm(Form parent)
        {
            InitializeComponent();
            InitLocation(parent);
        }

        private void InitLocation(Form parent)
        {
            if (parent != null)
            {
                Left = parent.Left + (parent.Width - Width) / 2;
                Top = parent.Top + (parent.Height - Height) / 2;
            }
        }

        private int maxRange;
        private int minRange;
        private int stepValue;
        private int position;
        private string message;

        public ITrackCancel TrackCancel { get; set; }

        public string Message
        {
            get { return message; }
            set
            {
                if (message != value)
                {
                    MethodInvoker invoker = () => lab_Message.Text = value; ;
                    if (lab_Message.InvokeRequired)
                    {
                        //lab_Message.Invoke(invoker);
                        IAsyncResult asyncResult =lab_Message.BeginInvoke(invoker);
                        lab_Message.EndInvoke(asyncResult);
                    }
                    else
                    {
                        invoker();
                    }
                    message = value;
                }
            }
        }

        public int MaxRange
        {
            get { return maxRange; }
            set
            {
                if (maxRange != value)
                {
                    progressBar1.Maximum = value;
                    maxRange = value;
                }
            }
        }

        public int MinRange
        {
            get { return minRange; }
            set
            {
                if (minRange != value)
                {
                    progressBar1.Minimum = value;
                    minRange = value;
                }
            }
        }

        public int StepValue
        {
            get { return stepValue; }
            set
            {
                if (stepValue != value)
                {
                    progressBar1.Step = value;
                    stepValue = value;
                }
            }
        }

        public int Position
        {
            get { return position; }
            set
            {
                if (position != value)
                {
                    progressBar1.Value = value;
                    position = value;
                }
            }
        }

        public void Step()
        {
            MethodInvoker invoker = () =>
                {
                    int newValue = progressBar1.Value + this.StepValue;
                    //当进度超过最大值,默认赋最小值
                    if (newValue > this.MaxRange)
                    {
                        progressBar1.Value = MinRange;
                        progressBar1.Refresh();
                    }
                    else
                    {
                        progressBar1.Value = newValue;
                        progressBar1.Refresh();
                    }
                };
            if (progressBar1.InvokeRequired)
            {
                //progressBar1.Invoke(invoker);
                //采用异步委托的方法,提高进度条的响应速度,不知道线程创建多会不会影响效率(PS:没有研究过异步委托创建线程的方式,不知道是从线程池那线程,还是每次来就创建一个)
                IAsyncResult asyncResult =progressBar1.BeginInvoke(invoker);
                progressBar1.EndInvoke(asyncResult);
            }
            else
            {
                invoker();
            }
        }

        private void ProgressBarForm_KeyDown(object sender, KeyEventArgs e)
        {
            if( e.KeyCode == Keys.Escape && TrackCancel.CancelOnKeyPress)
                TrackCancel.Cancel();
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            if (TrackCancel.CancelOnClick)
            {
                TrackCancel.Cancel();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            //当中断处理不支持按钮取消时候,将取消按钮不显示
            if (!TrackCancel.CancelOnClick)
            {
                this.Height = 65;
            }
        }
    }

         下面完成对进度的调用进度条的简单封装类(DialogManager)如下:

     /// <summary>
     /// 管理进度条的方法类
     /// </summary>
    public class ProgressManager
    {
        public static IStepProgress ShowProgressBarDialog(ITrackCancel trackCancel,Form pForm)
        {
            if (trackCancel == null)
                return null;
            return new ProgressBarDialog() {TrackCancel = trackCancel,ParentForm = pForm};
        }

        public static IStepProgress ShowProgressBarDialog(ITrackCancel trackCancel, Form pForm,int maxRange,int minRange,int stepValue)
        {
            if (trackCancel == null)
                return null;
            return new ProgressBarDialog()
                {
                    TrackCancel = trackCancel,
                    ParentForm = pForm,
                    MaxRange = maxRange,
                    MinRange = minRange,
                    StepValue = stepValue
                };
        }
    }

          最后,在自己代码中调用进度的写法如下:

         private void button1_Click(object sender, EventArgs e)
        {
            ITrackCancel trackCancel = new CancelTracker() {CancelOnClick = true, CancelOnKeyPress = true};
            IStepProgress stepProgress = ProgressManager.ShowProgressBarDialog(trackCancel,this);
            stepProgress.MinRange = 0;
            stepProgress.MaxRange = 100;
            stepProgress.StepValue = 1;
            bool pContinue = true;
            stepProgress.Show();
            for (int i = 0; i < 100; i++)
            {
                stepProgress.Message = String.Format("正在计算{0}...",i);
                pContinue = trackCancel.Continue();
                if(!pContinue)
                    break;
                stepProgress.Step();
                Thread.Sleep(50);
            }
            stepProgress.Hide();

        }

           以上为对简单进度条的封装过程,接口的定义参考了ArcGIS的接口定义方式。如果想增加对其它的进度条的支持,需要实现对应的进度条接口和对应的展示UI即可。如:想增加一个转圈等待的进度条展示进度,你只需要实现IProgressor接口,想支持中断处理,实例化实现ITrackCancel接口的类即可,可以用工厂模式来管理你的进度条展示。

         题外的话:最近在学习fyiReporting和SharpDeveloper的源代码,fyiReporting的源代码太多,网上没发现什么知道的资源,看起来还是一知半解。SharpDeveloper的源码,网上资源大都讲的都是关于插件框架的东西,还有SharpDeveloper的团队也出过一本书的,讲的都是很久以前的版本。在此,求园友们对此感兴趣的能指导下学习fyiReporting或者SharpDeveloper的资料,或者一起共同学习。

        本文的Demo:下载地址