刷新 WPF 命令

有人知道我如何强制 CanExecute在自定义命令(Josh Smith 的 RelayCommand)中被调用吗?

通常,只要 UI 上发生交互,就会调用 CanExecute。如果我单击某些内容,我的命令就会更新。

我遇到了这样一种情况,即 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; }
53559 次浏览

调用 System.Windows.Input.CommandManager.InvalidateRequerySuggested()会强制 CommandManager 引发 Requery  ㄆ。

备注: CommandManager 在确定命令目标何时更改时只关注某些条件,例如键盘焦点的更改。在 CommandManager 不能充分确定导致命令无法执行的条件更改的情况下,可以调用 InvalidateRequery 以强制 CommandManager 引发 Requery 。

我知道指挥官经理。很久以前就建议()了,并且使用了它,但是有时候它对我不起作用。我终于知道为什么会这样了!即使它不像其他操作那样抛出,您也必须在主线程上调用它。

在后台线程上调用它似乎可以工作,但有时会禁用 UI。我真心希望这能帮到别人,让他们省下我浪费的时间。

谢谢你们的建议。下面是一些关于如何将调用从 BG 线程编组到 UI 线程的代码:

private SynchronizationContext syncCtx; // member variable

在构造函数中:

syncCtx = SynchronizationContext.Current;

在后台线程上,触发请求:

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

希望能帮上忙。

—— Michael

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

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

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

我个人觉得这样更方便。

也许这个变体会适合你:

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

只更新一个 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(); }));
}
}