刷新WPF命令

有谁知道我可以如何强制CanExecute调用自定义命令(Josh Smith的RelayCommand )?

通常,只要在UI上进行交互,就会调用CanExecute 。 如果我点击一下,我的命令就会更新。

我有一个情况, CanExecute的条件是由幕后计时器打开/closures的。 因为这不是由用户交互驱动的, 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; } 

CommandManager.InvalidateRequerySuggested()

我很早以前就知道了CommandManager.InvalidateRequerySuggested(),并且使用它,但有时候它并不适合我。 我终于明白为什么这样了! 即使它不像其他一些动作,你必须在主线程中调用它。

在后台线程上调用它将显示工作,但有时会禁用UI。 我真的希望这能帮助别人,并为他们节省我浪费的时间。

解决方法是将IsEnabled绑定到一个属性:

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

然后在ViewModel中实现这个属性。 这也使得UnitTesting更容易使用属性而不是命令来查看命令是否可以在某个时间点执行。

我个人认为它更方便。

也许这个变种会适合你:

  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}"/> 

谢谢你们的提示。 以下是关于如何将从BG线程调用到UI线程的一些代码:

 private SynchronizationContext syncCtx; // member variable 

在构造函数中:

 syncCtx = SynchronizationContext.Current; 

在后台线程上,触发查询:

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

希望有所帮助。

迈克尔