2013-02-25 76 views
1

在我的應用程序中,我有一個命令,我只希望用戶能夠觸發,如果它尚未運行。有問題的命令綁定到WPF按鈕,這意味着如果CanExecute爲false,它會自動禁用該按鈕。到現在爲止還挺好。如何從另一個線程更新命令的「CanExecute」值?

不幸的是,由命令執行的操作是長時間運行的,所以它需要發生在不同的線程上。我不認爲這會是一個問題......但似乎是這樣。

我已經提取出了一個可以顯示問題的最小樣本。如果綁定到一個按鈕(通過LocalCommands.Problem靜態引用),該按鈕將根據需要被禁用。當工作線程嘗試更新CanExecute時,InvalidOperationException將從System.Windows.Controls.Primitives.ButtonBase中拋出。

什麼是解決這個問題的最合適的方法?

命令示例下面的代碼:

using System; 
using System.Threading; 
using System.Windows.Input; 

namespace InvalidOperationDemo 
{ 
    static class LocalCommands 
    { 
     public static ProblemCommand Problem = new ProblemCommand(); 
    } 

    class ProblemCommand : ICommand 
    { 
     private bool currentlyRunning = false; 
     private AutoResetEvent synchronize = new AutoResetEvent(false); 

     public bool CanExecute(object parameter) 
     { 
      return !CurrentlyRunning; 
     } 

     public void Execute(object parameter) 
     { 
      CurrentlyRunning = true; 

      ThreadPool.QueueUserWorkItem(ShowProblem); 
     } 

     private void ShowProblem(object state) 
     { 
      // Do some work here. When we're done, set CurrentlyRunning back to false. 
      // To simulate the problem, wait on the never-set synchronization object. 
      synchronize.WaitOne(500); 

      CurrentlyRunning = false; 
     } 

     public bool CurrentlyRunning 
     { 
      get { return currentlyRunning; } 
      private set 
      { 
       if (currentlyRunning == value) return; 

       currentlyRunning = value; 

       var onCanExecuteChanged = CanExecuteChanged; 
       if (onCanExecuteChanged != null) 
       { 
        try 
        { 
         onCanExecuteChanged(this, EventArgs.Empty); 
        } 
        catch (Exception e) 
        { 
         System.Windows.MessageBox.Show(e.Message, "Exception in event handling."); 
        } 
       } 
      } 
     } 

     public event EventHandler CanExecuteChanged; 
    } 
} 

回答

4

變化:

onCanExecuteChanged(this, EventArgs.Empty);

到:

Application.Current.Dispatcher.BeginInvoke((Action)(onCanExecuteChanged(this, EventArgs.Empty))); 

編輯:

原因是WPF正在監聽這些事件並嘗試在UI元素中執行操作(I.E在Button中切換IsEnabled),因此必須在UI線程中引發這些事件。

+0

雖然這毫無疑問的工作,你能澄清爲什麼它是必要的嗎?我(顯然不正確)的假設是任何與UI綁定關聯的事件的處理程序都應該已經在檢查他們試圖在正確的線程上執行UI更新。 – 2013-02-25 20:33:39

+0

是的,它檢查它是在正確的線程,並拋出一個異常讓你知道。 – user7116 2013-02-25 20:58:36

+0

我想我誤解了@sixlettervariables(並感謝澄清@HighCore) - 我知道異常讓我知道這個問題。我不明白的是,爲什麼這與我認爲將「啓用」綁定到視圖模型屬性的幾乎完全相同的場景不同。我沒有遇到任何類似的問題,它不是CanExecuteChanged,而是導致更改的OnPropertyChanged事件。我擔心的是,到目前爲止,我只是很幸運,並且如果在綁定上下文中使用OnPropertyChange事件,也只需要在UI線程上發生。 – 2013-02-26 15:39:31