As Dylan explained, this is because the Adorner layer, in which the validation errors are drawn, is discarded on tab switch. So you need to wrap the content with AdornerDecorator
.
I have created a behavior that wraps the Content
of TabItem
automatically in an AdornerDecorator
, so that it doesn't have to be done manually on all 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;
}
}
}
}
You can set this behavior on all TabItems
via a default style:
<Style TargetType="TabItem">
<Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter>
</Style>
b
is the namespace where the behavior is located, something like this (will be different for every project):
xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling"