[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