0

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?

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

Comments

0

Ok, I found the solution with converter. I ended up with style similar to this:

    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Margin" Value="10" />
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel>
                        <AdornedElementPlaceholder />
                        <ItemsControl ItemsSource="{Binding}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding (ValidationError.ErrorContent)}" Foreground="Red" Margin="0,5"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Trigger.Setters>
                    <Setter Property="Margin" Value="{Binding (Validation.Errors).Count, RelativeSource={RelativeSource Self}, Converter={StaticResource ErrorsToMarginConverter}}"/>
                </Trigger.Setters>
            </Trigger>
        </Style.Triggers>
    </Style>

and converter:

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is int)
        {
            var errors = (int)value;
            return new Thickness(10, 10, 10, (errors * 20));
        }

        return value;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.