I have an issue very similar to these:
Issue with WPF validation(IDataErrorInfo) and tab focusing
TextBox with validation loses ErrorTemplate on tab change
AdornerDecorator
do the trick within the same instance of the Window
, but when the Window
is reloaded and I switch to the TabItem
containing the TextBox
in error, the ErrorTemplate
won't show up anymore.
<Window x:Class="Views.MyWindowView">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl HorizontalAlignment="Stretch"
Height="Auto"
VerticalAlignment="Top"
Width="Auto"
SelectionChanged="TabItemChanged"
Name="MyTabControl">
<!-- Below, AdornerDecorator are added for the following reason:
the Validation.Error cues are painted in the Adorner Layer.
When tabs are switched, that layer is discarded. -->
<!-- The view 1 tab.-->
<TabItem Header="{Resx tab1_Header}"
Name="Tbi1">
<AdornerDecorator>
<vw:MyView1 DataContext="{Binding}"/>
</AdornerDecorator>
</TabItem>
<!-- The view 2 tab.-->
<TabItem Header="{Resx tab2_Header}"
Name="Tbi2">
<AdornerDecorator>
<vw:MyView2 DataContext="{Binding}"/>
</AdornerDecorator>
</TabItem>
</TabControl>
...
I tried to retrigger the validation in the code-behind on TabControl
SelectionChanged
, didn't work.
Any idea?
Putting together the pieces of the puzzle
An AdornerLayer represents a surface for rendering adorners.
As an AdornerLayer
usually serves an entire view, not just one control, some containers implement them by default.
An adorner is a custom FrameworkElement
that is bound to a UIElement
. Adorners are rendered in an AdornerLayer
, which is a rendering surface that is always on top of the adorned element or a collection of adorned elements.
So in this case the adorner (red rectangle) is bound to a TextBox
, but is rendered in a layer on top of the TextBox
.
Adorning (e.g. in case of a validation error) is done by calling the static method GetAdornerLayer
to get an AdornerLayer
object for the UIElement
to be adorned.
Enough theory
Changing TabItems
discards the AdornerLayer
, resulting in the adorner not being drawn. 2 fixes:
\The manual way, as proposed by DRapp:
<XAML for MyView1>
<AdornerDecorator>
...
</AdornerDecorator>
</close XAML for MyView1>
Of course, if there's another container implementing an AdornerLayer
between the AdornerDecorator
and the TextBox
(in the visual tree), this won't do any good. So the explicit AdornerDecorator
needs to be the last one wrapping the TextBox
.
<XAML for MyView1>
<Grid>
...
<GroupBox>
<AdornerDecorator>
<Grid>
...
<TextBox ... />
</Grid>
</AdornerDecorator>
</GroupBox>
</Grid>
</close XAML for MyView1>
\Second solution (which I prefer) resetting the ErrorTemplate each time the TextBox
becomes visible. In doing so the lack of AdornerLayer
gets spotted and fixed.
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsVisible" Value="true">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>