Microsoft Developer Network > Domovská stránka fór > Windows Presentation Foundation (WPF) > Custom ValidationRule throws ArgumentOutOfRangeException
Odeslat dotazOdeslat dotaz
 

OdpovědětCustom ValidationRule throws ArgumentOutOfRangeException

  • 17. srpna 2007 9:49planetmarshalluk Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     
    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

    Code Snippet

    public 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;
    }

    }



    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 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.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>


    Validation is performed as expected, and the tooltip is correctly set. However, when the ValidationResult changes, the following error is reported


    Code Snippet

    System.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.

Odpovědi

Všechny reakce

  • 17. srpna 2007 17:58Sam Bent - MSFT Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     
    This is a known bug.  Sorry, I don't have a workaround offhand.

     

  • 20. srpna 2007 8:30planetmarshalluk Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Odpovědět
    Well at least I know I'm not doing anything stupid. I'll hope for a bug fix in the next service pack Smile

    Thanks,
    Andrew.
  • 20. srpna 2007 17:57Sam Bent - MSFT Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     
    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.
  • 30. října 2007 12:59Martin Rasmusson Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     

     

    This is very unfortunate news, since he certainly is not the only one with these problems.

  • 29. listopadu 2007 7:06Scott Reed Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     

     

    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

  • 29. listopadu 2007 17:47Sam Bent - MSFT Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     

    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.

  • 17. dubna 2008 20:58Pixe1grass Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Navržená odpověď
    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)


    • Navržen jako odpověďPixe1grass 23. června 2008 20:18
    •  
  • 9. září 2008 5:09HappyNomad Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Obsahuje kód
    Here's the code I wrote for the workaround converter:
    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'm also disappointed this bug hasn't been fixed.  Could someone post the link to this issue's location on Microsoft Connect?

  • 28. října 2008 6:58Jenkis Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     
    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.");
            }
        }
    }



  • 11. března 2009 21:07a_romanb Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Navržená odpověď
    You can just use:

    Path=(Validation.Errors).CurrentItem.ErrorContent

    That works fine for me.
    • Navržen jako odpověďJohn J._ 26. března 2009 20:48
    •  
  • 29. dubna 2009 0:43ausadmin Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     
    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
  • 6. července 2009 17:07Sam Bent - MSFT Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     

    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