Binding to (Validation.Errors)[0] without Creating Debug Spew

Microsoft’s documentation that shows how to display validation errors for elements says to use a binding like this:

This works, but it causes reams of debug spew to fill the Output window.  Why?  Because is tries to access the first item in the Validation.Errors attached property collection, but if there are no errors, an exception is thrown.  Here’s the type of debug spew that fills the output window:

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=’symbolTxt’); target element is ‘TextBox’ (Name=’symbolTxt’); 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)’

I use the Output window all the time for debugging purposes, so having it fill up with these error messages breaks my workflow.  I decided to find a better way to display error messages, such that the Output window is not clogged with garbage.  Here’s what I came up with:

When you run my demo app, it looks like this:

The trick is to bind a ContentPresenter’s Content to the CurrentItem of Validation.Errors for the target element.   Binding to the CurrentItem means that we’re  binding to the CurrentItem property of the default ICollectionView that wraps the ReadOnlyObservableCollection<ValidationError> returned by the attached Errors property.  When the current item is non-null, that means there is a validation error; when it is null, there are no validation errors.  We can rely on ICollectionView to safely access the validation error, or not return anything if there are no errors.  That is what prevents the debug spew from pouring out.

The DataTemplate declared in the StackPanel’s Resources knows how to render a ValidationError object.  If the ContentPresenter has a null value, the template is not used to render anything.   That template could also render in a tooltip, if you prefer to keep with Microsoft’s example of showing validation error messages in a tooltip.

Download the demo project here.  Be sure to change the file extension from .DOC to .ZIP and then decompress the file.

About these ads

22 Responses to Binding to (Validation.Errors)[0] without Creating Debug Spew

  1. Mr. WPF is at it AGAIN!

  2. Josh Smith says:

    :) Yeah, after a long blog-hiatus, I’m back.

  3. Josh Smith says:

    Thanks Rudi. It took me a while to solve this puzzle, but it was worth the effort!

    Josh

  4. Walt Ritscher says:

    Very useful tip. I know that I’ll be using this in the future.

  5. Josh Smith says:

    Glad to help out, Walt. Thanks for the feedback!

    Josh

  6. Corrado Cavalli says:

    Mr. WPF :-)
    Thanks for the tip, this will become my preferite way of displaying error informations.

  7. [...] Bowen details Syntax for Nested Attached Properties Josh Smith on Binding to (Validation.Errors)[0] without Creating Debug Spew Pavan Podila notes Generic.xaml must have a build action of Page Rudi Grobler documents Learning [...]

  8. Fabrice says:

    At last, a solution to this problem! It seems that everything can be achieved in WPF and XAML, provided that Mr Smith is around to show us the way ;-)

  9. John Mairs says:

    Hi,

    How would you add a BindingGroup policy to this example to say…not allow a 2nd additional symbol below the first one to not be the same as symbol 1?

    I was using tooltip errors but have switched over to “your” style instead. I like the dynamic as you type response in addition to the window resizing to accomodate the error message.

    Enjoy your weekend…and stay away from the keyboard.

  10. Josh Smith says:

    John,

    That rule (no duplicate symbols allowed) should be handled by the data model or, if you’re using MVVM, perhaps the ViewModel. In my demo app, that means the Foo class, assuming it had a Symbol1 and Symbol2 property.

    Josh

  11. John Mairs says:

    Ok I do have Symbol1 and Symbol2 in my data model as properties. When either one is changed I call SendPropertyChanged(“Symbol1″) or “Symbol2″ as appropriate. I can easily add logic to block the a set call if the properties are the same but how do I propagate the “error” back to the UI. I haven’t downloaded your example…perhaps I should.

  12. Josh Smith says:

    You tell the UI about validation errors via IDataErrorInfo.

  13. John Mairs says:

    I should also note that my symbols are actually UDP port numbers in a simple startup configuration dialog. The user must enter to 2 unique port numbers to get the system started, else if they hit cancel on the dialog we don’t run.

    Thanks,

    John

  14. John Mairs says:

    Man your fast. :-)

  15. Hi Josh – nice tip. I’ve often wondered why most examples show the Errors[0] method. Personally I use the forward-slash as the abbreviation for “CurrentItem”, e.g. Path=(Validation.Errors)/

    I just posted an example (based on the one you provided) on my blog.

  16. John Mairs says:

    I’m almost there, home base in view. I have IDataErrorInfo successfully updating my dialog but my last hurdle appears to be NOT allowing the user to close the dialog until both port numbers are unique.

    When I set the port numbers the same, I get the cool italic error message but I’m still allowed to close the dialog via the Ok button.

    My OK event handler loops through each DataBinding individually but I’m missing the case where the Data Object on the whole is invalid.

    Thanks,

    John

  17. Josh Smith says:

    You can have a method/property called IsValid which calls into the IDataErrorInfo on that object, passing in the name of each property that is validated. If any of them return an error message, return false from the method/property.

  18. John Mairs says:

    If you ever write a book I promise I’ll buy it.

  19. peteohanlon says:

    Josh – very nice, and so blindingly obvious when you point it out. Doh!

  20. Yogesh says:

    Even this perfect:
    <Style x:Key=”textBoxInError” TargetType=”{x:Type TextBox}”>
    <Style.Triggers>
    <Trigger Property=”Validation.HasError” Value=”true”>
    <Setter Property=”ToolTip”
    Value=”{Binding RelativeSource={x:Static RelativeSource.Self},
    Path=(Validation.Errors).CurrentItem.ErrorContent}”/>
    </Trigger>
    </Style.Triggers>
    </Style>

    Great find Josh. :)

Follow

Get every new post delivered to your Inbox.

Join 285 other followers

%d bloggers like this: