Some of my recent projects have required a lot of user interaction.  So I’ve had to prepare my applications for the possibility of someone mistyping or typing random stuff into a text box just to see what would happen.  I spent some time trying to find a good way of handling validation in a WPF app. I came across a great tool called Fluent Validation that makes validation almost fun.

FluentValidation

A Fluent Interface aims to make code more readable mainly by allowing the programmer to chain method calls together.  Wikipedia says there is more to it than this but at any rate that is as much of it as I understand.  To start using FluentValidation head to http://fluentvalidation.codeplex.com/ and download it or, if you like to do things the easy way, use Nuget to add it to your project (PM> Install-Package FluentValidation).

With FluentValidation you create a class which defines rules for every property of a class that is bound to user input.  Here is an example:

Public Class PartValidator
 Inherits AbstractValidator(Of Part)
'http://blogsprajeesh.blogspot.com/2009/11/fluent-validation-wpf-implementation.html
 Public Sub New()
 RuleFor(Function(part) (part.LeftPartProgramName)).
 NotEmpty.
 Matches("[A-Z](?:[A-Z]|[0-9]){0,15}\.MIN").
 WithMessage("Invalid program name")

RuleFor(Function(part) (part.FinishedPartNumber)).
 NotEmpty.
 Length(10, 11)

RuleFor(Function(part) (part.RawPartNumber)).
 NotEmpty.
 Length(10, 11)

RuleFor(Function(part) (part.QualityCheckCount)).
 InclusiveBetween(2, 300)

RuleFor(Function(part) (part.LoaderYShift)).
 InclusiveBetween(-0.5#, 0.5#)

RuleFor(Function(part) (part.LoaderZShift)).
 InclusiveBetween(-0.5#, 0.5#)

RuleFor(Function(part) (part.Variable1)).
 InclusiveBetween(1, 200).
 NotEqual(Function(p) p.Variable2)

RuleFor(Function(part) (part.Variable2)).
 InclusiveBetween(1, 200).
 NotEqual(Function(p) p.Variable1)

End Sub

End Class

Here I am using some of the built in validators such as .Match(Regex Expression), .NotEmpty, .NotEqual and .InclusiveBetween.  You can find a list of the built in validators and information about how to make your own in the documentation section here.

FluentValidation isn’t WPF specific but, since I’m doing everything  in WPF, I’m going to show you how I’m using it in WPF.  The method I’m using I converted from this C# example: http://goo.gl/3mQZJ

Here is the class I needed to validate. (To keep the post length down I’ve removed some properties)

</pre></pre>
Imports System.ComponentModel
Imports FluentValidation
Imports FluentValidation.Results

Public Class Part : Implements INotifyPropertyChanged, IDataErrorInfo
 'Backing Store variables removed for space

Public Property FinishedPartNumber As String
 Get
 Return _finishedPartNumber
 End Get
 Set(ByVal Value As String)
 If (_finishedPartNumber = Value) Then Return
 _finishedPartNumber = Value
 NotifyPropertyChanged("FinishedPartNumber")
 End Set
 End Property

'... More properties...

#Region "Implement INotifyPropertyChanged"

Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
 Private Sub NotifyPropertyChanged(PropertyName As String)
 RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
 End Sub

#End Region

#Region "Validation"

Public Function SelfValidate() As ValidationResult
 Dim v = ValidationHelper.Validate(Of PartValidator, Part)(Me)
 IsValid = v.IsValid
 Return v
 End Function

Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
 Get
 Return ValidationHelper.GetError(SelfValidate())
 End Get
 End Property

Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
 Get
 Dim __ValidationResults = SelfValidate()
 If __ValidationResults Is Nothing Then
 Return String.Empty
 End If
 Dim __ColumnResults = __ValidationResults.Errors.FirstOrDefault(Function(x) String.Compare(x.PropertyName, columnName, True) = 0)
 Return If(__ColumnResults IsNot Nothing, __ColumnResults.ErrorMessage, String.Empty)
 End Get
End Property

#End Region

End Class

The validation action is happening in the implemented ‘Item’ property.  Here we can check all of our items for

To notify the user of a validation error all we have to do is add ValidatesOnDataErrors=”True” to our binding for the input field like so:

<TextBox Text=”{Binding LeftPartProgramName, ValidatesOnDataErrors=True}”/>

In a standard WPF window this will give us a red outline around the textbox with the message we specified for that property given in the tool tip.  This doesn’t work well for touch screen apps so you will want to create an error template to display the message.  Google “WPF error template” for examples like this one.  Some WPF themes like MahApps Metro theme have a nice template for touch screens already built in.

Here is the ValidationHelper Class

Imports System.ComponentModel
Imports FluentValidation
Imports FluentValidation.Results
Imports System.Text

Public Class ValidationHelper
 Public Shared Function Validate(Of T As {IValidator(Of K), New}, K As Class)(entity As K) As ValidationResult
 Try
 Dim __Validator As IValidator(Of K) = New T()

Return __Validator.Validate(entity)
 Catch ex As Exception
 MessageBox.Show(ex.Message)
 End Try
 End Function

Public Shared Function GetError(result As ValidationResult) As String
 Dim __ValidationErrors = New StringBuilder()
 For Each validationFailure In result.Errors
 __ValidationErrors.Append(validationFailure.ErrorMessage)
 __ValidationErrors.Append(Environment.NewLine)
 Next
 Return __ValidationErrors.ToString()
 End Function
End Class

About jweaver

Service Tech at Gosiger Michigan. Twitter: @jon_weaver6

One response »

  1. Chris says:

    Nice!!! Thank you for sharing this, great info!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s