2.7 KiB
WPF Binding stops working after an item container leaves the visual tree
Symptoms
This issue affects bindings whose path contains an explicit use of the DataContext
property of an element that belongs to the subtree generated for an item within an
System.Windows.Controls.ItemsControl
. When the element leaves the visual tree,
the binding stops working; any subsequent changes to other properties mentioned in
the path are ignored.
The visible symptoms depend on how the binding is used. For example, an app
may want to share a System.Windows.Controls.ContextMenu
among the
item containers within an System.Windows.Controls.ItemsControl
, and have the
content and behavior of the menu depend on the item container for which it is
displayed. This can be implemented by declaring the menu as a resource and binding its
DataContext
to that of its PlacementTarget
:
<UserControl.Resources>
<ContextMenu x:Key="SharedContextMenu"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.DataContext}">
... other content ...
</ContextMenu>
</UserControl.Resources>
Then refer to the resource in the appropriate data template:
<DataTemplate>
<StackPanel ContextMenu="{StaticResource SharedContextMenu}">
... other content ....
</StackPanel>
</DataTemplate>
When the user right-clicks the StackPanel
for a particular data item, the shared context menu
is displayed, its PlacementTarget
is changed to be the StackPanel
, the binding sets its
DataContext
to the data item, and the menu's content and behavior can depend on the data item.
The issue arises if the app removes the item container from the visual tree while the menu
is visible. (There are many ways this can happen: removing the data item from the underlying
collection, replacing the ItemsSource
, scrolling enough to re-virtualize the data item, etc.)
When this happens, the binding stops working, and any subsequent changes to the PlacementTarget
property are ignored. The visible symptom is that right-clicking a different data item
will bring up the menu and reset its PlacementTarget
, but its content and behavior are
still attached to the original item rather than to the newly-chosen one.
Cause
When an item container leaves the visual tree, its DataContext
is changed to {DisconnectedItem}
.
The binding recognizes this sentinel value and suppresses the normal data transfer, but
mistakenly leaves itself in a state where it ignores subsequent changes to other properties
along the path (such as PlacementTarget
in the example).
Resolution
A hotfix for this issue is planned for .Net Framework 4.8. This page will be updated with a link to the hotfix when it becomes available.