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

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


前言

 

當我們在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,

大家可以按照自己在開發時的需求做改變,

在這邊提供了一種實作的方式,也歡迎大家多多指教與討論 ^^

範例下載