Como validar duas propriedades que dependem uma da outra?

8

Tenho modelo de vista com 2 propriedades: Ae Be quero validar que A < B.

Abaixo está minha implementação simplificada, onde eu uso a regra de validação personalizada. Como cada propriedade é validada de forma independente, isso gera um problema irritante: se o Avalor digitado é inválido, permanece assim mesmo após a alteração B, pois a validação de Bnão sabe nada sobre A.

Isso pode ser visto nesta demonstração:

Aé inválido após a entrada 11, está correto desde então 11 > 2. Alterar Bpara 22não reavaliar A, preciso editar Apara que a validação seja aprovada.

O que eu quero? Eu quero que depois de entrar 22na Bborda vermelha (erro de validação) desapareça e A = 11, B = 22sejam valores de origem no modelo de exibição.

Como na Bvalidação, de alguma forma, forço a Avalidação depois que um novo Bvalor é sincronizado com a fonte?


Ver modelo:

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

Visão:

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

Ver código:

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

Obrigatório:

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

Regra de validação:

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);
    }
}
Sinatr
fonte
2
Funciona se você também chamar OnPropertyChanged(nameof(B))o levantador de a A(e o equivalente para o levantador de B)?
Dirk
1
A regra de validação é realmente adequada apenas para casos de uso simples. inotifydataerrorinfo na vm é a abordagem usual para qualquer coisa substancial. Dê uma olhada na validação fluente. Exemplo de introdução: gist.github.com/holymoo/11243164 fluentvalidation.net/built-in-validators Offhand - ambas as propriedades provavelmente teriam a mesma regra com uma verificação de predicado e ambas são boas.
Andy
1
Somente relevante se você expor diretamente um modelo. Não exponha diretamente um modelo. Use viewmodels. Confirme apenas dados válidos.
Andy
1
@ Sinatr: "Acho o conceito de regra de validação mais correto" está errado. Você deve implementar INotifyDataErrorInfono seu modelo de visualização se desejar executar esse tipo de validação. Não é suportado por regras de validação.
MM8
1
@ Rekshino, sim, eu também estava pensando quando validar A: antes ou depois da validação B(em outras palavras, antes que o valor de B seja aceito e sincronizado com a fonte ou depois). Assunto da ordem. E, de fato, primeiro eu tenho que ter todos os valores modificados em mãos e só então fazer a validação na ordem normal.
Sinatr 16/10/19

Respostas:

6

ValidationRules não suporta a invalidação de uma propriedade ao definir outra propriedade.

O que você deve fazer é implementar INotifyDataErrorInfoem seu modelo de visualização e gerar o ErrorsChangedevento sempre que desejar atualizar o status de validação de uma propriedade.

Há um exemplo disponível neste artigo do TechNet .

mm8
fonte