The MSDN binding validation sample would have you bind your validation error messages like so:
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
This code will flood your output window with exceptions – and if you have the “break when exception is thrown” option turned on, you'll be doing a whole lot of stepping through these.
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll A first chance exception
of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll A first chance exception of type
'System.Reflection.TargetInvocationException' occurred in mscorlib.dll System.Windows.Data Error: 16 :
Cannot get 'Item[]' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1').
BindingExpression:Path=(0).[0].ErrorContent; DataItem='TextBox' (Name='textBox1'); target element is 'TextBox'
(Name='textBox1'); target property is 'ToolTip' (type 'Object')
TargetInvocationException:'System.Reflection.TargetInvocationException: Exception has been thrown by the target
of an invocation. ---> System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and
less than the size of the collection.
Parameter name: index at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument,
ExceptionResource resource)
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at System.Collections.Generic.List`1.get_Item(Int32 index)
at System.Collections.ObjectModel.Collection`1.get_Item(Int32 index)
at System.Collections.ObjectModel.ReadOnlyCollection`1.get_Item(Int32 index)
--- End of inner exception stack trace ---
. . . at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'
The problem appears to be that when the error is cleared (and HasError is false,) the binding is trying to evaluate again anyway. So the index of zero is out of range.
Josh Smith has a workaround for this that involves a content and data presenter. It’s a little heavy for my taste.
However that solution and this post detailing Microsoft’s failure to fix this known issue started me on the following solution:
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem,
Converter={StaticResource ValidationErrorConverter}}"/>
The CurrentItem property is null if there is nothing in the collection – so no exceptions are thrown evaluating it if there are no errors. It is an instance of ValidationError, however, and doesn't have a useful ToString() override. So I'm using an IValueConverter to get the error message. Here it is in its entirety:
/// <summary>
/// Helps with avoiding of exceptions when setting a tooltip to the validation error
/// </summary>
/// <remarks>
/// Bind to Path=(Validation.Errors).CurrentItem, and use this converter
/// </remarks>
public class ValidationErrorConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
var error = value as ValidationError;
// If we have an error, return its content if (error != null)
return error.ErrorContent;
else return "";
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
// Can't convert back to a ValidationError
throw new Exception("The method or operation is not implemented.");
}
}