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:
Tutto come da manuale…, proviamo però a impostare a zero l’Opacity del Tabcontrol che contiene la textbox e il risultato è il seguente:
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.
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 (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