I've been working with Silverlight 3 in a number of LoB (Line of Business) scenarios for a while now and I'm consistently running in to a few dead ends with respect to Validation. Given I've posted twice before on Silverlight, MVVM and Validation:
However, the only way to get a control into an 'invalid state' is to throw an exception in a bound setter. This is the key problem and leads to a number of inconsistencies as to how one should deal with validation, e.g.:
- If an exception is thrown within a setter then the property’s value should not be updated. This means that the bound object is still actually in a valid state and the controller (e.g. code-behind, presenter or viewmodel) has no visibility that the UI and bound model are now inconsistent, or that the UI is in an invalid state (because the model is still valid).
- When a TypeConverter fails to convert input for a binding, a similar problem to one described above arises – the model never receives the input and therefore the validation issue is unknown to the model (I tried to work-around these problems with limited success in this post: http://www.thejoyofcode.com/Silverlight_Validation_and_MVVM_Part_II.aspx - however, this falls flat in the face of cross-property validation).
- Throwing exceptions in property setters means we can’t bind to many generated models (e.g. WCF proxy types) as there is no way we can insert this logic into the generated type (no partial methods etc).
- Custom types that throw exceptions in setters can make cross-field validation very difficult (nay impossible, practically speaking). Furthermore, this approach may cause further difficulty for the framework in deserializing such a type depending on the order in which properties are set (e.g. Salutation and Gender could cause a failure during deserialization if the two aren’t in sync as the properties are set). Trying to work around these issues creates horrific spaghetti validation code that is best avoided altogether.
- You can manually trigger the change of visual state [e.g: VisualStateManager.GoToState(MyTextBox, “InvalidFocussed”, true); ]. The internal operation and flip between focussed and infocussed state appears to hang off a private _isInvalid member of the Control class – blocking the developer from working with the validation framework.
- Even if we could overcome these issues, this behaviour invokes a template that binds directly to the Validation.Errors attached property for the tooltip. However, Validation.Errors is a read-only collection and the AddValidationError method is internal. Furthermore, the actual type ValidationError has an internal constructor. So all routes are blocked as far as I can see.
The good news... is, that there is better support for Validation in Silverlight 4 (available in Beta at the time of writing). This is thanks to added support for the IDataErrorInfo interface which I have written about before with respect to its being supported inside WPF.
Based on the example in the previous post in this 'series': Silverlight, Validation and MVVM - Part II, here's an example of how you might implement this interface. I've chosen to do it this way because I'm a big ViewModel fan and already have a base class to which adding some validation logic seems to make sense. Additionally, I want to leverage the ValidationAttributes available in RIA services (even if I'm not using RIA services themselves). Ideally, the attributes could just be added to the 'model' properties without anymore work. And that's what I've shot for with my new base class.
And, even to my own surprise, this works remarkably well. You can even add new objects and the validation invokes nicely!
but...There are however, a couple of problems.
If the binding engine can't convert your input to the ViewModel type (for example, try inputting some nonsense string into the Age field above); then the view will display a notification (thank to ValidatesOnExceptions=true) but the ViewModel will be unaware of this failing and you won't be able to prevent the update going ahead. For this reason, I'd strongly recommend a hybrid approach utilising the ValidationScope from the previous post to capture Binding Errors and report them to the ViewModel. Note, I haven't included this in the sample above and, instead, have left that as an exercise for the reader.
The other problem to be aware of is silent conversion success. That is, if you enter 21.322 into the Age field then the binding will succeed. But, because that field is bound to an Int32, some of your data will be lost. As far as I know, there is now way to know this is happening so you'd have to implement something at the view to deal with this in a more sophisticated way (a new, derived TextBox control maybe?).
I've always felt IDataErrorInfo to be an 'immature' interface - it feels as though it's from a bygone era. Somebody in Redmond obviously feels the same and Silverlight 4 introduces a new INotifyDataErrorProperty which feels more elegant. The code above should modify to support this new interface with relative ease. Have a read of Mike Taulty's excellent posts on both interface for more information:
Originally posted by Josh Twist on 15 January 2009 here.