Validate Business Entities with WPF - Take 2

In the first part I showed how to validate an entire object with the help of BindingGroup class, now I want to extend the discussion to handle a typical problem that arise with it. Suppose your business object has a property of type Int32, and you bind this property to a standard textbox, what happens when you digit in the previous example a string that is not convertible to an Int32? The answer is nothing.

image

The reason is quite simple, thanks to BindingGroup I’m actually validating the entire Customers class, thus here is what happens, when I write 0aada into the textbox, the binding try to convert this value to an Int32, it fails throwing an exception, so the value of property Count remains unchanged, thus when you validate the entire Customer object, the value of the property IntProperty is always a valid Int32. The smart reader can immediately say the solution is to use a ExceptionValidationRule object, this is true with standard binding, but with validation of the entire business object we have some strange behaviour.

image

If you remember the previous article, the error message is shown with a simple listview, that shows each error with a label. This because our entity validator returns us a list<String> containing all error messages. If you use the ExceptionValidationRule, the error is set to the message of the exception, thus, since a string is an IEnumerable<Char> the result is a series of label, each one showing a single char of the single error message. Moreover if you type a long text into the Country textbox, the error wont show.

The solution is a little change to our validator

1
2
3
4
5
6
7
List<String> alreadyFailed = new List<string>();
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{BindingGroup bindingGroup = (BindingGroup)value;if (bindingGroup.Items.Count == 0) return ValidationResult.ValidResult;
alreadyFailed.Clear();foreach (BindingExpressionBase bindingExpression in bindingGroup.BindingExpressions){	if (bindingExpression.HasError)	{		String property = ((Binding) bindingExpression.ParentBindingBase).Path.Path;		alreadyFailed.Add(property + ":" +bindingExpression.ValidationError.ErrorContent.ToString());		Validation.ClearInvalid(bindingExpression);	}}
var lastValidationResult = validator.ValidateObject(bindingGroup.Items[0]);if (lastValidationResult.Success){	if (alreadyFailed.Count == 0) return ValidationResult.ValidResult;}
return new ValidationResult(false, lastValidationResult.ErrorMessages.Union(alreadyFailed));
}

I’ve simply declared a list of string called alreadyFailed, this list was cleared at the start of each validation. Since the BindingGroup contains the list of the single inner bindingExpressions objects, I did a simple foreach scanning each bindingExpression, and if the bindingExpression has errors (line 10) I add the error message to the alreadyFailed list, and clear the error status (line 14).

The trick is clearing the error status, in this way the only errors are those ones returned from the BusinessEntityValidationRule. The result is the following.

image

As you can see both the standard entity validation error and the conversion error are correctly shown into the error list.

Code is avaliable Here.

Alk.

Tags: Wpf Validation