Search code examples
wpfvalidationxamlerrortemplate

WPF validation - displaying errors for elements in StackPanel


I'm having problems with how validation errors are displayed for TextBox elements in vertical StackPanel. I'm trying to display error messages below TextBox.

I have this error template:

    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel>
                        <AdornedElementPlaceholder />
                        <ItemsControl ItemsSource="{Binding}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding (ValidationError.ErrorContent)}" Foreground="Red" Margin="5"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

If I have enough white space below the TextBox, error is displayed fine, but in StackPanel (for example), it does not add extra margin or padding for error messages when there are some, because of adorner layer.

It is expected to be so, according to this source:

Note that the Validation.ErrorTemplate will be displayed on the adorner layer. Elements in the adorner layer are rendered on top of the rest of the visual elements and they will not be considered when the layout system is measuring and arranging the controls on the adorned element layer.

How can I display validation error messages, so that they won't show over other elements in StackPanel?


Solution

  • You can also consider to include your error template in the TextBox's template. Something like that (of course it can be improved):

    <Style x:Key="eTextBox" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <StackPanel>
                        <Border BorderBrush="Gray" BorderThickness="1" CornerRadius="1" Padding="2">
                            <ScrollViewer Name="PART_ContentHost" Focusable="False" 
                                        ScrollViewer.HorizontalScrollBarVisibility="Hidden"
                                        ScrollViewer.VerticalScrollBarVisibility="Hidden"
                                        Background="#00FFFFFF" />
                        </Border>
                        <ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(Validation.Errors)}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding (ValidationError.ErrorContent)}" Foreground="Red" Margin="5"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
    
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    In this way the ItemsControl is considered for the layout computation.