前言
當我們在Winform進行某些比較花時間的運算時,
若沒有使用非同步的方法來呼叫,畫面上的視窗就會顯示沒有回應,
這是一種比較差的使用者體驗,可能會讓使用者以為當機了,
在這邊為了方便重複使用,所以寫了一個Template Class來當作非同步的呼叫媒介,
也可以同時在非同步處理時,跳出提示訊息顯示"處理中"
實際演練
為了統一非同步的行為,並在處理時顯示同樣的提示視窗,
所以我們撰寫了一個Template Class來處理非同步的邏輯。
public class AsyncTemplate { public static Action OnInvokeStarting { get; set; } public static Action OnInvokeEnding { get; set; } public static void DoWorkAsync(Action beginAction, Action endAction, Action<Exception> errorAction) { ThreadPool.QueueUserWorkItem(new WaitCallback( (o) => { try { beginAction(); endAction(); } catch (Exception ex) { errorAction(ex); return; } finally { if (OnInvokeEnding != null) { OnInvokeEnding(); } } }) , null); if (OnInvokeStarting != null) { OnInvokeStarting(); } } public static void DoWorkAsync<TResult>(Func<TResult> beginAction, Action<TResult> endAction, Action<Exception> errorAction) { ThreadPool.QueueUserWorkItem(new WaitCallback( (o) => { TResult result = default(TResult); try { result = beginAction(); endAction(result); } catch (Exception ex) { errorAction(ex); return; } finally { if (OnInvokeEnding != null) { OnInvokeEnding(); } } }) , null); if (OnInvokeStarting != null) { OnInvokeStarting(); } } }
在這邊提供了兩個方法來提供非同步的作業,分別是沒有回傳值以及有回傳值,
然後提供了兩個Action,讓我們可以在處理之前跳出提示視窗,處理完後關閉提示視窗,
在這邊準備了兩個Form,一個Form提供了加法的計算
另外一個Form則顯示處理中,以及一個Progress Bar滾動模擬資料處理
(小技巧:將Progress Bar的Style設為Marquee即可獲得如上圖之效果)
Form1的程式碼如下
public partial class Form1 : Form { private Form2 mProgressFrom = new Form2(); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { AsyncTemplate.DoWorkAsync( () => { return doWork(int.Parse(textBox1.Text), int.Parse(textBox2.Text)); }, (result) => { MessageBox.Show("Success, Result is " + result.ToString()); }, (exception) => { MessageBox.Show(exception.Message); //error handling }); } private int doWork(int a, int b) { Thread.Sleep(10000); return a + b; } private void Form1_Load(object sender, EventArgs e) { AsyncTemplate.OnInvokeStarting = () => { mProgressFrom.ShowDialog(); }; AsyncTemplate.OnInvokeEnding = () => { if (mProgressFrom.InvokeRequired) { mProgressFrom.Invoke(new MethodInvoker( () => { mProgressFrom.Close(); }) ); } else { mProgressFrom.Close(); } }; } }
在Form_Load方法中,我們設定了OnInvokeStarting和OnInvokeEnding的屬性,
分別在開始處理時顯示處理中視窗,以及當運算完成時關閉提示視窗。
在doWork方法中,進行的是加法的運算,在這邊為了模擬長時間的運算,
所以我們在此方法中停留了十秒。
而button1_Click方法中使用Template Class來呼叫主要的演算邏輯
實際執行程式,我們可以發現達到了我們預期中的效果,
按下計算時,跳出了提示視窗,讓使用者知道正在進行大量的運算,
也不會呈現沒有回應的狀態。
結語
好的使用者體驗,是非常重要的,
不但可以讓使用者使用更順手,也不會誤認程式常常當機,
在.Net中還有許多種非同步的作法,例如BackgroundWorker,
大家可以按照自己在開發時的需求做改變,
在這邊提供了一種實作的方式,也歡迎大家多多指教與討論 ^^