Проверка WPF - отображение ошибок для элементов в StackPanel

У меня проблемы с тем, как отображаются ошибки валидации для элементов TextBox в вертикальной StackPanel. Я пытаюсь отобразить сообщения об ошибках ниже TextBox.

У меня есть этот шаблон ошибки:

    <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>

Если у меня достаточно пустого пространства под TextBox, ошибка отображается нормально, но в StackPanel (например) она не добавляет дополнительного поля или отступов для сообщений об ошибках, когда они есть, из-за слоя adorner.

Ожидается, что так будет, согласно данным этого источника:

Обратите внимание, что Validation.ErrorTemplate будет отображаться на слое Adorner. Элементы в слое adorner визуализируются поверх остальных визуальных элементов, и они не будут учитываться, когда система макетов измеряет и размещает элементы управления на слое украшенных элементов.

Как я могу отобразить сообщения об ошибках валидации, чтобы они не отображались поверх других элементов в StackPanel?

2 ответа

Решение

Вы также можете включить свой шаблон ошибки в шаблон TextBox. Нечто подобное (конечно, его можно улучшить):

<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>

Таким образом, ItemsControl рассматривается для вычисления макета.

Хорошо, я нашел решение с конвертером. Я закончил со стилем, подобным этому:

    <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>

и конвертер:

    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;
    }
Другие вопросы по тегам