Quando si utilizza un custom ErrorTemplate per segnalare eventuali errori di validazione bisogna tenere in considerazione alcuni effetti secondari derivanti dal suo utilizzo.
Consideriamo il caso più semplice: Una textbox bindata ad una proprietà di un ViewModel che implementa IDataErrorInfo la cui parte di validazione è la seguente:

   1: #region IDataErrorInfo Members
   2:  
   3: public string Error
   4: {
   5:    get { throw new NotImplementedException(); }
   6: }
   7:  
   8: public string this[string columnName]
   9: {
  10:    get
  11:    {
  12:       if (columnName == "Name")
  13:       {
  14:          return string.IsNullOrEmpty(this.Name) ? "Name is mandatory" : null;
  15:       }
  16:  
  17:       return null;
  18:    }
  19: }
  20:  
  21: #endregion

in pratica viene verificato che la proprietà Name sia valorizzata.
La View è collegata alla proprietà Name in questo modo:

   1: <Window.Resources>
   2:      <ControlTemplate x:Key="ValidationTemplate">
   3:         <DockPanel>
   4:            <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
   5:            <AdornedElementPlaceholder />
   6:         </DockPanel>
   7:      </ControlTemplate>
   8:   </Window.Resources>
   9: ...
  10: <TextBox Height="23"
  11:                            Margin="38,37,56,0"
  12:                            Name="textBox1"
  13:                            Validation.ErrorTemplate="{StaticResource ValidationTemplate }"
  14:                            Text="{Binding Name, ValidatesOnDataErrors=True}"
  15:                            VerticalAlignment="Top" />

ottenendo un risultato simile a quello riportato di seguito:
image

Tutto come da manuale…, proviamo però a impostare a zero l’Opacity del Tabcontrol che contiene la textbox e il risultato è il seguente:

image

Che non è quello che il manuale prevede…

Il motivo dipende dal fatto che l’ErrorTemplate viene “renderizzato” nell’AdornerLayer esposto dall’AdornerDecorator che contiene la client area della nostra finestra, in parole povere: Sta in un layer che è ‘sopra’ il TabControl con Opacity=0.

image 
La soluzione che viene subito in mente per ovviare il problema è quella di ‘resettare’ l’errore prima di azzerare l’opacity, se non fosse che in realtà abbiamo un altro problema da risolvere: il fatto che cambiando Tab mentre è visualizzato un errore quando si ritorna sulla tab precendete l’errore di validazione non è più visibile smile_omg (provare per credere…)
La soluzione fortunatamente è abbastanza semplice (quando qualcuno te la suggerisce): Fornire un AdornerDecorator specifico che verrà utilizzato in sostituzione di quello presente nella Window, ad esempio modificare lo xaml in questo modo:

   1: <TabItem Header="tabItem1"
   2:                      Name="tabItem1">
   3:             <AdornerDecorator>
   4:                <Grid x:Name="tab1Grid">
   5:                   <TextBox Height="23"
   6:                            Margin="38,37,56,0"
   7:                            Name="textBox1"
   8:                            Validation.ErrorTemplate="{StaticResource ValidationTemplate }"
   9:                            Text="{Binding Name, ValidatesOnDataErrors=True}"
  10:                            VerticalAlignment="Top" />
  11:                </Grid>
  12:             </AdornerDecorator>
  13:          </TabItem>

risolvendo entrambi i problemi.

Kudos to Karl for the hint