wpf - validate - TextBox con validación pierde ErrorTemplate en cambio de pestaña
wpf combobox validate (3)
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 luego, nuevamente, 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 Height="35" >
<TextBox.Text>
<Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ps:PanIdValidation />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Como Dylan explicó, 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. Entonces, debe envolver el contenido con AdornerDecorator
.
TabItem
un comportamiento que envuelve automáticamente el Content
de TabItem
en AdornerDecorator
, de modo que no tiene que hacerse 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 establecer este comportamiento en todos los TabItems
través de un estilo predeterminado:
<Style TargetType="TabItem">
<Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter>
</Style>
b
es el espacio de nombres donde se ubica el comportamiento, algo así (será diferente para cada proyecto):
xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling"
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.
Entonces esto no funcionó:
<TabItem>
<AdornerDecorator>
<Grid>
<GroupBox>
<Grid>
<TextBox>...<TextBox/>
</Grid>
</GroupBox>
</Grid>
</AdornerDecorator>
</TabItem>
Pero esto hizo:
<TabItem>
<Grid>
<GroupBox>
<AdornerDecorator>
<Grid>
<TextBox>...<TextBox/>
</Grid>
</AdornerDecorator>
</GroupBox>
</Grid>
</TabItem>
Lo estoy agregando porque no pude encontrar la solución fácilmente e incluso la documentación de AdornerLayer.GetAdornerLayer()
(aunque no estoy seguro 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.
TabItem se debe definir de la siguiente manera:
<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>
El problema es que las claves de Validación.Error están pintadas en la capa de Adorner. Cuando cambias de pestaña, esa capa se descarta.