DispatherObject
很多wpf 對象有線程相關性(thread affinity),意味在你隻能在建立它的線程上使用它。這和window form的UI控件一樣。 WinForm的control.invoke() ,winform.timer 會在進行UI操作時做一個線程切換。
在wpf中,繼承自System.Windows.Threading的DispatherObject的對象都具有線程相關性, 比如:Brush, Geometry
public class DependencyObject : DispatcherObject
public abstract class DispatcherObject
{
// Fields
private Dispatcher _dispatcher;
// Methods
protected DispatcherObject();
[EditorBrowsable(EditorBrowsableState.Never)]
public bool CheckAccess();
[FriendAccessAllowed]
internal void DetachFromDispatcher();
[EditorBrowsable(EditorBrowsableState.Never)]
public void VerifyAccess();
// Properties
[EditorBrowsable(EditorBrowsableState.Advanced)]
public Dispatcher Dispatcher { get; }
}
CheckAccess()與VeriftyAccess()來判斷是否在正确的線程上,VerifyAccess會抛異常,如果不在正确的線程上。Freezable對象能夠通過frozon,來消除線程的限制。
The Dispather:
每一個需要建立UI objects的線程必須要有一個dispatcher object.這個對象做的事相當于win32的消息泵,用一個循環不斷dispatch input message to appropriate handlers.
DispatcherObject中有這個屬性,或者可以通過Dispatcher.CurrentDispather來獲得目前線程的dispather.
如果要做完一些事情後更新UI,必須保證在UI線程上更新。Dispatcher 就是提供這種功能的,Dispatcher.Invoke()方法 會等待方法執行完再傳回,Dispather.Invoke()方法不會等待方法執行完,而是立即傳回。Invoke()方法有死鎖風險,BeginInvoke 沒有。
匿名方法:
Public class window1:window
{
Public void delegate Mymethod;
Mymethod method = delegate{ this.foreground = new SolidColorBrush(Color.Blue);}
This.Dispather.BeginInvoke(DispatcherPriority.Normal,method);
} //有ApplicationIdle 和SystemIdle優先級
;
}
傳參數的方法:delegate void BackGround(Color c)
Private void SetBackgroundColor(Color c) {this.background = c;}
BackGround method = SetBackgroundColor;
DispatcherOperation e =This.dispacher.BeginInvoke(DispatherPriority.Normal,method,Color.Blue);
傳回的 DispatherOperation.Status屬性可以檢視狀态:
DispatcherOperationStatus
Value | Meaning |
Pending | The dispatcher has not yet called the method. |
Executing | The method is currently executing on the dispatcher thread. |
Completed | The method has finished executing. |
Aborted | The operation was aborted. |
可以用DispatherOperation.Abort()退出,不要DispatherOperation.Completed+=delegate{}; 因為當加上的時候可能線程可能已經傳回了。直接把完成時要做的事,放到線程執行方法中去,特别是界面處理。
沒有了.net異步程式設計的,EndInvoke()方法,也沒有把一個completion callback傳給BeginInvoke的 方式。
DispatcherTimer:
WPF 的timer,
private DispatcherTimer dt;
public MyWindow( ) {
DispatcherTimer dt = new DispatcherTimer( );
dt.Tick += dt_Tick;
dt.Interval = TimeSpan.FromSeconds(2);
dt.Start( );
}
void dt_Tick(object sender, EventArgs e) {
Random r = new Random( );
byte[] vals = new byte[3];
r.NextBytes(vals);
Color c = Color.FromRgb(vals[0], vals[1], vals[2]);
this.Background = new SolidColorBrush(c);
}
private DispatcherTimer _timer;
timer = new DispatcherTimer(DispatcherPriority.Background);
timer.Interval = TimeSpan.FromMinutes(5);
timer.Tick += delegate { ScheduleUpdate(); };
timer.Start();
多UI thread和dispather
一個Application能有多個線程來建立UI,但是一個視窗和它的element都必須在一個線程中,每一個host UI object的線程都需要一個dispatcher,讓UI 對象來工作。在一個單線程的application,我們不需要建立dispather,因為application類會自動在startup時建立dispatcher,在Exit時候shut down dispatcher.但是如果我們要自己建多線程UI,就得自己start up和shut down Dispather.
ThreadStart threadMethod = delegate {
Window1 w = new Window1( );
w.Show( );
System.Windows.Threading.Dispatcher.Run( ); // Won't return until dispatcher shuts down
};
Thread thread = new Thread(threadMethod);
thread.SetApartmentState(ApartmentState.STA);
thread.Start( );
繼承DispatcherObject基類會在建立時自動建dispatcher執行個體,是以不需要new,但是要Dispatcher.Run()來使得message能夠正确發送到UI object的線程中。這個方法不會傳回知道dispatcher.InvokeShutdown. 對于客戶自己建立的UI線程,需要手動shut down。
set the COM threading model to STA線程套間,雖然在com互動時候才用到COM threading model, 但是因為太多的系統特征需要com互動,是以dispatcher要求設定STA。
BackgroundWorker:
System.ComponentModel.BackgroundWorker, 這個類可以在windows forms和WPF中用,但是卻是兩種不同的實作機制。用AsyncOperationManager來區分,一個application層的變量,當WPF application start up時候,WPF會自動配置AsyncOperationManager,(用目前dispatcher).
WPF的BackgroundWorker實際上是為了更友善使用,對dispatcher的包裝。
使用:
1. Attach event to DoWork event.
2. Add handler to ProgressChanged, RunWorkerCompleted event
3. 為了使用progressChanged event,要設定BackgroundWorker.WorkerReportsProgress 為true; 在DoWork的handler中,調ReportProgress()報告狀态。
例子:
partial class MyWindow : Window {
private BackgroundWorker bw;
public MyWindow( ) {
bw = new BackgroundWorker( );
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
bw_RunWorkerCompleted);
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync( );
}
void bw_DoWork(object sender, DoWorkEventArgs e) {
// Running on a worker thread
for (int i = 0; i < 10; ++i) {
int percent = i * 10;
bw.ReportProgress(percent);
Thread.Sleep(1000);
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) {
this.Text = "Working: " + e.ProgressPercentage + "%";
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
this.Text = "Finished";
}
當運作RunWorkerAsync( ),BackgroundWorker 在worker thread中抛出DoWork event,是以在DoWork handler中不能進行UI操作。但是ProgressChanged and RunWorkerCompleted events 是在UI thread中raise的,在它們的handler中可以進行UI 操作。
RunWorkerCompleted handler 傳入RunWorkerCompletedEventArgs object,DoWork m方法中可能抛出異常,如果有異常抛出,RunWorkerCompletedEventArgs 對象會包含異常,否則為null。