Проверка 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;
}