2013年9月11日 星期三

[轉貼] 非同步呼叫方法並跳出處理中視窗

出處:http://www.dotblogs.com.tw/kirkchen/archive/2010/06/14/15885.aspx


前言


當我們在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提供了加法的計算
18 
另外一個Form則顯示處理中,以及一個Progress Bar滾動模擬資料處理
19 
(小技巧:將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來呼叫主要的演算邏輯

實際執行程式,我們可以發現達到了我們預期中的效果,
按下計算時,跳出了提示視窗,讓使用者知道正在進行大量的運算,
也不會呈現沒有回應的狀態。
20

結語


好的使用者體驗,是非常重要的,
不但可以讓使用者使用更順手,也不會誤認程式常常當機,
在.Net中還有許多種非同步的作法,例如BackgroundWorker,
大家可以按照自己在開發時的需求做改變,
在這邊提供了一種實作的方式,也歡迎大家多多指教與討論 ^^

沒有留言:

張貼留言