2012-04-24 20 views
13

Estoy tratando de implementar la validación en mi aplicación WPF usando la interfaz IDataErrorInfo, y he encontrado una situación no tan deseada.La plantilla de error se muestra encima de otros controles, cuando debería estar oculta

tengo esta plantilla que se utiliza 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 intento mostrar algo por encima del control que falló la validación, por ejemplo, mostrar un elemento de muelle por encima de ella:

Normal display Display when part of the control is hidden

¿Cómo puedo evitar esto y hacer mi plantilla de error que aparece debajo del elemento de muelle, como debe ser?

EDITAR

me encontré que podría envolver mi TextBox con un AdornerDecorator de solucionar este problema, pero realmente no quiero hacer esto para todos y cada uno TextBox de control en mi solicitud. ¿Hay alguna forma de configurarlo con un Style o de otra forma?

EDIT 2

probablemente podría cambiar el valor predeterminado ControlTemplate TextBox incluir un AdornerDecorator, pero no estoy demasiado interesado en el cambio de cualquiera de las plantillas de control por defecto de WPF. Cualquier otra sugerencia es bienvenida.

Respuesta

10

Bien, 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 esto

<StackPanel> 
    <AdornerDecorator> 
     <TextBox Text={Binding ...} /> 
    </AdornerDecorator> 
    <AdornerDecorator> 
     <TextBox Text={Binding ...} /> 
    </AdornerDecorator> 
</StackPanel> 

que puede tener el AdornerDecorator envolver toda mi punto de vista, que logra el mismo resultado.

<AdornerDecorator> 
    <StackPanel> 
     <TextBox Text={Binding ...} /> 
     <TextBox Text={Binding ...} /> 
    </StackPanel> 
</AdornerDecorator> 

De esta forma puedo definirlo a lo sumo una vez por vista.

0

Usaría un estilo, y aquí hay un ejemplo de uno que puede adaptar fácilmente.

Tenga en cuenta que ErrorContent proviene de (Validation.Errors) .CurrentItem.ErrorContent en lugar de Errors [0]. Aunque ambos funcionarán, este último arrojará basura a su ventana de salida con excepciones anuladas como outlined here.

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

no lo hago mira cómo esto resuelve algo. El borde todavía se muestra encima del elemento de la base. –

2

Basado en @AdiLester gran respuesta, si los controles se derivan de una clase base y que no quieren poner en XAML AdornerDecorator de cada control, y luego ir 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; 
     } 
    } 
} 
Cuestiones relacionadas