有谁知道如何强制CanExecute
调用自定义命令(Josh Smith的RelayCommand
)?
通常,只要在UI上发生交互,就会调用CanExecute
。如果我点击某些内容,我的命令会更新。
我的情况是CanExecute
的条件被幕后的计时器打开/关闭。由于这不是由用户交互驱动的,因此在用户与UI交互之前不会调用CanExecute
。最终结果是我的Button
保持启用/禁用,直到用户点击它为止。点击后,它会正确更新。有时Button
会启用,但是当用户点击它时,会更改为禁用而不是触发。
当计时器更改影响CanExecute
的属性时,如何强制更新代码?我尝试在影响PropertyChanged
的属性上触发INotifyPropertyChanged
(CanExecute
),但这没有帮助。
示例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 :(得分:101)
答案 1 :(得分:28)
我很久以前就知道了CommandManager.InvalidateRequerySuggested(),并且使用过它,但它有时对我不起作用。我终于弄清楚为什么会这样!即使它不像其他一些动作那样抛出,你也必须在主线程上调用它。
在后台线程上调用它似乎可行,但有时会禁用UI。我真的希望这可以帮助某人,并为他们节省我浪费的时间。
答案 2 :(得分:17)
解决方法是将IsEnabled
绑定到属性:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
然后在ViewModel中实现此属性。这也使得UnitTesting更容易使用属性而不是命令来查看命令是否可以在某个时间点执行。
我个人认为更方便。
答案 3 :(得分:6)
这个版本可能适合你:
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}"/>
答案 4 :(得分:4)
谢谢大家的提示。这里有一些关于如何编组从BG线程到UI线程的调用的代码:
private SynchronizationContext syncCtx; // member variable
在构造函数中:
syncCtx = SynchronizationContext.Current;
在后台线程上,触发重新查询:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
希望有所帮助。
- 迈克尔
答案 5 :(得分:0)
要仅更新一个GalaSoft.MvvmLight.CommandWpf.RelayCommand,可以使用
mycommand.RaiseCanExecuteChanged();
对我来说,我创建了一个扩展方法:
public static class ExtensionMethods
{
public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
}