Input validation is necessary in every business application. Time to look into validation with Silverlight.
Starting a little internet research will reveal two variations:
- Putting validation code into property setters, throwing exceptions (covered by Jesse Liberty).
- Decorating properties with validation attributes, either checked by the data grid, or manually, again eventually based on exceptions (see MSDN).
OK, obviously you don’t need me to repeat what has been described by others in sufficient detail.
However, neither the property setter approach, nor validation attributes work well with code generated classes (which are employed quite regularly with SL). Would property changed event handler solve that? How do value converters fit into the picture? What about mutual dependent properties? Null values? Actually, there’s no overall concept that would help answer these questions.
Note: WCF RIA Services (see also here; formerly known as RIA Services) addresses the „generated code“ issue by copying the validation from the server to the client. However I refrain from using WCF Services in this learning experience, since the aim is to understand what’s going on under the hood. Additionally I can easily envision cases in which I may want different (i.e. more restricting) validations on the frontend than on the server.
Let’s clarify what we are talking about. Validation (more to the point, input validation) deals with checking some user input, however it is made, against, let’s call it syntactical and semantical rules. Syntactical rules include all that is necessary to satisfy the type of the backing data, e.g. ensuring the string actually is a valid time (provided the property is of type DateTime). Semantical rules deal with additional restrictions, usually business related, say whether some value is in a certain range.
Note: Validation always happens after the user has made his input, making sure the input was correct. The alternative approach (though not always feasible) is to let him only make valid input in the first place, e.g. with date pickers, numeric up/down, or masked edit boxes. This renders at least the need for syntactic validation obsolete, semantic validation may or may not be covered. Still, it may provide the better user experience.
Syntactic rules are usually enforced implicitly during the actual conversion. The need for a conversion depends on what data the control actually provides (e.g. a date picker control supplies a DateTime ready to use, no need for validation in this case). In other cases syntactic validation is covered by the databinding mechanism of Silverlight.
Semantical rules are up to the developer.
There’s another difference between syntactic and semantic validation: Syntactic validation has to occur before changing the data (since it’s a precondition to a successful type conversion), thus it is also tied to the UI and the databinding process. Semantic validation on the other hand can also happen after the data has already been changed. And it doesn’t have to happen in the UI either. Matter of fact, some validations may not even be possible in the UI, but have to be enforced by the business logic or even the database. Heck, they may happen out-of-band at any time, say by asking some other system asynchronously.
Note: Within my Silverlight UI I don’t care who actually does the validation, but the fact that it may happen outside of the usual sequence (databinding, triggered by some user input) is important.
Typical use cases for validation include:
A) ensuring a string represents a certain type (numeric, date,…)
B) ensuring some input, i.e. required fields
C) ensuring a certain text length
D) ensuring a value matches some criterion (range check, regular expression, date in the future, etc.)
E) mutually dependent fields (mutual exclusion, value dependencies, etc.)
F) distinguishing between input and no input (i.e. null values)
Validation “mechanics” in SL
Validation is tied to data binding and to get it working the binding properties have to be set respectively:
Now let’s take a closer look at the data binding process. Here’s a (somewhat simplified) sequence diagram of the relevant code for validation, after a control changed its value. Basically it all depends on exceptions:
(Note: this is not entirely correct regarding the exception handling, but is conveys the actual meaning better.)
Let’s go over the locations I numbered:
1: The value converter you could declare in the binding expression is called – yet it does not take part in the validation handling. Meaning, any exception thrown here won’t appear as validation error, but as actual application error. This renders value converters useless in cases that need syntactic validation (like use case A).
2: Right after the value converter, a try marks the area in which every exception will be treated as validation error.
Personally I don’t understand why the value converters have been left out of this area (seems to be a conscious decision). They would have offered some easy to use validation mechanism…
3: The type converter of the property’s type is called, which translates the value from the type passed in by the control (a textbox passes a string, other controls may pass in other types) to the target type, say Int32. Exceptions thrown here will show up as something like „Input string was not in a correct format.“, and of course the property retains its original value. Hence the type converter does syntactic validation (use case A). There’s however no feasibly way to customize the conversion, like what a value converter could do.
Personally I don’t understand why the value converters… . Ah, said that already, didn’t I?
4: The property value is set, and in turn ticks off any validation code put in the property setter for semantic validation. The samples on the internet usually have the validation code before actually changing the property value, thus the property still retains its original value in case of a validation error, read exception. Anyway, use cases B (ignoring null values for the moment), C, and D are covered here.
5: The setter will also raise the PropertyChangedEvent, and in turn any exception thrown in a respective handler will also take part in the validation handling. However if we get that far, the property now has the invalid data already set. Still, this may be another location for use cases B, C, and D.
6: Any validation error is announced to the control via the BindingValidationError event. Many controls have an error state and will show a red border and an error tooltip. Alternatively there is a ValidationSummary control that takes care of presenting the feedback:
7: Finally there’s some missing parts in the sequence diagram:
- The databinding mechanism doesn’t check the validation attributes, so who does? The DataGrid is the only control that actively validates the row in edit mode, but one can trigger the validation himself.
If done before writing the data, this approach bypasses any value and type conversion (another reason why value converters have no part in validation). Theoretically one could validate a property this way without setting the values at all. However due to the type conversion issue, the only sensible approach is validate afterwards (which is what the DataGrid does), and let the data binding mechanism care about conversions. This way one could trigger that validation generically in the PropertyChanged event handler.
- Nobody cares about errors (exceptions) in property setters and event handlers (point 4 and 5) outside of this sequence. Meaning any other code manipulation the properties and causing an exception will tear the whole application down.
As a corollary: Validation works one binding at a time. This implies that mutually dependent properties (use case E) are not part of the equation. If property X caused an error that may be fixed by changing property Y, SL doesn’t help. One can work around this (by a combination of UI design and simulated PropertyChanged events), but it’s ugly work.
I left out use case F (null values) so far: Data intensive applications may have to distinguish between no entry (empty string, null value) and the 0-value for the data type (e.g. „0“, „00:00“). The data type of the property would be a nullable value type, e.g. Nullable<Int32> or Nullable<DateTime>. The sad part: Type converters don’t handle null values, neither does some other part of the data binding mechanism. nullable types are treated like their non-nullable counterpart, thus no empty strings are allowed, worse, an empty string even usually causes an exception, thus validation error. The best way to solve this is a separate property of type string that handles null and 0 representation and does the type conversion. However it takes some effort to keep those two properties in sync and propagate PropertyChanged events of the other property.
Consequences & Conclusions
Now that the input validation mechanism is understood (I hope), I can draw some conclusions:
To set or not to set…
There is some inconsistency between whether an invalid value will actually be set in the data property or not: Type converter (syntactic validation) and property setter issues (semantic validation) generally leave the value as is. Validations in property changed notifications – including validation attributes if implemented that way – (also semantic validations) will set the property to the invalid value and notify only afterwards. This in turn means that our view model logic has to take the validity of the data into account, i.e. a save button should check some kind of IsValid property on the model.
To throw or not to throw…
In SL3 the validation mechanism relies on exceptions. Adverse effect is that the first validation error hides subsequent errors, which may obscure the feedback for the user. Secondly this code is also present when I set the properties from code, with no data binding infrastructure readily available to catch the exceptions. Hence I have to be very careful, or some innocent code may take my whole application down.
To depend or not to depend…
Validation in SL3 doesn’t cover mutually dependent properties properly. Some nasty stunts and compromises may get you a working solution, but it hurts to write that code (been there, done that!).
Anyway, with SL4 around the corner (see below) I‘d refrain from putting too much effort into this issue right now.
To convert or not to convert…
As said before, value converters do not exactly work with validation, at least if the conversion itself may fail. Thus I’d refrain from using them altogether in case validation is also needed. I’d rather put the conversion logic into „shadow properties“, i.e. properties that replace/complement the original property, changing only the property type (usually to string). Another reason for these shadow properties would be the null/0 issue mentioned above.
To keep these two properties connected and synchronized takes some effort. For example if one property changes, the other should raise the PropertyChanged event as well.
Contrary to SL3 this isn’t tied to exceptions any more. This will change the picture completely:
- The first validation error won’t necessarily prevent other validations
- Other code setting the value (outside of databinding) won’t have to deal with exceptions (and still partake in validation).
- Asynchronous validation is covered.
- Mutual dependencies can be addressed.
The one notable gap is the still missing MetadataTypAttribute that would enable the usage of validation attributes for code generated classes. The only other aspect not covered is the null values and value converter issue. But strictly speaking this is more an issue of type conversion than of the validation itself. (Still a pesky issue.)
If SL4 changes nearly everything, then why this post in the first place? Well, apart from the fact that existing code doesn’t migrate itself, while the contents presented here are less relevant, they are still valid.
With SL3 I’d say about 80% of my validation demands are covered. But as always, the remaining 20% don’t appear all that often, but if they do, it hurts.
Looking ahead, SL4 will provide a better foundation for validation, and solve some issues — yet it still won’t cover every aspect. Still, having looked at SL4 may give some hints on what to implement today for SL3 (and what not), with a clean migration path to SL4.
That’s all for now folks,