【问题标题】:How to validate two properties which depend on each other?如何验证两个相互依赖的属性?
【发布时间】:2020-02-13 02:41:21
【问题描述】:

我有具有 2 个属性的视图模型:AB,我想验证 A < B

下面是我使用自定义验证规则的简化实现。由于每个属性都是独立验证的,因此会导致一个令人讨厌的问题:如果输入的 A 值无效,那么即使在更改 B 后它也会保持不变,因为 B 的验证不知道任何关于 A 的信息。

这可以在这个演示中看到:

A在输入11后无效,因为11 > 2是正确的。将B 更改为22 不会重新评估A,我必须编辑A 才能通过验证。

我想要什么?我希望在将22 转换为B 后,红色边框(验证错误)消失,A = 11, B = 22 将是视图模型中的源值。

在新的B 值与源同步后,我如何在B 验证中以某种方式强制A 验证?


查看模型:

public class ViewModel : INotifyPropertyChanged
{
    int _a;
    public int A
    {
        get => _a;
        set
        {
            _a = value;
            OnPropertyChanged();
        }
    }

    int _b;
    public int B
    {
        get => _b;
        set
        {
            _b = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public virtual void OnPropertyChanged([CallerMemberName] string property = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}

查看:

<StackPanel>
    <TextBox Margin="10" Text="{local:MyBinding A}" />
    <TextBox Margin="10" Text="{local:MyBinding B}" />
</StackPanel>

查看代码:

public MainWindow()
{
    InitializeComponent();
    DataContext = new ViewModel { A = 1, B = 2 };
}

绑定:

public class MyBinding : Binding
{
    public MyBinding(string path) : base(path)
    {
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        ValidationRules.Add(new MyValidationRule());
    }
}

验证规则:

public class MyValidationRule : ValidationRule
{
    public MyValidationRule() : base(ValidationStep.ConvertedProposedValue, false) { }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo) => ValidationResult.ValidResult; // not used

    public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingExpressionBase owner)
    {
        var binding = owner as BindingExpression;
        var vm = binding?.DataItem as ViewModel;
        switch (binding.ResolvedSourcePropertyName)
        {
            case nameof(vm.A):
                if ((int)value >= vm.B)
                    return new ValidationResult(false, "A should be smaller than B");
                break;
            case nameof(vm.B):
                if ((int)value <= vm.A)
                    return new ValidationResult(false, "B should be bigger than A");
                break;
        }
        return base.Validate(value, cultureInfo, owner);
    }
}

【问题讨论】:

  • 如果在A 的设置器中也调用OnPropertyChanged(nameof(B)) 是否有效(以及B 的设置器的等效项)?
  • Validationrule 只适用于简单的用例。 vm 中的 inotifydataerrorinfo 是处理任何实质性内容的常用方法。看看流利的验证。介绍示例:gist.github.com/holymoo/11243164fluentvalidation.net/built-in-validators Offhand - 两个属性可能具有相同的规则,谓词检查都很好。
  • 仅当您直接公开模型时才相关。不要直接暴露模型。使用视图模型。只提交有效数据。
  • @Sinatr:“我发现验证规则的概念更正确”是错误的。如果你想执行这种验证,你应该在你的视图模型中实现INotifyDataErrorInfo。验证规则不支持。
  • @Rekshino,是的,我也在考虑何时验证A:在验证B 之前或之后(换句话说,在B 的值被接受并与源同步之前或之后)。订购事宜。实际上,首先我必须准备好所有修改过的值,然后才能按正常顺序进行验证。

标签: c# wpf validation dependencies


【解决方案1】:

ValidationRules 不支持在设置另一个属性时使一个属性无效。

您应该做的是在您的视图模型中实现INotifyDataErrorInfo,并在您想要刷新属性的验证状态时引发ErrorsChanged 事件。

this TechNet article 中有一个示例。

【讨论】:

    猜你喜欢
    • 2020-01-18
    • 2018-08-11
    • 1970-01-01
    • 2012-12-29
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    相关资源
    最近更新 更多