I have view model with 2 properties: A
and B
and I want to validate that A < B
.
Below is my simplified implementation where I use custom validation rule. Since each property is validated independently, it lead to an anoying issue: if entered A
value is invalid, than it stay so even after changing B
, since validation of B
doesn't know anything about A
.
This can be seen on this demo:
A
is invalid after entering 11
, that's correct since 11 > 2
. Changing B
to 22
doesn't re-evalute A
, I have to edit A
to have validation passed.
What I want? I want that after enering 22
into B
the red border (validation error) disappears and A = 11, B = 22
would be source values in view model.
How can I in B
validation somehow force A
validation after new B
value is synchronized with source?
View model:
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));
}
View:
<StackPanel>
<TextBox Margin="10" Text="{local:MyBinding A}" />
<TextBox Margin="10" Text="{local:MyBinding B}" />
</StackPanel>
View code:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel { A = 1, B = 2 };
}
Binding:
public class MyBinding : Binding
{
public MyBinding(string path) : base(path)
{
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ValidationRules.Add(new MyValidationRule());
}
}
Validation rule:
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);
}
}
ValidationRules
don't support invalidating a property when setting another property.
What you should do is to implement INotifyDataErrorInfo
in your view model and raise the ErrorsChanged
event whenever you want to refresh the validation status for a property.
There is an example available in the this TechNet article.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With