2009-02-26 55 views
35

在我使用MVVM模式編寫的WPF應用程序中,我有一個後臺進程來完成這件事,但需要從中獲取狀態更新到用戶界面。我正在使用MVVM模式,所以我的ViewModel幾乎不知道向用戶展示模型的視圖(UI)。確保在MVVM WPF應用程序中的UI線程上調用OnPropertyChanged()

說我有以下的方法在我的視圖模型:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) 
{ 
    this.Messages.Add(e.Message); 
    OnPropertyChanged("Messages"); 
} 

我認爲,我有綁定到視圖模型的消息屬性(一個List<string>)列表框。 OnPropertyChanged通過調用PropertyChangedEventHandler履行INotifyPropertyChanged接口的作用。

我需要確保在UI線程上調用OnPropertyChanged - 我該怎麼做?我已經試過如下:

public Dispatcher Dispatcher { get; set; } 
public MyViewModel() 
{ 
    this.Dispatcher = Dispatcher.CurrentDispatcher; 
} 

然後添加以下到OnPropertyChanged方法:

if (this.Dispatcher != Dispatcher.CurrentDispatcher) 
{ 
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate 
    { 
     OnPropertyChanged(propertyName); 
    })); 
    return; 
} 

但這並沒有工作。有任何想法嗎?

回答

33

WPF自動將屬性更改編組到UI線程。但是,它不編組收集更改,所以我懷疑您添加一條消息導致失敗。

您可以自己手動編組添加(請參閱下面的示例),或者使用this technique之類的內容。

手動編組:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) 
{ 
    Dispatcher.Invoke(new Action<string>(AddMessage), e.Message); 
    OnPropertyChanged("Messages"); 
} 

private void AddMessage(string message) 
{ 
    Dispatcher.VerifyAccess(); 
    Messages.Add(message); 
} 
+0

也做到了,與另外一個小的修改 - 我改變了我的列表到ObservableCollction 和它的作品就像一個魅力。謝謝! – 2009-02-26 14:04:07

3

我剛剛在本週類似的情景(MVVM這裏太)。我有一個單獨的類來完成它的事情,報告事件處理程序的狀態。事件處理程序按預期方式被調用,並且我可以看到使用Debug.WriteLine的及時返回的結果。

但是對於WPF,不管我做了什麼,UI都不會更新,直到過程完成。該過程完成後,UI將按預期進行更新。就好像它正在獲取PropertyChanged一樣,但是在一次完成UI更新之前等待線程完成。

(令我失望的是,在Windows.Forms的相同的代碼與調用DoEvents和.REFRESH()的工作就像一個魅力。)

到目前爲止,我已經開始在自己的過程中解決了這一螺紋:

//hook up event handler 
myProcess.MyEvent += new EventHandler<MyEventArgs>(MyEventHandler); 

//start it on a thread ... 
ThreadStart threadStart = new ThreadStart(myProcess.Start); 

Thread thread = new Thread(threadStart); 

thread.Start(); 

,然後在事件處理程序:

private void MyEventHandler(object sender, MyEventArgs e) { 
.... 
Application.Current.Dispatcher.Invoke(
       DispatcherPriority.Send, 
       (DispatcherOperationCallback)(arg => 
       { 
     //do UI updating here ... 
     }), null); 

我不建議這個代碼,因爲我仍然在試圖瞭解WPF線程模型,如何調度工作,並WH y在我的情況下,即使事件處理程序按預期調用(按設計?),直到該過程完成後,UI纔會更新。但到目前爲止,這對我來說已經奏效。

我發現這兩個環節有所幫助:

http://www.nbdtech.com/blog/archive/2007/08/01/Passing-Wpf-Objects-Between-Threads-With-Source-Code.aspx

http://srtsolutions.com/blogs/mikewoelmer/archive/2009/04/17/dealing-with-unhandled-exceptions-in-wpf.aspx

0

我處理BackgroundWorker.ReportProgress事件我的視圖模型的外面,並通過實際的BackgroundWorker的實例和視圖模型在我的類定義異步方法。

異步方法然後調用bgWorker.ReportProgress並傳遞一個包裝委託作爲userstate(作爲對象)的類。我以匿名方式撰寫的代表。

在事件處理程序中,我將它從對象轉換回包裝類型,然後調用其中的委託。

所有這一切意味着我可以直接從異步運行的代碼編寫UI更改,但它只是圍繞它進行包裝。

此詳細介紹了:

http://lukepuplett.blogspot.com/2009/05/updating-ui-from-asynchronous-ops.html

8

我真的很喜歡傑里米的答案: Dispatching In Silverlight

摘要:

  • 在視圖模型放置調度似乎不雅

  • 創建操作<行動>屬性,將其設置爲只運行在虛擬機的構造

  • 當從V使用虛擬機的操作,設置Action屬性來調用調度
0

這是更延伸到接受的答案,但我這樣做是我的事件處理程序...

using System.Threading; 

private void Handler(object sender, RoutedEventArgs e) 
{ 
    if (Thread.CurrentThread == this.Dispatcher.Thread) 
    { 
     //do stuff to this 
    } 
    else 
    { 
     this.Dispatcher.Invoke(
      new Action<object, RoutedEventArgs>(Handler), 
      sender, 
      e); 
    } 
}