Using the EntLib Validation Block with WPF Part 1: Attribute Based Validation
Wednesday, December 24, 2008 – 9:21 AMGetting validation working with data binding in WPF is pretty simple as .NET 3.5 added support for IDataErrorInfo. You can read more about this on the WPF SDK blog and in the databinding overview article on MSDN. This post outlines how you can go about extending this validation approach to take advantage of the EntLib Validation Block and use attributes on the bound properties to enable validation.
Standard data binding validation really means implementing the IDataErrorInfo interface. I’m going to assume that you already have this working. The two links in the paragraph above should be enough to get going with out of the box validation. The resulting bound class looks something like this:
public class ConfiguratorPresentationModel : IConfiguratorPresentationModel, INotifyPropertyChanged, IDataErrorInfo {
...
public string this[string columnName] { get { string result = null; switch (columnName) { case PropertyNames.Size: if (Size < 2) result = "Size too small"; break; ... } return result; } } public string Error { get { return null; } }
I’m using the Presentation Model pattern described in the p&p’s Composite Application Guidance for WPF (Prism). For the purposes of this discussion the ConfiguratorPresentationModel is just like any other object bound to a WPF control. The Error property returns null because it’s used for cross-field validation. The example on the WPF SDK blog also does this. depending on your scenario you might want this to return an error string and bind the property to another WPF control.
What if I wanted to use the EntLib Validation Block to validate properties on my bound object and surface errors from that instead? There are two approaches you could take. The first, which I’ll be covering in this post, is to add validation attributes to the properties on the bound class. Using a configuration based validation is something I’ll cover in a subsequent post.
Validation with Attributes
It turns out that this is very easy. First configure your project to reference the correct EntLib assemblies. This is covered in the Adding Application Code section of the documentation.
Next re-implement the IDataErrorInfo interface to use the EntLib Validator.
public class ConfiguratorPresentationModel : IConfiguratorPresentationModel, INotifyPropertyChanged, IDataErrorInfo { private ValidationResults _validationResults; public DelegateCommand<object> SetupCommand { get; private set; } public ConfiguratorPresentationModel() { UpdateValidationState(); }
public event PropertyChangedEventHandler PropertyChanged; protected void OnNotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler p = PropertyChanged; if (p != null) p(this, new PropertyChangedEventArgs(propertyName)); UpdateValidationState(); } private void UpdateValidationState() { Validator<ConfiguratorPresentationModel> validator = ValidationFactory.CreateValidator<ConfiguratorPresentationModel>(); _validationResults = validator.Validate(this); SetupCommand.RaiseCanExecuteChanged(); } public string this[string columnName] { get { ValidationResults filteredResults = new ValidationResults(); filteredResults.AddAllResults(from result in _validationResults where result.Key == columnName select result); return !filteredResults.IsValid ? filteredResults.First().Message : null; } }
public string Error { get { return null; } }
You’ll note that I abstracted the validation into a single UpdateValidationState method which gets called in the constructor and in the property changed event handler. The validation results are then cached. I’m doing it this way because this reduces the number of times validation happens. The this[] indexer gets called twice for a single update so executing validation there would mean it would run more often than it has to.
Now all that’s needed is to add attributes to the bound properties.
[RangeValidator(2, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Inclusive)] public int Size { get { return _size; } set { _size = value; OnNotifyPropertyChanged(PropertyNames.Size); } }
This is just an example using a very simple validation rule with the default error message. It’s possible to do much more sophisticated validation with the EntLib Validation block. There are lots of other validation rule attributes that ship with EntLib and you can also write your own validators.
The end result is a UI that behaves as you might expect a WPF UI to, the offending input field is highlighted and the tooltip give the user feedback as to what might be wrong. It gives visual feedback to the user but the validation is configured in the Validation Block.
In a future post I’ll cover using configuration file based validation instead of attributes. This allows you to further decouple your presentation and business logic and reuse your validation rulesets.
11 Responses to “Using the EntLib Validation Block with WPF Part 1: Attribute Based Validation”
What is “SetupCommand.RaiseCanExecuteChanged”?
I can’t find a reference to that in the Composite Application Guidance.
Thanks.
By Mario on Jan 22, 2009
Mario,
SetupCommand is a DelegateCommand <object>. I updated the source to include it’s declaration.
You can read more about commands here:
http://msdn.microsoft.com/en-us/library/cc707894.aspx
Ade
By Ade Miller on Jan 22, 2009
Ade,
Allthough this example works, it’s not the kind of behaviour we expect from our WPF applications in 2009. You want to guide the user to the faulty input.
The only way to do this in WPF is to loop through all the bindings and put a validation on that binding, this is the designed behaviour of WPF. Then you can make the background of your textbox red or something, catching the user’s attention. I’d like to see this kind of behaviour maybe in the next version of Entlib or PRISM.
We’ve used WPF, PRISM and EntLib in one of our projects and this was our biggest challenge. We managed to pull it off though. Drop me a line if you are interested how.
Greetings,
Matthijs Krempel
By Matthijs Krempel on Feb 8, 2009
Matthijs,
Actually the approach I describe above does integrate the validation results back into the UI. I added a picture showing what the application looks like. The original article wasn’t very clear on this. Hopefully it’s better now.
This type of integration between the VAB and WPF is one of the things we’re considering for EntLib 5.
Ade
By Ade Miller on Feb 25, 2009
Hi, I’m trying to implement your code but am having a number of problems, the first being a NullReference exception when I try to call SetupCommand.RaiseCanExecuteChanged() in the UpdateValidationState called from the constructor. I’m working with an MVVM pattern and use the DelegateCommand object that was developed for Expression Blend and the one that appears to be generally used (is it different to the Composite Application?). thought I would need to initialise my DelegateCommand but in order to do so I must provide the CanExecute code.
How do you initialise your DelegateCommand? Do you have any working example code that can be downloaded?
Thanks in advance.
Rick
By Rick Edwards on Jul 20, 2009
Rick,
This sample uses the Microsoft.Practices.Composite.Wpf.Commands.DelegateCommand from the p&p Composite WPF guidance (link in blog post above). it’s initialized using
public DelegateCommand SetupCommand { get; private set; }
public ctor()
{
SetupCommand = new DelegateCommand(Setup, CanSetup);
}
Unfortunately I’m unable to provide a downloadable sample on my blog.
Ade
By Ade Miller on Jul 24, 2009
Do you have any suggestions on how to enhance your solution such that multiple properties could be invalidated because of a change to one of those properties? For example, using SelfValidation to compare a StartDate to an EndDate and if the EndDate is earlier than the start, cause both properties to become invalid and display the error template at the same time? BTW, good work!
By Rick Burke on Aug 5, 2009
Rick,
You could definitely use Self Validation (sample here). You can also use the And/Or validators (here) to combine validation clauses on the same property.
For the scenario you describe I’d opt for SelfValidation I think. I just implemented this on a different presentation model and it’s exactly as you would expect.
[HasSelfValidation]
public class PlayerPresentationModel : PresentationModelBase, IPlayerPresentationModel, IDataErrorInfo
{
// ...
[SelfValidation]
public void Validate(ValidationResults results)
{
if (_domainModel.Model.Timestep > _domainModel.MajorTimestep)
results.AddResult(new ValidationResult("Error!", this, "UpdateTimestep", "", null));
}
//...
The only gotcha is that you need to provide the name of the property(s) you want the error to get bound to when you add a result. In this case the property name is “UpdateTimestep”.
Ade
By Ade Miller on Aug 6, 2009