Graeme Hill's Dev Blog

Finding all validation errors on an IDataErrorInfo object

Star date: 2009.300

WPF data binding has built in support for IDataErrorInfo, so it is easy to display a validation error when a property has invalid data. However, sometimes we need to manually find all the validation errors on an object. A perfect example of this is when trying to save data. Often you will want to verify that there are no validation errors before allowing the save operation to proceed. Using the MVVM pattern it would be ideal to determine whether there are any errors purely within your model view. The default behavior of IDataErrorInfo does not give a collection of all current errors. Instead, it will just tell you if a given property has an error (ie: using IDataErrorInfo.Item), so all we have to do to find all the errors is enumerate through each property on the class and call the Item property with that property name as the argument. The GetValidationErrors function below does exactly that:

Option Strict On

Imports System.ComponentModel

Public Class ValidationHelper

    ''' <summary>
    ''' Checks for errors in <c>validatable</c> and returns all the errors found.
    ''' </summary>
    Public Shared Function GetValidationErrors(
        ByVal validatable As IDataErrorInfo) As IEnumerable(Of DataError)

        Dim errors As New List(Of DataError)

        ' Iterate through every property in the class
        For Each prop In validatable.GetType.GetProperties

            ' If the property has an error, then add it to the list
            Dim errorMessage = validatable(prop.Name)
            If errorMessage IsNot Nothing Then
                errors.Add(New DataError(prop.Name, errorMessage))
            End If

        Next

        Return errors

    End Function

    ''' <summary>
    ''' Represents a single error.  It's a propertyName, errorMessage pair
    ''' </summary>
    Public Class DataError

        Private _propertyName As String
        Private _errorMessage As String

        Public Sub New(ByVal propertyName As String, ByVal errorMessage As String)
            _propertyName = propertyName
            _errorMessage = errorMessage
        End Sub

        ''' <summary>
        ''' The name of the property that has the error.
        ''' </summary>
        Public ReadOnly Property PropertyName() As String
            Get
                Return _propertyName
            End Get
        End Property

        ''' <summary>
        ''' A description of the property's error.
        ''' </summary>
        Public ReadOnly Property ErrorMessage() As String
            Get
                Return _errorMessage
            End Get
        End Property

    End Class

End Class