74

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

通常,CanExecute在 UI 上发生交互时调用。如果我单击某些东西,我的命令就会更新。

我有一种情况,条件CanExecute是由幕后的计时器打开/关闭。因为这不是由用户交互驱动的,CanExecute所以在用户与 UI 交互之前不会调用。最终结果是我的Button保持启用/禁用状态,直到用户点击它。单击后,它已正确更新。有时Button显示已启用,但当用户单击时,它会变为禁用而不是触发。

当计时器更改影响的属性时,如何强制更新代码CanExecute?我尝试在影响的属性上触发PropertyChanged( ) ,但这没有帮助。INotifyPropertyChangedCanExecute

示例 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; }
4

6 回答 6

104

调用System.Windows.Input.CommandManager.InvalidateRequerySuggested()会强制 CommandManager 引发 RequerySuggested 事件。

备注: CommandManager 在确定命令目标何时发生变化时只关注某些条件,例如键盘焦点的变化。在 CommandManager 无法充分确定导致命令无法执行的条件变化的情况下,可以调用 InvalidateRequerySuggested 以强制 CommandManager 引发 RequerySuggested 事件。

于 2009-04-23T19:08:44.070 回答
29

我很久以前就知道 CommandManager.InvalidateRequerySuggested() 并使用它,但有时它对我不起作用。我终于明白为什么会这样了!即使它不像其他一些动作那样抛出,你必须在主线程上调用它。

在后台线程上调用它似乎可以工作,但有时会禁用 UI。我真的希望这对某人有所帮助,并为他们节省我刚刚浪费的时间。

于 2010-02-23T15:30:01.500 回答
17

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

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

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

个人觉得比较方便。

于 2012-05-04T08:10:46.070 回答
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}"/>
于 2012-12-05T08:58:56.197 回答
4

谢谢大家的提示。下面是一些关于如何将 BG 线程的调用编组到 UI 线程的代码:

private SynchronizationContext syncCtx; // member variable

在构造函数中:

syncCtx = SynchronizationContext.Current;

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

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

希望有帮助。

——迈克尔

于 2011-03-03T19:45:57.393 回答
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(); }));
        }
    }
于 2018-06-27T17:38:54.887 回答