.net - La plantilla de error se muestra sobre otros controles, cuando debería estar oculta
wpf validation (3)
Estoy tratando de implementar la validación en mi aplicación WPF usando la interfaz IDataErrorInfo
, y me he encontrado con una situación no tan deseable.
Tengo esta plantilla que se usa cuando un control no puede validar
<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>
Todo está bien hasta que trato de mostrar algo sobre el control que falló la validación, como mostrar un elemento de acoplamiento encima:
¿Cómo puedo evitar esto y hacer que mi plantilla de error se muestre debajo del elemento de acoplamiento, como debería?
EDITAR
Descubrí que podía envolver mi TextBox
con un AdornerDecorator
para solucionar este problema, pero realmente no quiero hacer esto para todos y cada uno de los controles de TextBox
en mi aplicación. ¿Hay tal vez una forma de configurarlo con un Style
o de alguna otra manera?
Editar 2
Probablemente podría cambiar el TextBox
ControlTemplate predeterminado para incluir un AdornerDecorator
, pero no estoy demasiado interesado en cambiar cualquiera de las plantillas de control predeterminadas de WPF. Cualquier otra sugerencia es bienvenida.
Basado en la gran respuesta de @AdiLester, si sus controles se derivan de una clase base y no desea colocar AdornerDecorator
en XAML de cada control, vaya de esta manera:
public class MyBaseUserControl : UserControl
{
public MyBaseUserControl()
{
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
if (!(newContent is AdornerDecorator))
{
this.RemoveLogicalChild(newContent);
var decorator = new AdornerDecorator();
decorator.Child = newContent as UIElement;
this.Content = decorator;
}
}
}
OK, encontré una solución relativamente simple que no me obliga a cambiar ninguna plantilla de control.
En lugar de decorar cada TextBox
con un AdornerDecorator
como este
<StackPanel>
<AdornerDecorator>
<TextBox Text={Binding ...} />
</AdornerDecorator>
<AdornerDecorator>
<TextBox Text={Binding ...} />
</AdornerDecorator>
</StackPanel>
Puedo hacer que AdornerDecorator
envuelva mi vista completa, lo que logra el mismo resultado.
<AdornerDecorator>
<StackPanel>
<TextBox Text={Binding ...} />
<TextBox Text={Binding ...} />
</StackPanel>
</AdornerDecorator>
De esta manera puedo definirlo como máximo una vez por vista.
Yo usaría un estilo, y aquí hay un ejemplo de uno que puede adaptar fácilmente.
Tenga en cuenta que el ErrorContent proviene de (Validation.Errors) .CurrentItem.ErrorContent en lugar de los Errores [0]. Aunque ambos funcionarán, este último ensuciará su ventana de resultados con excepciones tragadas como se describe aquí .
<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="0,0,16,0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<!--
Error handling
-->
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Text=" *"
Foreground="Red" FontWeight="Bold" FontSize="16"
ToolTip="{Binding ElementName=placeholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="placeholder"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="LightYellow"/>
</Trigger>
</Style.Triggers>
</Style>