【问题标题】:How does NotifyOnValidationError end up calling CanExecute (and why doesn't it work with MVVM Light RelayCommand)NotifyOnValidationError 如何最终调用 CanExecute(以及为什么它不能与 MVVM Light RelayCommand 一起使用)
【发布时间】:2016-08-14 14:49:05
【问题描述】:

也许是有史以来最长的问题标题!因为这是一个两部分的问题。

(1) 我不明白设置 NotifyOnValidationError="True" 如何触发对我的 CanExecute 的更新。这里有一点我需要理解的魔法。有人(事物)订阅了我的 ICommand 的 CanExecuteChanged 事件,但调用堆栈指向外部代码,所以我不知道发生了什么。

(2) 也许最重要的后续问题是:为什么它在 MVVM Light RelayCommand 中不起作用! CanExecute 仅在初始化时调用一次,然后再也不调用。与我自己的实现相比,查看 MVVM Light 中 RelayCommand 的源代码并没有发现任何令人窒息的差异。我应该提到 Prism 的 DelegateCommand 似乎也不起作用。

(奖励)也许我以错误的方式处理这个问题?我基本上只是想根据验证失败启用/禁用按钮。

XAML (sn-p):

    <TextBox Grid.Column="1" Grid.Row="0">
        <Binding Path="X" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
            <Binding.ValidationRules>
                <ExceptionValidationRule></ExceptionValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox>

    <Button Grid.Column="1" Grid.Row="3" Command="{Binding CalculateCommand}">
        Calculate
    </Button>

中继命令:

public class MyRelayCommand : ICommand
{
    readonly Action<object> Execute_;
    readonly Predicate<object> CanExecute_;

    public MyRelayCommand(Action<object> Execute, Predicate<object> CanExecute)
    {
        if (Execute == null)
            throw new ArgumentNullException("No action to execute for this command.");

        Execute_ = Execute;
        CanExecute_ = CanExecute;
    }

    public bool CanExecute(object parameter)
    {
        return (CanExecute_ == null) ? true : CanExecute_(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        Execute_(parameter);
    }
}

视图模型:

    private DelegateCommand _calculateCommmand;
    public DelegateCommand CalculateCommand
    {
        get
        {
            return _calculateCommmand ?? (_calculateCommmand = new DelegateCommand(
            () =>
            {
                Sum = X + X;
            },
            () =>
            {
                try
                {
                    Convert.ChangeType(X, TypeCode.Byte);
                    return true;
                }
                catch
                {
                    return false;
                }
            }));
        }
    }

PS:如果你想在完成后购买我的 X + X 程序,请发送电子邮件至 sales@xplusx.com

【问题讨论】:

    标签: wpf mvvm prism mvvm-light


    【解决方案1】:

    (1) 我认为是这样的。

    1. 当我们将 RelayCommand : ICommand 绑定到 Button.Command 时,绑定过程还会将事件处理程序附加到 ICommand.CanExecuteChanged。这是默认行为。
    2. 传递给 CanExecutedChanged 的​​事件处理程序将被传递并附加到静态事件 CommandManager.RequerySuggested。
    3. 当发生验证错误并设置 NotifyOnValidationError 时,外力或绝地武士将引发 RequerySuggested 事件,该事件将广播到所有活动命令。
    4. Button 接收到该事件并因此调用 CanExecute 以了解它是否应该禁用/启用该按钮。

    我想了解更多关于上面第三个要点的信息,所以我会将这个问题留待更长时间,让专家们有机会插话。

    【讨论】:

      【解决方案2】:

      (2) 这个是我自己想出来的。您可以选择包含来自两个不同命名空间的 RelayCommand,请确保使用

      using GalaSoft.MvvmLight.CommandWpf;
      

      我仍在寻找 (1) 的良好答案,即管道如何根据验证错误引发 CanExecutetChanged。

      【讨论】:

        【解决方案3】:

        我不认为这取决于ICommand 的实现。在你的代码中,我看到了一个public event EventHandler CanExecuteChanged,你告诉CommandManager 来处理你的命令的CanExecute() 方法的调用。如果没有 CommandManager,您将不得不自己处理这个问题,例如通过为您的 ICommand 实现提供一个 public void RaiseCanExecuteChanged() 方法,您的 ViewModel 必须为它认为需要重新计算的每个命令调用该方法,例如在 ViewModel 的 OnPropertyChanged 内。示例:https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right

        因此,CommandManager 为您施展魔法。只要您调用 ViewModel 的 PropertyChanged 事件,“外部代码”就会处理受影响的命令并要求它们提供新的 CanExecute() 值。

        【讨论】:

        • 我应该指出,我的实现是 RelayCommand (MVVM Light) 源代码的精简变体,可在此处找到:mvvmlight.codeplex.com/SourceControl/latest#GalaSoft.MvvmLight/… (PCL)/Command/RelayCommand.cs 如您所见它还使用 CommandManager。
        • 这就是我想告诉你的。您所要求的魔法由 CommandManager 完成。我假设您的 ViewModel 实现了INotifyPropertyChanged,因此必须有一个可以调用的event PropertyChangedEventHandler PropertyChanged:PropertyChanged?.Invoke()。这告诉命令管理器它需要再次调用你的命令的CanExecute()。试试看!
        猜你喜欢
        • 2011-10-08
        • 1970-01-01
        • 1970-01-01
        • 2011-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-18
        相关资源
        最近更新 更多