Validation errors prevent the property setter being called
I am looking for a simple solution to the following problem:
I am using a simple TextBox control with the Text property bound to a property in the code behind. Additionally I am using a validation rule to notify the user of malformed input.
... error display style here ...
Now after entering valid data into the TextBox the user can hit a button to send the data. When clicking the button the data from the bound property UserName in the code behind is evaluated and sent.
The problem is that a user can enter valid data into the TextBox and this will be set in the property UserName. If the user then decides to change the text in the TextBox and the data becomes invalid, the setter of the property UserName is not called after the failed validation.
This means that the last valid data remains in the property UserName, while the TextBox display the invalid data with the error indicator. If the user then clicks o开发者_JAVA技巧n the button to send the data, the last valid data will be sent instead of the current TextBox content.
I know I could deactivate the button if the data is invalid and in fact I do, but the method is called in the setter of UserName. And if that is not called after a failed validation the button stays enabled.
So the question is: How do I enable calling of the property setter after a failed validation?
You could set the ValidationRule.ValidationStep
property for your validation rules to ValidationStep.UpdatedValue
. This first updates the source, and then performs validation. That means, your property setter should be called even though your validation fails. Note that this property is only available from .NET 3.5 SP1 upwards. For more details, see this blog post (paragraph "How do I use it? (Part 1)").
How I handle this in my view model classes:
public class MyViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private Dictionary<string, string> _Errors = new Dictionary<string, string>();
public object SomeProperty
{
get { return _SomeProperty; }
set
{
if (value != _SomeProperty && !ValidationError("SomeProperty", value))
_SomeProperty = value;
OnPropertyChanged("SomeProperty");
}
}
}
private bool ValidationError(string propertyName, object value)
{
// I usually have a Dictionary<string, Func<object, string>> that maps property
// names to validation functions; the functions return null if the property
// is valid and an error message if not. You can embed the validation logic
// in the property setters, of course, but breaking them out as separate methods
// eases testing.
_Errors[propertyName] = _ValidationMethods[propertyName](value);
OnPropertyChanged("IsValid");
}
public bool IsValid
{
get { return !(_Errors.Where(x => x.Value != null).Any()));
}
public string this[string propertyName]
{
get
{
return (_Errors.ContainsKey(propertyName))
? _Errors[propertyName]
: null;
}
}
}
It's a little awkward to get this all set up at first, but once you've done it, you have a simple and straightforward way to report validation errors to the UI (via the DataErrorValidationRule
), a straightforward way to know whether any given property is valid or not (check _Errors
), and an IsValid
property that tells you whether or not the whole view model is valid. (Also, you can extend the IsValid
property to handle the case where all the properties of the view model are valid but the view model itself is not, e.g. two mutually exclusive flags are both set.) And as long as you make them internal
, the validation methods can be unit tested via NUnit or whatever.
I should add that the above code is off the top of my head and may or may not work as written - my actual working code is in a base class and has a lot of other things baked into it that would just be confusing.
精彩评论