views:

1046

answers:

3

Hello,

I have a TextBox.TextProperty bound to a ViewModel property. In the binding I have explicitly set ValidatesOnExceptions to True.

In resources the TextBox has this trigger:

<Trigger Property="Validation.HasError" Value="true">
   <Setter 
      Property="ToolTip"
      Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
   <Setter TargetName="Border" Property="Background" Value="Crimson"/>

Unfortunately my implementation does not work perfectly because when I have an exception the TextBox background is highlighted with the Crimson color, but the tooltip text contains "Exception has been thrown by the target of an invocation." instead of the message I wrote in the exception constructor.

Do you have any suggestion?

Thank you in advance, Marco

+1  A: 

This is because the validation code is being invoked via reflection. Any caught exceptions will be wrapped up in a TargetInvocationException instance. The original exception will be stored as this exception's InnerException.


What happens if you bind to the ValidationError.Exception property instead of ValidationError.ErrorContext?

Stu Mackellar
Good catch Stu!If a bind to Path=(Validation.Errors)[0].Exception I get in the tooltip the whole exception Message.Then I tried to bind to:(Validation.Errors)[0].Exception.InnerException.Messageand I finally got the excpected one. But now...is the behavior of ValidatesOnExcpetions correct?
marco.ragogna
There does seem to be something strange going on in your validation - I think you need to work out why your exception is being wrapped in the first place. Binding to the inner exception is a bit brittle 'cos it'll stop working if you have any unwrapped exceptions.
Stu Mackellar
A: 

Path=(Validation.Errors)[0].Exception.InnerException.Message}

A: 

I'm having this same issue and I can't see why in my case validation is being invoked through reflection. I'm considering one of two solutions.

First I'm thinking to implement a converter to extract the InnerException from the ValidationError.Exception when necessary. Something like this:

[ValueConversion(typeof(ValidationError), typeof(string))]
public class ErrorContentConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var validationError = (ValidationError)value;

        if ((validationError.Exception == null) || (validationError.Exception.InnerException == null))
            return validationError.ErrorContent;
        else
            return validationError.Exception.InnerException.Message;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

I'm using the converter on the Tooltip message:

<Trigger Property="Validation.HasError" Value="true">
    <Setter Property="ToolTip"
        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
        Path=(Validation.Errors).CurrentItem, Converter={StaticResource ErrorContentConverter}}"/>
</Trigger>

Alternatively, I thought to use the UpdateSourceExceptionFilter on the Binding. I've implemented a filter as below. This solution is kind of a pain to use because you have to set the UpdateSourceExceptionFilter property in the code behind.

object InnerExceptionFilter(object bindingExpression, Exception exception)
{
    if (exception.InnerException != null)
    {
        var be = (System.Windows.Data.BindingExpression)bindingExpression;
        var rule = be.ParentBinding.ValidationRules.First(x=>x is ExceptionValidationRule);
        return new ValidationError(rule, be, exception.InnerException.Message, exception.InnerException);
    }
    else
        return exception;
}    
usage:
public MyConstructor()
{
    myTextBox.GetBindingExpression(TextBox.TextProperty).ParentBinding.UpdateSourceExceptionFilter
        = new UpdateSourceExceptionFilterCallback(InnerExceptionFilter);
}

The converter is simple but only changes the message that is shown. The filter is a more complete solution but is unfriendly to work with on each binding. Any comments would be greatly appreciated!

Thanks

Terrence