Custom ValidationRule throws ArgumentOutOfRangeException
- Hi there.
I've been developing a custom ValidationRule, following the MS example by the letter but it is throwing an ArhumentOutOfRangeException during the validation process.
I have the following ValidationRule
A custom TextBox derived control uses the validation rule, as follows. The MyTextBox.Text property is bound to the MyText property on the DataContext object
Code Snippetpublic class MyValidationRule : ValidationRule
{
public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo cultureInfo){...
if ( errorCondition1 )return new ValidationResult(false);
if ( errorCondition2)return new ValidationResult(false,"Error 2");
return ValidationResult.ValidResult;}}
Code Snippet<local:MyTextBox x:Name="_myTextBox" Validation.ErrorTemplate="{StaticResource TextBoxValidationTemplate}" Style="{StaticResource TextBoxInError}"><local:MyTextBox.Text><Binding Path="MyText" UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:MyValidationRule/></Binding.ValidationRules></Binding></local:MyTextBox.Text></local:MyTextBox>
The TextBoxInError style is defined as follows
Code Snippet<Style x:Key="TextBoxInError" TargetType="{x:Type local:MyTextBox}"></Style>
<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>
Validation is performed as expected, and the tooltip is correctly set. However, when the ValidationResult changes, the following error is reported
Code SnippetSystem.Windows.Data Error: 12 : Cannot get '' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1'). BindingExpression:Path=(0).[0].ErrorContent; DataItem='MyTextBox' (Name='_myTextBox'); target element is 'MyTextBox' (Name='_myTextBox'); 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 System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'It seems that accessing ValidationErrors[0] is the cause of the exception, but I cannot see how this is occuring since that property is only accessed when an Error exists.
Any pointers appreciated.
答案
- Well at least I know I'm not doing anything stupid. I'll hope for a bug fix in the next service pack

Thanks,
Andrew.
全部回复
- This is a known bug. Sorry, I don't have a workaround offhand.
- Well at least I know I'm not doing anything stupid. I'll hope for a bug fix in the next service pack

Thanks,
Andrew. - I'm afraid you'll be disappointed. We've known about this one for a while, but there were no reports from customers (until yours) so it was deemed low-priority. So now it's too late for the next service pack; best you can hope for is the one after that. Sorry.
This is very unfortunate news, since he certainly is not the only one with these problems.
Sam,
Can you give us more information about the bug? Are there any updates as to when it will be fixed?
I am having the same problem, but without the custom validation rule. I am using a simple ExceptionValidationRule, yet my tooltip is being populated with a TargetInvocationException.
The weird thing is, I downloaded the PaulStovell Validation code, and that seems to work fine. So there has to be a workaround of some type?
-Scott
Warning: this gets technical.
It all depends on how the binding writes the new value back to the source property. Sometimes it uses a PropertyInfo, calling propertyInfo.SetValue(item, value), and sometimes it uses a PropertyDescriptor, calling propertyDescriptor.SetValue(item, value). Both cases eventually call the source property's setter, which might throw an exception. In the former case the exception bubbles up directly to the binding code, where it is caught and placed in a ValidationError object. In the latter case, the PropertyDescriptor code catches the exception and wraps it in a TargetInvocationException, which is what the binding code catches and places in the ValidationError.
The choice between PropertyInfo and PropertyDescriptor is complicated, and is somewhat of an implementation detail (i.e. nothing I'm about to say should be taken as a contract). Roughly speaking, if the source object has a public setter for the property, and also implements INotifyPropertyChanged, we choose PropertyInfo. Otherwise we choose PropertyDescriptor. (It's actually more complicated, because we do something different if the source property is a DependencyProperty, we have to support synthetic properties implemented via ICustomTypeDescriptor or TypeDescriptorProvider, etc.)
Chances are that Stovell's validation code is changing something that influences the choice - perhaps adding INotifyPropertyChanged - and thus removing PropertyDescriptor from the equation.
The bug is on my radar to fix for the next release after Orcas, but it's competing with a lot of other stuff so there's no guarantee. There's a non-trivial compat question to be answered: if we drill into the TargetInvocationException to fill the ValidationError with the original exception, will we break existing apps that expect to see TargetInvocationException? If anyone owns such an app, I'd appreciate hearing about it.
- I have noticed a large performance hit when the validation starts spitting out exceptions,
I'm rather disappointed to see this has not been addressed in almost a year now.
However this is the fix I came up with :
Change :
Code Snippet<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
To:
Code Snippet<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors), Converter={StaticResource ErrorsToString}}" />
Passing the "validation.errors" read only collection to a converter
(converter code is simply a check to make sure there is an array object before attempting to access it)- 已建议为答案Pixe1grass 2008年6月23日 20:18
- Here's the code I wrote for the workaround converter:I'm also disappointed this bug hasn't been fixed. Could someone post the link to this issue's location on Microsoft Connect?
public class ErrorsToStringConverter : IValueConverter { public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) { IList<ValidationError> validationErrors = (IList<ValidationError>)value; return validationErrors.Count == 0 ? "" : validationErrors[0].ErrorContent; } public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) { throw new Exception( "The method or operation is not implemented." ); } }
- I have an application where this problem arises.
I am using LinqToSQL, which implements INotifyPropertyChanging, INotifyPropertyChanged.
I am implementing some validation logic in partial methods. eg
partial void OnNoOfTanksChanging(int value)
{
if (value > 2)
{
throw new ApplicationException("Value must be <= 2");
}
}
I have the following :
<ResourceDictionary>
<local:ErrorsToString x:Key="ErrorsToStringConverter" ></local:ErrorsToString>
</ResourceDictionary>
<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
.....
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors),
Converter={StaticResource ErrorsToStringConverter}}" />
</Trigger>
</Style.Triggers>
public class ErrorsToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IList<ValidationError> validationErrors = (IList<ValidationError>)value;
return validationErrors.Count == 0 ? "" : validationErrors[0].ErrorContent;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
}
- You can just use:
Path=(Validation.Errors).CurrentItem.ErrorContent
That works fine for me.
- 已建议为答案John J._ 2009年3月26日 20:48
- I have also encountered this problem. I changed my xaml code as suggested by a_romanb:
ToolTip ="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}">
This seems to have resolved the problem - can anyone comment as to whether this is a recommended solution to the problem
Thanks,
Tim Using CurrentItem is certainly more robust than using a fixed index. BTW, WPF 4.0 will have fixes for this issue.
Dev Lead, Windows Presentation Foundation, WinFX

