Monday, 3 March 2014

Validation adorners not displaying in WPF

The problem

I was developing a WPF application using the IDataErrorInfo interface to perform validation on view models. When errors were encountered I wanted them to be displayed in the UI using adorners to change border appearances and to show error messages. I also wanted these adorners to show when the form first loaded so the user could see straight away what fields needed to be completed.

The problem was that the adorners only appeared after the user changed the values of the validated controls. When the form first loaded things like required fields were not adorned.


Figure 1 – No adorners showing on required fields etc. when the form first loaded.



Figure 2 – The desired result with adorners displayed correctly.

After spending some time checking the behaviour of the application I could see that IDataErrorInfo members were being called when the form loaded and that INotifyPropertyChanged was correctly implemented and the property changed event was being raised correctly.

The solution

The solution was to wrap the form elements in an AdornerDecorator. So, where I previously had a Grid element containing the rows of input controls and labels I wrapped that grid in an AdornerDecorator.

<StackPanel Orientation="Vertical">
    <AdornerDecorator>
        <Grid>
            <Grid.RowDefinitions>
                <!-- Row definitions omitted -->
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <!-- Column definitions omitted -->
            </Grid.ColumnDefinitions>

            <!-- Input controls and labels omitted -->
            
        </Grid>
    </AdornerDecorator>
    
    <!-- Other layout omitted -->
    
</StackPanel>

Explanation

The controls displaying validation errors in the application were based on styles that used the Validation.ErrorTemplate to provide adorned layout to use in the case of errors. For adorners to be displayed there needs to be an AdornerLayer available in the visual tree.

“An Adorner is a custom FrameworkElement that is bound to a UIElement. Adorners are rendered in an AdornerLayer, which is a rendering surface that is always on top of the adorned element or a collection of adorned elements.” [1]

There will be times when there isn’t an AdornerLayer available so you may have to provide one. The way to do this is to add an AdornerDecorator to your XAML remembering that somewhere there will be a call to the static AdornerLayer.GetAdornerLayer method which walks the visual tree looking for an AdornerLayer.

“This static method traverses up the visual tree starting at the specified Visual and returns the first adorner layer found.” [2]
“The AdornerDecorator specifies the position of the AdornerLayer in the visual tree. It is typically used in a ControlTemplate for a control that might host Adorner objects. For example, the ControlTemplate of a Window contains an AdornerDecorator so that the child elements of the window can be adorned. The GetAdornerLayer method returns null if you pass in an element that does not have an AdornerDecorator as an ancestor in its visual tree.” [3]

Note that - as stated above - the AdornerDecorator is typically used in a ControlTemplate. You might want to explore this avenue further.

References

[1] Adorners Overview
[2] AdornerLayer.GetAdornerLayer Method
[3] AdornerDecorator Class