2010-04-27 72 views
0

在我的代碼中,我訂閱了發生在另一個線程上的事件。每到這個事件發生時,我收到投遞到觀察集合的字符串:ObservableCollection上的BeginInvoke不立即

Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher; 
    var SerialLog = new ObservableCollection<string>(); 

    private void hitStation_RawCommandSent(object sender, StringEventArgs e) 
    { 
     string command = e.Value.Replace("\r\n", ""); 
     Action dispatchAction =() => SerialLog.Add(command); 
     currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Render); 
    } 

下面的代碼是在我的視圖模型(可以在後面的代碼,它不會在這種情況下重要) 。當我打電話給「hitstation.PrepareHit」時,上面的事件被調用了幾次,然後我等待並調用「hitStation.HitBall」,並且上面的事件被調用了幾次。

private void HitBall() 
    { 
     try 
     { 
      try 
      { 
       Mouse.OverrideCursor = Cursors.Wait; 

       //prepare hit 
       hitStation.PrepareHit(hitSpeed); 

       Thread.Wait(1000); 
       PlayWarning(); 

       //hit 
       hitStation.HitBall(hitSpeed); 
      } 
      catch (TimeoutException ex) 
      { 
       MessageBox.Show("Timeout hitting ball: " + ex.Message); 
      } 
     } 
     finally 
     { 
      Mouse.OverrideCursor = null; 
     } 
    } 

我遇到的問題是,只有當HitBall方法完成綁定到我的SerialLog ListBox中得到更新。我期待着看到來自PrepareHit的一堆更新,一個暫停,然後來自HitBall的更多更新。

我已經嘗試了幾個DispatcherPriority參數,但它們似乎沒有任何效果。

回答

4

我想你是在阻止自己。

UI線程正在等待Thread.Wait,BeginInvoke將操作發送到dipatcher,但UI線程正忙於等待。這就是爲什麼UI更新只在HitBall完成後完成的原因,也就是說,當UI線程完成處理時。

要解決這個問題,你應該在另一個線程啓動HitBall方法的代碼,釋放UI:

private void HitBall() 
{ 
    try { 

     Mouse.OverrideCursor = Cursors.Wait; 
     Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 

     Action hitOperations =() => 
     { 
      hitStation.PrepareHit(hitSpeed); 

      Thread.Wait(1000); 

      //Only needed if PlayWarning needs access to UI Thread 
      dispatcher.Invoke(() => PlayWarning()); 

      hitStation.HitBall(hitSpeed); 

      dispatcher.Invoke(() => Mouse.OverrideCursor = null); 
      }; 

      //Free the UI from work, start operations in a background thread 
      hitOperations.BeginInvoke(null, null); 

     } 
     catch (TimeoutException ex) 
     { 
      MessageBox.Show("Timeout hitting ball: " + ex.Message); 
     } 
} 

此外,如果使用目的Thread.Wait(1000)的是等待事件刷新用戶界面,這個實現不再需要。

+0

好的,工作!但現在我感到困惑。我嘗試了同樣的事情,但不是使用Action和begininvoke,而是創建並運行了一個任務,但結果並不相同......爲什麼? – 2010-05-20 05:42:26

+0

對不起,你是如何創建任務的? 請記住,只使用一個任務,您不會與UI線程同步,但通過調度程序上的Invoke和BeginInvoke,您將明確地與UI同步。使用BeginInvoke可以以異步方式執行。 – 2010-06-01 06:01:48

0

除非存在上下文切換,並且不能保證上下文切換將發生(除非您阻止當前線程並且會增加發生上下文切換的可能性),否則您可能不會看到來自PrepareHit的更新。

正如iCe提到,如果你在UI線程上這樣做,那麼你可能會阻止你的用戶界面。當你致電Thread.Wait時,你的用戶界面是否被阻擋/凍結?如果沒有則繼續閱讀以下:

更新:
我想不出任何東西,不會妥協併發...不影響併發你可以嘗試增加BeginInvoke優先級:http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Send); 

順便說一句,它看起來像Render優先級低於Normal優先級:

渲染枚舉值爲7. 作爲渲染處理的優先級爲 。

DataBind 枚舉值是8.操作是 處理與數據 綁定相同的優先級。

正常枚舉值 是9.操作處理在 正常優先級。這是典型的 應用程序優先級。

發送 枚舉值爲10.操作 之前處理其他 異步操作。這是最高優先級的 。

+0

是否有替代解決方案,我可以在他們到達時看到添加到列表中的項目? – 2010-04-27 22:39:59

+0

@Padu,嘗試增加BeginInvoke – Kiril 2010-04-27 22:59:31

+0

剛剛嘗試的優先級,結果相同。明天我會嘗試iCe的建議 – 2010-04-28 03:16:26

1

難道這需要在ViewModel上引發PropertyChanged事件嗎?

+0

我以爲你不需要爲ObservableCollections提出這個事件......我錯了嗎? – 2010-04-28 18:00:39

+0

如果什麼是changins是ObservableCollecction中的元素的屬性,該元素需要實現INotifyPropertyChanged並引發該事件。提取自: http://msdn.microsoft.com/en-us/magazine/dd252944.aspx 「如果您需要知道某人是否更改了集合中某個項目的屬性,那麼您將需要確保集合中的項目實現INotifyPropertyChanged接口,並且您需要手動爲這些對象附加屬性已更改的事件處理程序「 – 2010-04-29 05:59:31