I have a TextBox with a validation rule that is on a tab of a TabControl. The default ErrorTemplate correctly shows (red border around TextBox) when the validation rule fail
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:
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"