'Omitting the TargetName from a Setter causes a BindingError on its DataTrigger if the control is initially invisible
I am re-templating the WPF TabItem and I have come across a phenomenon I cannot explain. The Template has a DataTrigger that is supposed to change the Control's Background when the mouse is over it:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=border, Path=IsMouseOver}" Value="true">
<Setter Property="Background" Value="Azure" />
</DataTrigger>
</ControlTemplate.Triggers>
border is a Border within the ControlTemplate.
This, however, causes a Binding Error if the TabItem is set to Visibility.Collapsed initially (the TabVisibility property can be toggled with a Button):
<TabControl>
<TabItem Header="One">
<Button Content="Hello1!" />
</TabItem>
<TabItem Header="Two">
<Button Content="Hello2!" />
</TabItem>
<TabItem Header="Four" Visibility="{Binding TabVisibility}">
<Button Content="Hello4!" />
</TabItem>
<TabItem Header="Five" Visibility="{Binding TabVisibility}">
<Button Content="Hello5!" />
</TabItem>
</TabControl>
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=border'. BindingExpression:Path=IsMouseOver; DataItem=null; target element is 'TabItem' (Name=''); target property is 'NoTarget' (type 'Object')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=border'. BindingExpression:Path=IsMouseOver; DataItem=null; target element is 'TabItem' (Name=''); target property is 'NoTarget' (type 'Object')
The resulting behavior is that the MouseOver effect works as desired for the two TabItems that are initially visible, but does not work for the latter two TabItems that were made visible later on: there simply is no MouseOver effect for these two.
When I now add a TargetName to the Setter, the BindingErrors vanish and even the initially collapsed TabItems show a correct MouseOver effect when their Visibility gets toggled:
<DataTrigger Binding="{Binding ElementName=border, Path=IsMouseOver}" Value="true">
<Setter TargetName="border" Property="Background" Value="Azure" />
</DataTrigger>
Note that the only difference between this and the first shown trigger is TargetName="border". The Binding remains unchanged.
Why and how does the TargetName on the Setter affect wether or not the DataTrigger's Binding breaks?
The Trigger with the TargetName solves the technical problem that I faced. I would like to understand why it does so.
My hypothesis is as follows: Apparently WPF skips loading the ControlTemplate when a Control is initially invisible. For the first Trigger, it still tries to evaluate the Binding and fails because the ControlTemplate is not yet applied and no Control named border exists yet. For the second Trigger, WPF must somehow realize that it should not yet evaluate the Binding and waits for the ControlTemplate to be applied. But I am stumped as to how WPF knows the difference and why it even tries to evaluate the Binding in the first Trigger, considering it's part of ControlTemplate.Triggers and the ControlTemplate has, presumably, not been loaded.
Any help is greatly appreciated!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
