2009-04-23 143 views
64

有誰知道我可以如何強制CanExecute調用一個自定義命令(約什史密斯的RelayCommand)?刷新WPF命令

通常,只要在UI上發生交互,就會調用CanExecute。如果我點擊了某些東西,我的命令就會更新。

我有一種情況,CanExecute的條件被幕後計時器打開/關閉。因爲這不是由用戶交互驅動的,所以在用戶與UI交互之前不會調用CanExecute。最終的結果是,我的Button保持啓用/禁用,直到用戶點擊它。點擊後,它會正確更新。有時會啓用Button,但當用戶點擊時,它會變爲禁用而不是觸發。

當計時器更改影響CanExecute的屬性時,如何強制更新代碼?我試圖解決影響CanExecute的財產PropertyChangedINotifyPropertyChanged),但這並沒有幫助。

例XAML:

<Button Content="Button" Command="{Binding Cmd}"/> 

示例代碼背後:

private ICommand m_cmd; 
public ICommand Cmd 
{ 
    if (m_cmd == null) 
     m_cmd = new RelayCommand(
      (param) => Process(), 
      (param) => EnableButton); 

    return m_cmd; 
} 

// Gets updated from a timer (not direct user interaction) 
public bool EnableButton { get; set; } 
+0

您是否嘗試爲命令提出INotifyPropertyChanged?你不需要爲Command指定一個字段,每次只需要返回一個新字段。這種組合應該可行。或者只在需要強制的情況下創建新的命令。 – egaga 2013-02-15 17:03:52

回答

98
+1

你是否建議從ViewModel類調用它? – 2009-04-23 19:10:59

+2

不一定,因爲這可能會讓你的課難以測試。嘗試一下,並在必要時將其轉移到服務中。另一種方法是在RelayCommand中添加一個方法,該方法允許您爲該命令引發CanExecuteChanged(CommandManager.InvalidRequerySuggested使所有命令無效,這有點矯枉過正)。 – 2009-04-23 19:15:49

+23

有趣......它的工作原理,但它必須在UI線程上調用。我不驚訝。 – 2009-04-23 19:16:03

27

我知道CommandManager.InvalidateRequerySuggested()的很久以前,並用它,但它有時候我不適合我。我終於明白爲什麼會是這種情況!儘管它不會像其他一些操作一樣,但您必須在主線程中調用它。

在後臺線程上調用它將顯示工作,但有時會禁用UI。我真的希望這能幫助別人,併爲他們節省我浪費的時間。

4

謝謝你們的提示。下面是關於如何在呼叫元帥從BG線程UI線程一些代碼:

private SynchronizationContext syncCtx; // member variable 

在構造函數中:

syncCtx = SynchronizationContext.Current; 

在後臺線程,觸發重新查詢:

syncCtx.Post(delegate { CommandManager.InvalidateRequerySuggested(); }, null); 

希望有所幫助。

邁克爾 -

14

,一種在解決方法是結合IsEnabled一個屬性:

<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/> 

,然後實現在您的視圖模型此屬性。這也使得UnitTesting更容易處理屬性而不是命令,以查看命令是否可以在某個時間點執行。

我個人認爲它更方便。

5

也許這變種會適合你:

public interface IRelayCommand : ICommand 
{ 
    void UpdateCanExecuteState(); 
} 

實現:

public class RelayCommand : IRelayCommand 
{ 
    public event EventHandler CanExecuteChanged; 


    readonly Predicate<Object> _canExecute = null; 
    readonly Action<Object> _executeAction = null; 

    public RelayCommand(Action<object> executeAction,Predicate<Object> canExecute = null) 
    { 
     _canExecute = canExecute; 
     _executeAction = executeAction; 
    } 


    public bool CanExecute(object parameter) 
    { 
     if (_canExecute != null) 
      return _canExecute(parameter); 
     return true; 
    } 

    public void UpdateCanExecuteState() 
    { 
     if (CanExecuteChanged != null) 
      CanExecuteChanged(this, new EventArgs()); 
    } 



    public void Execute(object parameter) 
    { 
     if (_executeAction != null) 
      _executeAction(parameter); 
     UpdateCanExecuteState(); 
    } 
} 

使用簡單:

public IRelayCommand EditCommand { get; protected set; } 
... 
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted); 

protected override bool CanEditCommandExecuted(object obj) 
    { 
     return SelectedItem != null ; 
    } 

    protected override void EditCommandExecuted(object obj) 
    { 
     // Do something 
    } 

    ... 

    public TEntity SelectedItem 
    { 
     get { return _selectedItem; } 
     set 
     { 
      _selectedItem = value; 

      //Refresh can execute 
      EditCommand.UpdateCanExecuteState(); 

      RaisePropertyChanged(() => SelectedItem); 
     } 
    } 

XAML:

<Button Content="Edit" Command="{Binding EditCommand}"/>