[02]WPF異步響應,自定義事件、委托——多線程處理


題記

     在編寫有GUI的程序時,會遇到這樣一種情形:用戶點擊了一個按鈕,程序處理這個事件,然而這個處理過程耗時間較長。我們不想讓軟件卡在這里,而是讓用戶可以繼續使用其他的軟件功能。這種問題可以用多線程的事件響應來解決。這里,我就WPF的多線程事件響應做一個簡單的歸納。

 

一、簡單的異步的事件響應

     在WPF中,針對簡單的多線程處理過程,我們可以使用.NET自帶的BackgroundWork完成。BackgroundWork的處理過程就是異步的,不會讓用戶界面停止響應。

using System.ComponentModel;
using System.Threading;

namespace TestProject
{    
    public partial class MainWindow : Window
    {
        //...
            
        private void Button1_Click(object sender, RoutedEventArgs e)
        {
            //聲明
            BackgroundWorker worker = new BackgroundWorker();
            
            // worker 要做的事情 使用了匿名的事件響應函數
            worker.DoWork += (o, ea) =>
            {
                //WPF中線程只能控制自己創建的控件,
                //如果要修改主線程創建的MainWindow界面的內容,
                //可以委托主線程的Dispatcher處理。
                //在這里,委托內容為一個匿名的Action對象。
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.TextBox1.Text = "worker started";
                }));
                Thread.Sleep(1000);
            };
            // worker 完成事件響應
            worker.RunWorkerCompleted += (o, ea) =>
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.TextBox1.Text = "worker finished";
                }));
            };
            
            //注意:運行了下面這一行代碼,worker才真正開始工作。上面都只是聲明定義而已。
            worker.RunWorkerAsync();
        }
        
        //...
        
    }
}

 

二、自定義事件的多線程處理過程

     有時候,在我們創建的新的線程中,可能有一些事件需要主線程處理。對於這種比較復雜的異步處理,可以自定義事件,用C#中的委托(delegate)實現。

     

      MyThread.cs (自定義事件)     

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace TestProject
{
    //定義 自定義事件的參數類型
    public class MyEventArgs : EventArgs
    {
        //參數可以攜帶值,方便處理程序使用
        public readonly int value;
        public MyEventArgs(int v)
        {
            value = v;
        }
    }

    class MyThread
    {
        //...
        
        //定義delegate
        public delegate void MyEventHandler(object sender, MyEventArgs e);
        
        //聲明 自定義事件
        public event MyEventHandler MyEvent;

        //...

        //觸發事件
        protected virtual void OnMyEvent(MyEventArgs e)
        {
            if (MyEvent != null)
            {
                MyEvent(this, e);
            }
        }

        //...
        
        //測試函數
        public void Test()
        {
            System.Threading.Thread.Sleep(1000);
            this.OnMyEvent(new MyEventArgs(100));
            System.Threading.Thread.Sleep(2000);
        }
    }
}

 

      MainWindows.xaml.cs

using System.ComponentModel;
using System.Threading;

namespace TestProject
{    
    public partial class MainWindow : Window
    {
        //...
        
        //測試
        private void Button2_Click(object sender, RoutedEventArgs e)
        {
            MyThread myThread = new MyThread();
            
            //為myThread的MyEvent事件聲明一個響應函數
            myThread.MyEvent += MyThread_MyEvent;
            
            //定義新的線程
            Thread thread = new Thread(new ThreadStart(myThread.Test));
            
            //開始新的線程
            thread.Start();
        }
        
        // 新的線程中 MyEvent 的響應函數
        public void MyThread_MyEvent(object sender, MyEventArgs e)
        {
            //同樣,如果想要修改主界面的控件,
            //需要委托主線程的Dispatcher來處理。
            this.Dispatcher.Invoke((Action)(() =>
            {
                this.TextBox2.Text = "MyEvent triggered";
            }));
        }
        
        //...
        
    }
}

 

總結

     以上介紹了兩種情況下,WPF多線程的實現方法。第一種可以滿足一般的需求,例如:后台加載文件。第二種主要針對后台處理時有特殊的事件需要前台響應,例如:后台加載文件,每當加載滿1M時,在MainWindow展示特定內容給用戶。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM