Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ internal class ElementProxy: IRawElementProviderFragmentRoot, IRawElementProvide
private ElementProxy(AutomationPeer peer)
{
if ((AutomationInteropReferenceType == ReferenceType.Weak) &&
(peer is UIElementAutomationPeer || peer is ContentElementAutomationPeer || peer is UIElement3DAutomationPeer))
(peer is UIElementAutomationPeer || peer is ContentElementAutomationPeer || peer is UIElement3DAutomationPeer
|| peer.ShouldUseWeakReferenceFromElementProxy))
{
_peer = new WeakReference(peer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,13 @@ internal virtual bool IsDataItemAutomationPeer()
return false;
}

// When true, ElementProxy holds this peer via WeakReference. Opt-in fix for the
// UIA-retained-ElementProxy leak in virtualized ItemsControls (see ItemAutomationPeer).
internal virtual bool ShouldUseWeakReferenceFromElementProxy
{
get { return false; }
}

// UpdatePeer is called asynchronously. Between the time the call is
// posted (InvalidatePeer) and the time the call is executed (UpdatePeer),
// changes to the visual tree and/or automation tree may have eliminated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,23 @@ public static bool ItemAutomationPeerKeepsItsItemAlive
return LocalAppContext.GetCachedSwitchValue(ItemAutomationPeerKeepsItsItemAliveSwitchName, ref _ItemAutomationPeerKeepsItsItemAlive);
}
}


// Opt-in: when true, ElementProxy holds ItemAutomationPeer (and subclasses) via WeakReference,
// letting the GC reclaim peers and their data-item/container chain when UIA Core retains the
// ElementProxy aggressively (heap growth in virtualized ItemsControls, e.g. DataGrid).
// The parent ItemsControlAutomationPeer's WeakRefElementProxyStorage may reuse still-live
// ElementProxy/peer pairs, but it does not guarantee peer re-discovery after GC; callers may
// need to handle ElementNotAvailableException and re-walk when the peer has been collected.
internal const string UseWeakReferenceFromElementProxyToItemPeerSwitchName = "Switch.System.Windows.Automation.Peers.UseWeakReferenceFromElementProxyToItemPeer";
private static int _UseWeakReferenceFromElementProxyToItemPeer;
public static bool UseWeakReferenceFromElementProxyToItemPeer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return LocalAppContext.GetCachedSwitchValue(UseWeakReferenceFromElementProxyToItemPeerSwitchName, ref _UseWeakReferenceFromElementProxyToItemPeer);
}
}

// Switch to opt-out Fluent theme Window Backdrop feature
internal const string DisableFluentThemeWindowBackdropSwitchName = "Switch.System.Windows.Appearance.DisableFluentThemeWindowBackdrop";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ internal override bool IsDataItemAutomationPeer()
return true;
}

// Gated by FrameworkAppContextSwitches.UseWeakReferenceFromElementProxyToItemPeer. When on,
// a long-lived UIA ElementProxy no longer pins the data item, container, or parent
// ItemsControlAutomationPeer caches; re-discovery goes via WeakRefElementProxyStorage on the parent.
internal override bool ShouldUseWeakReferenceFromElementProxy
{
get { return FrameworkAppContextSwitches.UseWeakReferenceFromElementProxyToItemPeer; }
}
Comment on lines +158 to +164
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback


internal override void AddToParentProxyWeakRefCache()
{
ItemsControlAutomationPeer itemsControlAutomationPeer = ItemsControlAutomationPeer;
Expand Down