刷新WPF命令
有谁知道我可以如何强制CanExecute
调用自定义命令(Josh Smith的RelayCommand
)?
通常,只要在UI上进行交互,就会调用CanExecute
。 如果我点击一下,我的命令就会更新。
我有一个情况, CanExecute
的条件是由幕后计时器打开/closures的。 因为这不是由用户交互驱动的, CanExecute
在用户与UI交互之前不会调用CanExecute
。 最终的结果是,我的Button
保持启用/禁用,直到用户点击它。 点击后,它正确更新。 有时Button
显示为启用,但是当用户点击时,它变为禁用而不是触发。
当计时器更改影响CanExecute
的属性时,如何强制更新代码? 我试图射击影响CanExecute
属性的PropertyChanged
( INotifyPropertyChanged
),但没有帮助。
示例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 );
希望有所帮助。
迈克尔