对进度条的通用封装实现
一直想写点啥对最近的工作做个总结,由于项目比较忙,可能还有自己的各种理由推脱有点懈怠,零碎的总结过一些,都没有动笔写下来过。眼看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:下载地址