I am getting started with using ValidationRules in my WPF application, but quite confused.
I have the following simple rule:
class RequiredRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (String.IsNullOrWhiteSpace(value as string))
{
return new ValidationResult(false, "Must not be empty");
}
else
{
return new ValidationResult(true, null);
}
}
}
Used in XAML as follows:
<TextBox>
<TextBox.Text>
<Binding Path="Identity.Name">
<Binding.ValidationRules>
<validation:RequiredRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
This mostly works as I would expect. I was surprised to see that my source property (Identity.Name) was not being set; I have an undo function that never sees the change, and there is no way to revert the value other than re-type it (not good).
Microsoft’s Data Binding Overview describes the validation process near the bottom, which explains this behavior very well. Based on this, I would want to have my ValidationStep set to UpdatedValue.
<validation:RequiredRule ValidationStep="UpdatedValue"/>
This is where things get weird for me. Instead of Validate() being called with object value being the property value that was set (i.e., a string), I get a System.Windows.Data.BindingExpression! I don’t see anything in Microsoft’s documentation that describes this behavior.
In the debugger, I can see the source object (the DataContext of the TextBox), navigate the path to the property, and see that the value has been set. However, I don’t see any good way to get to the right property within the validation rule.
Note: With ValidationStep as ConvertedProposedValue, I get the entered string (I don’t have a converter in use), but it also blocks the source property update when validation fails, as expected. With CommittedValue, I get the BindingExpression instead of the string.
There are several questions in here:
-
Why do I get an inconsistent argument type being passed to Validate() based on the ValidationStep setting?
-
How can I get to the actual value from the BindingExpression?
-
Alternately, is there a good way to allow the user to revert the TextBox to the previous (valid) state? (As I mentioned, my own undo function never sees the change.)
I have solved the problem of extracting the value from the
BindingExpression, with a minor limitation.First, some more complete XAML:
Note that the first TextBox uses
ValidationStep="RawProposedValue"(the default), while the second one usesValidationStep="UpdatedValue", but both use the same validation rule.A simple ViewModel (neglecting INPC and other useful stuff):
And finally, the new RequiredRule:
The
GetBoundValue()method will dig out the value I care about if it gets a BindingExpression, or simply kick back the argument if it’s not. The real key was finding the “Path”, and then using that to get the property and its value.The limitation: In my original question, my binding had
Path="Identity.Name", as I was digging into sub-objects of my ViewModel. This will not work, as the code above expects the path to be directly to a property on the bound object. Fortunately, I have already flattened my ViewModel so this is no longer the case, but a workaround could be to set the control’s datacontext to be the sub-object, first.I’d like to give some credit to Eduardo Brites, as his answer and discussion got me back to digging on this, and did provide a piece to his puzzle. Also, while I was about to ditch the ValidationRules entirely and use IDataErrorInfo instead, I like his suggestion on using them together for different types and complexities of validation.