刷新WPF命令

2jcobegt  于 2023-03-09  发布在  其他
关注(0)|答案(7)|浏览(120)

有人知道如何强制CanExecute在自定义命令(Josh Smith的RelayCommand)上被调用吗?
通常,每当UI上发生交互时都会调用CanExecute,如果我单击某个对象,我的命令就会更新。
我遇到过这样的情况:CanExecute的条件由一个计时器在幕后打开/关闭。因为这不是由用户交互驱动的,所以CanExecute在用户与UI交互之前不会被调用。最终结果是,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; }
uinbv5nw

uinbv5nw1#

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

**备注:**CommandManager在确定命令目标何时更改时只注意某些条件,如键盘焦点的更改。如果CommandManager无法充分确定导致命令无法执行的条件更改,则可以调用InvalidateRequestSugested以强制CommandManager引发RequestSugested事件。

l3zydbqr

l3zydbqr2#

很久以前我就知道CommandManager. InvalidateRequestySuggested(),并且使用了它,但是有时候它对我不起作用。我终于明白为什么会这样了!尽管它不像其他操作那样抛出,但你必须在主线程上调用它。
在后台线程上调用它看起来会起作用,但有时会禁用UI。我真的希望这能帮助一些人,并为他们节省我刚刚浪费的时间。

deikduxw

deikduxw3#

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

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

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

6kkfgxo0

6kkfgxo04#

可能这个变体适合您:

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

t8e9dugd5#

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

private SynchronizationContext syncCtx; // member variable

在构造函数中:

syncCtx = SynchronizationContext.Current;

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

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

希望能有所帮助。
--迈克尔

6rqinv9w

6rqinv9w6#

要仅更新单个GalaSoft.MvvmLight.CommandWpf.RelayCommand,您可以使用

mycommand.RaiseCanExecuteChanged();

我自己创建了一个Extension方法

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(); }));
        }
    }
nom7f22z

nom7f22z7#

没有看到这里提到的,所以在2023年添加。
我通过NuGet安装Prism库解决了这个问题(VS建议我用它来安装“DelegateCommand”),并使用DelegateCommand而不是自己的RelayCommand实现。然后我用调用OnPropertyChanged(propName)的方法调用command.RaiseCanExecuteChanged()。对我来说效果很好。

相关问题