WPF多线程的一种解决方案

最近使用WPF来构建桌面程序,其中要求是:

1. 界面与后台程序使用不同线程,以便在后台运行耗时计算时界面依然响应。

2. 后台程序需要控制界面某些元素,如显示和修改值。

3. 后台程序执行中,需要获取界面把某些用户输入,如用户输出某字符串来继续执行。

 

然后开始设计,编码测试,过程如下:

对于第1个要求,根据以前的编程经验(Qt,GTK...),果断使用System.Threading.Thread来Start一个线程,与WPF界面线程分开,嗯。

对于第2个要求,使用DataBinding,把WPF界面的类的DataContext设置成给线程用的类里,绑定值,就很容易做到在第二个线程修改值直接反应到WPF的界面上。但对于切换界面(如修改MainWindow的Content给另外一个UserControl),搞得非常麻烦。想过通过绑定Visibility属性,添加IsVisibilityChange方法来做,效果都非常差。从MSDN看WPF多线程的例子http://msdn.microsoft.com/en-us/library/ms741870.aspx,想着把第1个要求使用System.Threading.Thread改成Dispatcher.BeginInvoke来运行后台线程,这样就可以随便更改WPF的元素了。

接着第3个要求,考虑到.Net有ManualResetEvent(或者AutoResetEvent)这个很方便的类,果断写下这个UserInput类(暂时使用string来作为用户输入的表现)。

然后在后台线程调用WaitInput,这下麻烦出来了,WPF界面线程也一起Block掉了。

于是不停的修改代码,尝试网上搜索到的方法,均不能解决。然后回头再仔细看MSDN关于WPF多线程的例子,每一个字都认真的读,发现Dispatcher是唯一的多线程可以修改WPF元素的类。那不知道我在另外一个线程来调用Dispatcher.BeginInvoke来修改WPF元素可否?立即编写代码尝试。结果是可行的。最终程序结构如下:

整个程序只有一个Window类,那就是从Window派生出来的MainWindow。定义若干UserControl与之相关的Presenter类(用于数据绑定和界面切换),并在MainWindow构造函数里一一生成并绑定,所有的Presenter有Dispatcher属性,所有的Presenter有若干delegate用于数据绑定不能实现的操作(定义一个基类即可),如更改MainWindow的Content,并在各个UserControl里定义相应的函数,赋于这些delegate,在Presenter调用这些delegate的时候,使用Dispatcher.BeginInvoke来调用。初始Content为某一UserControl。使用BackgroundWorker来启动后台线程,定义一个带ManualResetEvent的UserInput来给线程得到WPF输入。

这个后台线程就可以随时得到用户的输入,如用户想切换到某个特定界面, 后台线程得到这个输入后可以很方便的切换界面。

一些参考的类:

View Code
 1 using System;
 2 using System.Threading;
 3 
 4 namespace WPF
 5 {
 6     public class UserInput
 7     {
 8         // Won't send signal when construct.
 9         private ManualResetEvent _mre = new ManualResetEvent(false);
10         private string _currentInput = null;
11 
12         /// <summary>
13         /// Current Input, if not input, return null or "", using string.IsNullOrEmpty to determine.
14         /// </summary>
15         public string CurrentInput
16         {
17             get
18             {
19                 string result = _currentInput;
20                 _currentInput = null;
21                 return result;
22             }
23             set
24             {
25                 _currentInput = value;
26                 _mre.Set();
27             }
28         }
29 
30         public string WaitInput
31         {
32             get
33             {
34                 _mre.WaitOne();
35                 _mre.Reset();
36                 return CurrentInput;
37             }
38         }
39 
40         public const string BackString = "..";
41     }
42 }
View Code
 1 using System;
 2 using System.ComponentModel;
 3 
 4 namespace WPF
 5 {
 6     public abstract class Notifier : INotifyPropertyChanged
 7     {
 8         public event PropertyChangedEventHandler PropertyChanged;
 9 
10         protected virtual void OnProertyChanged(string propertyName)
11         {
12             if (PropertyChanged != null)
13             {
14                 PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
15             }
16         }
17     }
18 }
View Code
 1 using System;
 2 using System.Windows;
 3 using System.Windows.Threading;
 4 
 5 namespace WPF
 6 {
 7     public class PresenterBase : Notifier
 8     {
 9         private string _title;
10         private Dispatcher _dispatcher = null;
11         public delegate void NoArgDelegate();
12         public NoArgDelegate ShowHandler;
13 
14         public PresenterBase()
15         {
16             
17         }
18 
19         public PresenterBase(string title)
20         {
21             _title = title;
22         }
23 
24         public Dispatcher Dispatcher
25         {
26             get { return _dispatcher; }
27             set
28             {
29                 _dispatcher = value;
30             }
31         }
32 
33         public string Title
34         {
35             get { return _title; }
36             set
37             {
38                 _title = value;
39                 OnProertyChanged("Title");
40             }
41         }
42 
43         public void Show()
44         {
45             if (ShowHandler != null)
46                 Dispatcher.BeginInvoke(ShowHandler);
47         }
48     }
49 }
posted @ 2011-05-25 12:32  为学  阅读(2666)  评论(0编辑  收藏  举报