I Got the chance to look at the FluentValidation.Net Framework this week. Overall its pretty spiffy! The API seems to handle most validation situations pretty well. Often times these things fall apart as soon as you run into a mildly complex situation such as dependent rule processing etc. Using this for WPF and MVVM as it turns out is pretty simple. In this post I will show you a simple example of how to integrate FluentValidation.Net into your View Models, using the IDataErrorInfo interface and the databinding support provided by WPF.
Here is what I will cover in the post.
- Create a View for the MainWindow
- Create a ViewModel for the MainWindow
- Create a Validator for the MainWindowViewModel
- Setup Dependency Injection so ViewModels and Validators are both Injected as needed
- Implement IDataErrorInfo so it routes calls our Validator implementation
If you do not have Unity and the FluentValidation dll’s, please learn how to use NuGet and go get it.
The MainWindow View
Very basic XAML here the intent is only to get the moving parts setup, not to win a beauty contest. Two things to notice here.
- I have the binding set to update its source upon PropertyChanged this is not needed but I like instant feedback when possible.
- In order to support IDataErrorInfo implementations you must set the dependency property ValidatesOnDataErrors to true.
<ribbon:RibbonWindow x:Class="MoreFluent.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary" Title="MainWindow" x:Name="RibbonWindow" Width="640" Height="480"> <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <ribbon:Ribbon x:Name="Ribbon"> <ribbon:Ribbon.ApplicationMenu> <ribbon:RibbonApplicationMenu SmallImageSource="Images\SmallIcon.png"> <ribbon:RibbonApplicationMenuItem Header="Hello _Ribbon" x:Name="MenuItem1" ImageSource="Images\LargeIcon.png" /> </ribbon:RibbonApplicationMenu> </ribbon:Ribbon.ApplicationMenu> <ribbon:RibbonTab x:Name="HomeTab" Header="Home"> <ribbon:RibbonGroup x:Name="Group1" Header="Group1"> <ribbon:RibbonButton x:Name="Button1" LargeImageSource="Images\LargeIcon.png" Label="Button1" /> <ribbon:RibbonButton x:Name="Button2" SmallImageSource="Images\SmallIcon.png" Label="Button2" /> <ribbon:RibbonButton x:Name="Button3" SmallImageSource="Images\SmallIcon.png" Label="Button3" /> <ribbon:RibbonButton x:Name="Button4" SmallImageSource="Images\SmallIcon.png" Label="Button4" /> </ribbon:RibbonGroup> </ribbon:RibbonTab> </ribbon:Ribbon> <Grid Grid.Row="1" Margin="7,7,7,7"> <StackPanel Orientation="Horizontal" Height="24" Width="250"> <Label>Name</Label> <TextBox Width="150" Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"></TextBox> </StackPanel> </Grid> </Grid> </ribbon:RibbonWindow>
Not So Fancy eh?
I suppose I should give you a screen shot. Not so pretty but good enough to get the point across!
And The Code Behind
And of course the CodeBehind. However minimal this is there are a few important things here to point out.
- The Dependency Attribute. Unity uses this attribute to facility property dependency injection upon build up.
- The setter for the ViewModel property wraps the DataContext making MainWindowViewModel the DataContext for the view.
using Microsoft.Practices.Unity; using Microsoft.Windows.Controls.Ribbon; namespace Validation { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : RibbonWindow { /// <summary> /// Gets or sets the view model. /// </summary> /// <value>The view model.</value> [Dependency] public MainWindowViewModel ViewModel { get { return (MainWindowViewModel)DataContext; } set { DataContext = value; } } /// <summary> /// Initializes a new instance of the <see cref="MainWindow"/> class. /// </summary> public MainWindow() { InitializeComponent(); } } }
The MainWindow ViewModel
The MainWindowViewModel exposes a single property and the implementation of the IDataErrorInfo interface. Notice again we are relying on Unity to inject the Validator. In real life the the IDataErrorInfo implementation would reside in some sort of base ViewModel class. For example purposes I will not be doing that here.
Notice the IDataErrorInfo indexer, this forwards the call to the Validator asking it to run any rules associated to the supplied property name. If any rules are broken the error messages are returned in a single string with line breaks between each error message. Additionally the Error property asks the Validator to Validate all of the rules on a given object and return the enumerated results, again including line breaks.
using System; using System.ComponentModel; using System.Linq; using FluentValidation; using Microsoft.Practices.Unity; namespace MoreFluent { public class MainWindowViewModel : IDataErrorInfo { #region Dependencies /// <summary> /// Gets or sets the validator. /// </summary> /// <value>The validator.</value> [Dependency] public AbstractValidator<MainWindowViewModel> Validator { get; set; } #endregion #region Properties /// <summary> /// Gets or sets the name. /// </summary> /// <value>The name.</value> public string Name { get; set; } #endregion #region IDataErrorInfo Members /// <summary> /// Gets an error message indicating what is wrong with this object. /// </summary> /// <value></value> /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns> string IDataErrorInfo.Error { get { return Validator != null ? string.Join(Environment.NewLine, Validator.Validate(this).Errors.Select(x => x.ErrorMessage).ToArray()) : string.Empty; } } /// <summary> /// Gets the <see cref="System.String"/> with the specified property name. /// </summary> /// <value></value> string IDataErrorInfo.this[string propertyName] { get { if (Validator != null) { var results = Validator.Validate(this, propertyName); if (results != null && results.Errors.Count() > 0) { var errors = string.Join(Environment.NewLine, results.Errors.Select(x => x.ErrorMessage).ToArray()); return errors; } } return string.Empty; } } #endregion } }
The Validator
Of course this would be incomplete without the Validator so here it is! Just one simple rule.
namespace FluentValidation { /// <summary> /// Validator for the MainWindowViewModel /// </summary> public class MainWindowViewModelValidator : AbstractValidator<MainWindowViewModel> { #region Constructor /// <summary> /// Initializes a new instance of the <see cref="MainWindowViewModelValidator"/> class. /// </summary> public MainWindowViewModelValidator() { RuleFor(x => x.Name).NotEmpty().WithMessage("Name is Required"); } #endregion } }
The Unity Setup
Setting up Unity is simple. Two things need to be done.
- Remove the StartupUri from your App.xaml
- Override OnStartup in your App.xaml.cs
The App.xaml
<Application x:Class="MoreFluent.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Application.Resources> <!-- Resources scoped at the Application level should be defined here. --> </Application.Resources> </Application>
The App.xaml.cs
using System.Windows; using FluentValidation; using Microsoft.Practices.Unity; namespace MoreFluent { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); IUnityContainer container = new UnityContainer(); container.RegisterType<AbstractValidator<MainWindowViewModel>, MainWindowViewModelValidator>(); var mainWindow = container.Resolve<MainWindow>(); mainWindow.Show(); } } }
Enjoy!
Good post, I think the ViewModel code has been put where the CodeBehind code should be?
I am not sure I follow what you mean? What code do think should be in a code behind?
Hi, the code snippet under
“And The Code Behind” heading
Is the same code snippet that sites under the “The MainWindow ViewModel” heading
Both are – public class MainWindowViewModel : IDataErrorInfo
good post though, helped me out!
Wow, how the heck did I miss that 😐 Thanks for pointing it out! I have corrected this.
no stress, awesome thanks
Great thing 🙂
Great tutorial! This is by far the most elegant solution for WPF validation. I was already using fluent validation with asp.net mvc, but I didn’t know how to integrate it with wpf. Thank you!
Mike,
Glad you find it useful.
Brette
Hi Brette!
I’m late to the party but your tutorial is very helpful!
I have a question regarding this part: “In real life the the IDataErrorInfo implementation would reside in some sort of base ViewModel class.”
I’m using Prism so I’m guessing that I should derive BindableBase to implement IDataErrorInfo then derive my ViewModels from that?
When doing that, how do I set up the Validator in the base class?
Is it something like:
[Dependency]
public AbstractValidator Validator { get; set; }
I’m hoping that would let Unity inject the correct Validator type in the derived ViewModel class?
I’m new to MVVM so any input is appreciated!
Thanks!
It looks like my angle brackets got removed when posting!
I asked about AbstractValidator with “this.GetType().Name” inside the angle brackets.
Thanks!
Thanks for your comments. I am also using Prism on a current project and you are correct in that you would derive from BindableBase or NotificationObject if you are using more recent builds of Prism. The question of dependency injection of the validators can be tricky. I will outline my approach that I have had with some success.
ValidationManager
Introduce a new object called a ValidationManager. This object should be injected into your base ViewModel implementation. The role of this object is to associate the correct validator with your ViewModel instance. This method has a method named Initialize(IViewModelBase viewModel). This method will use ServiceLocation to lookup up the correct Validator object in Unity using the passed in ViewModel’s type information. So something like this.
public Initialize(IViewModel viewModel)
{
viewModel.Validator = ServiceLocator.Current.GetInstance(typeof(IValidator), viewModel.GetType().Name);
}
For this to work you will need to register a named dependency with Unity.
Now in your base ViewModels constructor you can do something like this.
public ViewModelBase(IValidationManager validationManager){ ValidationManager.Initialize(this);}
Once this line of code executes your viewModels Validator property will be handed an instance of the correct validator object.
Also its worth nothing that ValidationManager is what I used to control all validation type things from code. This keeps that messy stuff out of my viewModels. For example the ValidationManager can publish EventAggregator notifications to the rest of the app if other screens, controls care to know about validation changes etc.
Hope this helps,
Brette
Hi Brette,
Thank you for your detailed explanation.
I tried hard implementing it but hit a snag. Here is what I tried:
I made a validator class for my View Model, inheriting from the generic AbstractValidator. I also created the validationManager as you suggested. Then I created a ViewModelBase class that’s derived from BindableBase. I implemented IDataErrorInfo in that class and made my View Model inherit from it.
However, while implementing the logic in the indexer for IDataErrorInfo, I found out that the FluentValidation method Validator.Validate that takes an instance of a VM and the propertyName to validate is in fact an extension method that is only supported on the generic version of IValidator. The non-generic version of IValidator only takes the object instance and therefore will only validate all of the properties at once.
Now, I’m not quite sure how to register the generic version with my validator classes in the Unity container (in my BootStrapper) and how to resolve them using the service locator in the ValidationManager.Initialize().
Any insight would be appreciated!
Hey Hugues,
Lets do this over email. We can exchange code and communicate a bit better that way. I have run into this same limitation and there are a few ways you can deal with it. Shoot me a mail at Brette@Brette.net