一直想寫點啥對最近的工作做個總結,由於項目比較忙,可能還有自己的各種理由推脫有點懈怠,零碎的總結過一些,都沒有動筆寫下來過。眼看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:下載地址