Skip to content

Instantly share code, notes, and snippets.

@GrantByrne
Created April 24, 2014 06:02
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save GrantByrne/11243164 to your computer and use it in GitHub Desktop.
Save GrantByrne/11243164 to your computer and use it in GitHub Desktop.
Using Fluent Validation with WPF - Dead Simple
using System.Text.RegularExpressions;
using FluentValidation;
using WpfFluentValidationExample.ViewModels;
namespace WpfFluentValidationExample.Lib
{
public class UserValidator : AbstractValidator<UserViewModel>
{
public UserValidator()
{
RuleFor(user => user.Name)
.NotEmpty()
.WithMessage("Please Specify a Name.");
RuleFor(user => user.Email)
.EmailAddress()
.WithMessage("Please Specify a Valid E-Mail Address");
RuleFor(user => user.Zip)
.Must(BeAValidZip)
.WithMessage("Please Enter a Valid Zip Code");
}
private static bool BeAValidZip(string zip)
{
if (!string.IsNullOrEmpty(zip))
{
var regex = new Regex(@"\d{5}");
return regex.IsMatch(zip);
}
return false;
}
}
}
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:WpfFluentValidationExample.ViewModels" x:Class="WpfFluentValidationExample.Views.UserView"
Title="UserView" Height="300" MinWidth="500">
<Window.DataContext>
<viewModels:UserViewModel/>
</Window.DataContext>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Name" Margin="10"/>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="200" Margin="10">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<!-- Placeholder for the TextBox itself -->
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Margin="10" Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="E-Mail" Margin="10"/>
<TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="200" Margin="10">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<!-- Placeholder for the TextBox itself -->
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Margin="10" Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Zip" Margin="10"/>
<TextBox Text="{Binding Zip, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="200" Margin="10">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<!-- Placeholder for the TextBox itself -->
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Margin="10" Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
</StackPanel>
<Button Margin="10">Submit</Button>
</StackPanel>
</Window>
using System.Windows;
using WpfFluentValidationExample.ViewModels;
namespace WpfFluentValidationExample.Views
{
/// <summary>
/// Interaction logic for UserView.xaml
/// </summary>
public partial class UserView : Window
{
public UserView()
{
InitializeComponent();
DataContext = new UserViewModel();
}
}
}
using System;
using System.ComponentModel;
using System.Linq;
using WpfFluentValidationExample.Lib;
namespace WpfFluentValidationExample.ViewModels
{
public class UserViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private readonly UserValidator _userValidator;
private string _zip;
private string _email;
private string _name;
public UserViewModel()
{
_userValidator = new UserValidator();
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string Email
{
get { return _email; }
set
{
_email = value;
OnPropertyChanged("Email");
}
}
public string Zip
{
get { return _zip; }
set
{
_zip = value;
OnPropertyChanged("Zip");
}
}
public string this[string columnName]
{
get
{
var firstOrDefault = _userValidator.Validate(this).Errors.FirstOrDefault(lol => lol.PropertyName == columnName);
if (firstOrDefault != null)
return _userValidator != null ? firstOrDefault.ErrorMessage : "";
return "";
}
}
public string Error
{
get
{
if (_userValidator != null)
{
var results = _userValidator.Validate(this);
if (results != null && results.Errors.Any())
{
var errors = string.Join(Environment.NewLine, results.Errors.Select(x => x.ErrorMessage).ToArray());
return errors;
}
}
return string.Empty;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
@karelkral
Copy link

Thank you for excellent example!

@mrNo0b
Copy link

mrNo0b commented Feb 5, 2021

That's what I call Life Saver!! Thank you very much!

question: In UserViewModel.cs ln54, if I change the code to this:

_userValidator.Validate(this, options => options.IncludeProperties(columnName));

it will only check single rule from what I understood, will it be "faster"?

@GrantByrne
Copy link
Author

Wow. I totally forgot that I wrote this gist.

@mrNo0b - I'm not sure. I looked through the fluentvalidation documentation and I didn't find anything related to the performance implications of using the options.IncludeProperties(...) option. My guess would be that would improve performance, but I have no evidence to support it.

@moh-a-med
Copy link

What if I wanna check if my name is unique among all the usernames present in my database.

How would you integrate this rule. Let's suppose I'm working with EF.

@GrantByrne
Copy link
Author

@moh-a-med Making some assumptions here:

  • Assuming that you're using EF
  • Assuming that you're using the built in UserManager

The process would be to inject in the UserManager and then create a rule with the .Must(...) method. You would look up a user by username. If no user is returned, then the username is unique.

@Suhail0khalifa
Copy link

what should i consider if am going to use this with combox or datepicker ?

@GrantByrne
Copy link
Author

DatePicker should be pretty straightforward. ComboBox may be a bit of a pain because of it doesn't seamlessly integrate with the MVVM pattern. I would say try it and see what problems you run into.

@fseidl-bauradar
Copy link

fseidl-bauradar commented Jun 22, 2023

Is there a reason why you use a int for indexing even though you defined a overload which takes a column name?
Additionally, could it be that you have copy paste error, because all ErrorTemplates use [0] not [0],[1],[2], ...?
grafik

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment