2012-03-28 15 views
27

Tengo un TextBox con una regla de validación que está en una pestaña de un TabControl. El ErrorTemplate predeterminado muestra correctamente (borde rojo alrededor de TextBox) cuando falla la regla de validación.
Sin embargo, si hay un cambio a otra pestaña y luego regresa a la pestaña con el TextBox, el hightlight ErrorTemplate se ha ido. Si hay un cambio en el cuadro de texto, la regla de validación aún se llama y devuelve falso, pero el resaltado de error aún no se muestra.
Solo cuando el contenido del texto se cambia para que sea válido y, una vez más, para que no sea válido, vuelve a aparecer el marcador.
Me gustaría que, si el contenido del texto no es válido, el cambio a otra pestaña y el reverso mantengan el resaltado no válido. Cualquier idea para obtener este comportamiento es bienvenida.
El xaml:TextBox con validación pierde ErrorTemplate en la pestaña cambiar

<TextBox Height="35" > 
    <TextBox.Text> 
    <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged"> 
     <Binding.ValidationRules> 
     <ps:PanIdValidation /> 
     </Binding.ValidationRules> 
    </Binding> 
    </TextBox.Text> 
</TextBox> 
+0

¿Estás utilizando MVVM? – Paparazzi

Respuesta

47

TabItem debe definirse como sigue:

<TabItem Header="Foo"> 
    <Border> 
     <AdornerDecorator> 
      <Grid> 
       <TextBox Height="35" > 
        <TextBox.Text> 
         <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged"> 
          <Binding.ValidationRules> 
           <ps:PanIdValidation /> 
          </Binding.ValidationRules> 
          </Binding> 
         </TextBox.Text> 
        </TextBox> 
       </Grid> 
      </AdornerDecorator> 
     </Border> 
    </TabItem> 

La cuestión es, las señales Validation.Error están pintadas en la capa Adorner. Cuando cambias de pestaña, esa capa se descarta.

+0

Perfecto - gracias. – Ricibob

+0

¡increíble! también funciona cuando se envuelven varios controles que comparten los mismos atributos de estilo. – Salty

+0

¡Esto me ahorró mucho tiempo, gracias! – akagixxer

7

Solo una adición para casos especiales: estaba teniendo un problema similar y ahora estoy usando una solución similar al código de Dylan.

La diferencia es que mi TabItem contiene GroupBox y el TextBox está dentro de él. En este caso, AdornerDecorator debe estar en el GroupBox en sí mismo, no como un descendiente directo del TabItem.

Así que esto no funcionó:

<TabItem> 
    <AdornerDecorator> 
     <Grid> 
      <GroupBox> 
       <Grid> 
        <TextBox>...<TextBox/> 
       </Grid> 
      </GroupBox> 
     </Grid> 
    </AdornerDecorator> 
</TabItem> 

Pero esto lo hicieron:

<TabItem> 
    <Grid> 
     <GroupBox> 
      <AdornerDecorator> 
       <Grid> 
        <TextBox>...<TextBox/> 
       </Grid> 
      </AdornerDecorator> 
     </GroupBox> 
    </Grid> 
</TabItem> 

Estoy añadiendo que porque no podía encontrar la solución con facilidad e incluso la documentación de AdornerLayer.GetAdornerLayer() (aunque no estoy seguro de si es aplicable aquí) estados This static method traverses up the visual tree starting at the specified Visual and returns the first adorner layer found. - pero tal vez también se detiene en algún momento, esto no está claro en los documentos.

4

Como explicó Dylan, esto se debe a que la capa de Adorner, en la que se extraen los errores de validación, se descarta en el interruptor de tabulación. Por lo tanto, debe envolver el contenido con AdornerDecorator.

He creado un comportamientoque envuelve el Content de TabItem automáticamente en un AdornerDecorator, por lo que no tiene que hacer manualmente en todos los TabItems.

public static class AdornerBehavior 
{ 
    public static bool GetWrapWithAdornerDecorator(TabItem tabItem) 
    { 
     return (bool)tabItem.GetValue(WrapWithAdornerDecoratorProperty); 
    } 
    public static void SetWrapWithAdornerDecorator(TabItem tabItem, bool value) 
    { 
     tabItem.SetValue(WrapWithAdornerDecoratorProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for WrapWithAdornerDecorator. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty WrapWithAdornerDecoratorProperty = 
     DependencyProperty.RegisterAttached("WrapWithAdornerDecorator", typeof(bool), typeof(AdornerBehavior), new UIPropertyMetadata(false, OnWrapWithAdornerDecoratorChanged)); 

    public static void OnWrapWithAdornerDecoratorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
     var tabItem = o as TabItem; 
     if (tabItem == null) return; 

     if(e.NewValue as bool? == true) 
     { 
      if (tabItem.Content is AdornerDecorator) return; 
      var content = tabItem.Content as UIElement; 
      tabItem.Content = null; 
      tabItem.Content = new AdornerDecorator { Child = content }; 
     } 
     if(e.NewValue as bool? == false) 
     { 
      if (tabItem.Content is AdornerDecorator) 
      { 
       var decorator= tabItem.Content as AdornerDecorator; 
       var content = decorator.Child; 
       decorator.Child = null; 
       tabItem.Content = content; 
      } 
     } 
    } 
} 

Puede configurar este comportamiento en todas TabItems a través de un estilo por defecto:

<Style TargetType="TabItem"> 
    <Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter> 
</Style> 

b es el espacio de nombres donde se encuentra el comportamiento, algo como esto (será diferente para cada proyecto):

xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling" 
Cuestiones relacionadas