Search code examples
.netwpfvalidationxamlidataerrorinfo

Error template is displayed above other controls, when it should be hidden


I'm trying to implement validation in my WPF application using the IDataErrorInfo interface, and I've encountered a not-so-desirable situation.

I have this template which is used when a control fails to validate

<ControlTemplate x:Key="errorTemplate">
    <DockPanel LastChildFill="true">
        <Border Background="Red" DockPanel.Dock="Right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
                                    ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
            <TextBlock Text="!" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" Foreground="White" />
        </Border>
        <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
            <Border BorderBrush="red" BorderThickness="1" />
        </AdornedElementPlaceholder>
    </DockPanel>
</ControlTemplate>

Everything is well until I try to display something above the control that failed validation, such as displaying a dock item above it:

Normal display Display when part of the control is hidden

How can I avoid this and make my error template displayed below the dock item, as it should?

EDIT

I found that I could wrap my TextBox with an AdornerDecorator to fix this, but I really don't want to do this for each and every TextBox control in my application. Is there maybe a way to set it with a Style or some other way?

EDIT 2

I could probably change the default TextBox ControlTemplate to include an AdornerDecorator, but I'm not too keen on changing any of WPF's default control templates. Any other suggestions are welcome.


Solution

  • OK, I found a relatively simple solution which doesn't force me to change any control templates.

    Instead of decorating each TextBox with an AdornerDecorator like this

    <StackPanel>
        <AdornerDecorator>
            <TextBox Text={Binding ...} />
        </AdornerDecorator>
        <AdornerDecorator>
            <TextBox Text={Binding ...} />
        </AdornerDecorator>
    </StackPanel>
    

    I can have the AdornerDecorator wrap my entire view, which achieves the same result.

    <AdornerDecorator>
        <StackPanel>
            <TextBox Text={Binding ...} />
            <TextBox Text={Binding ...} />
        </StackPanel>
    </AdornerDecorator>
    

    This way I can define it at most one time per view.