Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM pattern, IDataErrorInfo and Binding to display error?

On MSDN Magazine it has a good article about MVVM and they are binding the validation error in the Xaml to Validation.ErrorTemplate="{x:Null}". I do not get it why and how they can display from the IDataErrorInfo the error? Anyone can light me on how to get the error message displayed to the screen with the MVVM approach?

like image 950
Patrick Desjardins Avatar asked Jul 14 '09 01:07

Patrick Desjardins


2 Answers

When you bind to an object that supports IDataErrorInfo, there are several features of the WPF Binding class to consider:

  1. ValidatesOnDataErrors must be True. This instructs WPF to look for and use the IDataError interface on the underlying object.

  2. The attached property Validation.HasError will be set to true on the target object if the source object's IDataError interface reported a validation problem. You can then use this property with trigger to change the tooltip of the control to display the validation error message (I'm doing this in my current project and the end user's love it).

  3. The Validation.Errors attached property will contain an enumeration of any ValidationResult errors resulting from the last validation attempt. If you're going with the tooltip approach, use an IValueConverter to retrieve only the first item... otherwise you run into binding errors for displaying the error message itself.

  4. The binding class exposes NotifyOnValidationError, which when True, will cause routed events to bubble up from the bound control every time a validation rule's state changes. This is useful if you want to implement an event handler in the container of the bound controls, and then add and remove the validation messages to/from a listbox.

There are samples on MSDN for doing both style of feedback (the tooltips as well as the listbox), but I'll paste below the code I roled to implement the tooltip feedback on my DataGridCells and TextBoxes...

The DataGridCell style:

   <Style TargetType="{x:Type dg:DataGridCell}"
           x:Key="DataGridCellStyle">

      <Setter Property="ToolTip"
              Value="{Binding Path=Column.(ToolTipService.ToolTip),RelativeSource={RelativeSource Self}}" />

      <Style.Triggers>
        <Trigger Property="Validation.HasError"
                 Value="True">
          <Setter Property="ToolTip"
                  Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors), Converter={StaticResource ErrorContentConverter}}" />
        </Trigger>
      </Style.Triggers>

    </Style>

The TextBox style:

     <Style x:Key="ValidatableTextBoxStyle" TargetType="TextBox">
  <!--When the control is not in error, set the tooltip to match the AutomationProperties.HelpText attached property-->
  <Setter Property="ToolTip"
          Value="{Binding RelativeSource={RelativeSource Mode=Self},Path=(AutomationProperties.HelpText)}" />

          <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
              <Setter Property="ToolTip"
                      Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors)[0].ErrorContent}" />
            </Trigger>
          </Style.Triggers>
        </Style>

The ErrorContentConverter (for retrieving the first validation error message for the tooltip):

Imports System.Collections.ObjectModel

Namespace Converters

    <ValueConversion(GetType(ReadOnlyObservableCollection(Of ValidationError)), GetType(String))> _
    Public Class ErrorContentConverter
        Implements IValueConverter

        Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
            Dim errors As ReadOnlyObservableCollection(Of ValidationError) = TryCast(value, ReadOnlyObservableCollection(Of ValidationError))
            If errors IsNot Nothing Then
                If errors.Count > 0 Then
                    Return errors(0).ErrorContent
                End If
            End If
            Return String.Empty
        End Function

        Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
            Throw New NotImplementedException()
        End Function

    End Class

End Namespace

...and finally an example of using the style in a textbox:

    <TextBox Text="{Binding Path=EstimatedUnits,ValidatesOnDataErrors=True,NotifyOnValidationError=True}"
             Style="{StaticResource ValidatableTextBoxStyle}"
             AutomationProperties.HelpText="The number of units which are likely to sell in 1 year." />
like image 155
Mark Avatar answered Oct 08 '22 02:10

Mark


I was looking at the same sample just a few minutes ago. Your guess is righ. In this code sample they removed default ErrorTemplate from TextBox control so it would't show red rectangle. Instead of using ErrorTemplate they create ContentProvider with content bound to validation error of specific text box.

like image 20
aku Avatar answered Oct 08 '22 04:10

aku