相互に依存する2つのプロパティを検証する方法は?


8

私は2つのプロパティを持つビューモデルを持っていますAし、B私はそれを検証しますA < B

以下は、カスタム検証ルールを使用する簡単な実装です。各プロパティは個別に検証されるため、厄介な問題Aが発生します。入力された値が無効な場合B、の検証はBについて何も知らないため、変更後もそのままですA

これはこのデモで見ることができます:

Aを入力した後は無効です。11それ以降は正しい11 > 2です。に変更B22ても再評価されません。検証に合格AするAには編集する必要があります。

私が欲しいものは?赤い境界線を確認22Bた後(検証エラー)が消えA = 11, B = 22、ビューモデルのソース値になることを望みます。

新しい値がソースと同期された後、B検証でどうやって検証を強制することができますか?AB


モデルを見る:

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

2
OnPropertyChanged(nameof(B))aのセッターA(およびのセッターの同等物B)も呼び出す場合に機能しますか?
Dirk、

1
Validationruleは、単純なユースケースにのみ適しています。VM内のinotifydataerrorinfoは、実質的なものに対する通常のアプローチです。流暢な検証を見てください。イントロの例:gist.github.com/holymoo/11243164 fluentvalidation.net/built-in-validators オフハンド-両方のプロパティはおそらく同じルールを持ち、述語チェックで両方が良いでしょう。
アンディ

1
モデルを直接公開する場合のみ関連します。モデルを直接公開しないでください。viewmodelsを使用します。有効なデータのみをコミットします。
アンディ、

1
@Sinatr:「検証ルールの概念の方が正しいと思う」は誤りです。INotifyDataErrorInfoこの種の検証を実行する場合は、ビューモデルに実装する必要があります。検証ルールではサポートされていません。
mm8

1
@Rekshino、そう、私はいつ検証Aするかについても考えていました:検証の前または後B(つまり、Bの値が受け入れられてソースと同期される前または後)。注文事項。実際、最初にすべての変更された値を手元に置いて、通常の順序で検証を実行する必要があります。
Sinatr

回答:


6

ValidationRules 別のプロパティを設定するときのプロパティの無効化はサポートしていません。

すべきことはINotifyDataErrorInfo、ビューモデルに実装しErrorsChanged、プロパティの検証ステータスを更新するたびにイベントを発生させることです。

このTechNetの記事で使用可能な例があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.