Many Windows Store apps are content presenters; they require very little data entry and, consequently, very little data validation. But data validation is a core component to a business apps. Is there validation built into the platform? No.
In several XAML frameworks data validation was baked-in. DataAnnotation and ExceptionValidationRule for bindings in WPF. IDataErrorInfo, INotifyDataErrorInfo, and BindingValidationError for setter exceptions in Windows Phone, Silverlight, et al. But these are not an option for a Windows Runtime developer, especially the Universal App developer. And, as we will see in this article, they were never really enough anyway. But, we have options.
I summarized these in a blog article: here
Not enough anyway
But, developers need more than property-level errors. Developers need Property.IsValid and Model.IsValid – we can disable the Save button until true. Developers need Property.IsDirty and Model.IsDirty – we can disable the Save button until true. Developers need Property.Revert() and Model.Revert() allowing edits to be cancelled and Properties reverted to their original values.
In the image above, notice how each field flagged when there is an error and how errors are aggregated for the whole model at the bottom. Also notice how the Save button is disabled while the Revert button is not. All of this, handled through simple binding, starts to deal with our common validation needs.
Developers need Property.Errors and Model.Errors – individually or aggregated to help the end user. Developer need a reusable control that allows them to display errors to the end user and mark fields in error.
In the image above, notice how the user taps or clicks on the red field error indicator and sees an in-context error message. This is a user interface that empowers the user to understand what’s wrong and how to fix it.
This type of requirement requires a custom solution. But, one that is repeatable. A solution that doesn’t ruin serialization, doesn’t ruin the design-time experience, doesn’t dramatically change the structure of a model, and allows for the extensibility necessary to meet varying community needs.
Introducing Property<T>
Property<T> is a generic class that can replace simple properties in your model. It enables IsValid, IsDirty, and Revert() for each property. Once Property<T> is enabled for each property, the same features (IsValid, IsDirty, Revert()) can be extended to the containing model by simply aggregating its properties.
There’s really nothing to it. But, to better understand Property<T>, you should review its source, see how simple it really is, and consider if you want to include this class in your own XAML projects. It’s available on CodePlex, here.
This is the syntax to implement a few properties:
In the code above, notice how the generic declaration matches the simple type the property would have otherwise been. This can be primitive or complex types. It’s up to you.
Binding
It might have crossed your mind that you will not be able to bind the same in XAML. This is true. Because C# does not implement default properties, the only way to access the value of the new property is to access its value property.
Here’s how the binding syntax will look, just add “.value”:
I wrote an article on XAML binding basics: here
Note (for complex types only): because we want to support complex types as well as primitive types, it’s an important note that a developer would need to do two things in order to fully support complex types: 1) override Equals() in your type to ensure it compares the way you want it to, and 2) manually set Property.Original to a clone of your type, Property<T> will not detach your type for you (ICloneable is not part of the Windows Runtime or I would have suggested you use that). Otherwise, both IsDirty and Revert() will not work.
Introducing ModelBase<T>
ModelBase<T> is a generic class that implements several helper methods to make working with Property<T> easy. It enables IsDirty, IsValid, Revert(), and Validate() for your model and encapsulates the implementation except for your actual validation logic – which will be custom to every app.
In the diagram above, notice the structure of the User class, which inherits from BaseModel<T>, and how it hasn’t significantly changed. Working with the User class feel natural and comfortable while the added functionality is automatic.
Here’s the full implementation of the class:
Validation logic
Once your User class inherits from BaseModel<T>, you can setup validation by setting the base.Validator property to an Action<IModel>. If you don’t set base.Validator then Validate() and IsValid will always return true.
Here’s how you might implement validation for User:
Serialization
Look. Every developer knows a POCO DTO is best for transferring serialized data over the wire – it’s the smallest footprint. Let’s not change that. But, using just that same simplistic DTO as your client-side model? Why? It has almost zero functionality.
Let’s create a strategy to simply convert our DTO models to rich models – something setting Rich.Property.Value to DTO.Property and back. If you use the Entity Framework you can let a TT code generate your models flawlessly. That’s an awesome approach that I use myself. How you do it is up to you.
Here’s a simple approach of converting types:
The user control
The ErrorControl user control implements for you what you could have easily implemented yourself. It’s a wrapper for a control (any XAML UIElement actually). It has a single binding, that of the IProperty<T>. From there it shows or hides the error indicator and displays the field-level errors to the user when they tap or click the indicator. When you see how simple it is, you’ll immediately understand how easily you could modify it to your own needs.
Here’s a bit of learning: when you build a user control that wraps another control there’s a surprise problem. Let’s pretend the consuming UI is wanting to wrap a TextBox. Fine – they are effectively setting the Content property of the user control. Now, let’s suppose you want to wrap that TextBox in a Grid. You would do something like <Grid><ContentPresenter /></Grid>. But here’s the problem – you are effectively setting the Content property of the user control. You can’t both set the Content property of the user control. Because the consuming UI is declaring the value of the Content property last, it wins. This means your custom UI is lost. The solution is simple, don’t let the consuming UI set the Content property of the user control. Instead, create a new dependency property of type UIElement, let’s call it InnerContent, and let the consuming UI declare the value of that property. Then, in the changed event of InnerContent you set the Content property of your ContentPresenter control. Done.
Here’s the syntax for using the ErrorControl user control:
In the code snippet above, there are some things to notice:
- The DataContext of the ErrorControl is set to the IProperty<T>. In this sample, FirstName is Property<String>. If you don’t set this, then there’s no good reason to use this user control.
- The InnerContent property of the user control is set to the TextBox and not the Content property of the user control. As I described just above, this is important because we are customizing the TextBox.
- The Placement property of the ErrorControl is being set to Left. This simply indicates if the error indicator is on the Right or the Left. The XAML is simple and you can update it to your own UI
- Nothing else. The point of this control is not to clutter your XAML, but to try and default things to the 99% use case. I worked hard to make it simpler and simpler, perhaps even too simple. Of course you can extend it yourself, but the control is meant to be simple and easy.
Here’s the basics of what it looks like:
FAQ
Can I customize when Validate() is called? By default base.Validate() is called whenever any model property of type IProperty<T> raises the PropertyChanged event and the PropertyName is “Value”. This logic is in the ModelBase<T> constructor. So, when any vale changes.
Here’s how you might call Validate() for additional reasons:
In the code above, we are invoking Validate() every 10 seconds regardless of changes to the properties. It should be noted that this is in addition to the times it would be called because of changes to the properties. If you have collection properties in your models you might add validation triggers.
Do I have to set Property.Errors? You don’t have to do anything. Having said that, you should. Property.IsValid is based on Property.Errors.Any(). So, not adding errors is basically setting Property.IsValid is always true, it also negates the UserControl which sets the opacity of the error indicator to Property.Errors.Any() so that a property without Property.Errors is not visible.
Are Property.Errors automatically included in Model.Errors? Yes
Is it important that my rich models still serialize? Yes, well, it depends. Are you going to save state when the user suspends your app? If so, you want to serialize and persist your rich models to a file or state bag. So, yes, I think so.
>> Update: 2014/08/07 <<
Maintaining simple types: Approach 2
After some personal implementation of this technique, I realize there might be a need to keep the properties of your objects as simple values, not complex types. This is important if you use SQLite. This is important if you use Data Generation in Blend. Whatever the reason, I implemented a second scenario in the App10 project in the same solution.
The real difference is that there is a new “Properties” property in the ModelBase<T> of type Dictionary<string, IProperty> wherein all the IProperties actually reside. The properties simply read and write to the objects in this dictionary. The Error user control has been updated, too. Again, you can find it all in App10 (here).
The alternate XAML syntax is something like this:
There are two changes you should notice. The first one is pretty nice. The binding to the property does not require Value. You use {Binding FirstName} instead of {Binding FirstName.Value}. The other requires a regrettable literal string in conjunction with a new converter to pluck the correct IProperty out of the Properties dictionary. Otherwise, the code changes are trivial.
Conclusion
There is no data validation built in the Windows 8.1 XAML framework. And, when we look back at what data validate was part of XAML in WPF and Phone, we can see it still didn’t give us the functionality we need – Validate(), IsValid, IsDirty, and Revert(). Most of the approaches were simply capturing exceptions on properties. Nothing wrong with that. It’s just not enough.
With IProperty<T> we can see an approach – remember this is not necessarily the solution for you – that developers might introduce to deliver these seemingly simple, yet actually complex and super-valuable features to their models. I hope you can use this or at least be inspired it. Remember, this is is just a start to solve the problem. There will be edge cases. I look forward to your comments.
Get the code: here
Best of luck!