Tenho modelo de vista com 2 propriedades: A
e B
e 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 A
valor digitado é inválido, permanece assim mesmo após a alteração B
, pois a validação de B
nã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 B
para 22
não reavaliar A
, preciso editar A
para que a validação seja aprovada.
O que eu quero? Eu quero que depois de entrar 22
na B
borda vermelha (erro de validação) desapareça e A = 11, B = 22
sejam valores de origem no modelo de exibição.
Como na B
validação, de alguma forma, forço a A
validação depois que um novo B
valor é 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);
}
}
fonte
OnPropertyChanged(nameof(B))
o levantador de aA
(e o equivalente para o levantador deB
)?INotifyDataErrorInfo
no seu modelo de visualização se desejar executar esse tipo de validação. Não é suportado por regras de validação.A
: antes ou depois da validaçãoB
(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.Respostas:
ValidationRules
não suporta a invalidação de uma propriedade ao definir outra propriedade.O que você deve fazer é implementar
INotifyDataErrorInfo
em seu modelo de visualização e gerar oErrorsChanged
evento sempre que desejar atualizar o status de validação de uma propriedade.Há um exemplo disponível neste artigo do TechNet .
fonte