diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs
index 98984b5c4bc..c838ea2cbd0 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs
@@ -177,6 +177,24 @@ public static bool UseNetFx472CompatibleAccessibilityFeatures
#endregion
+ #region UseLegacyAutomationPeerDisconnect
+
+ ///
+ /// Switch to opt-out of the automation peer disconnect behavior.
+ /// When true, removed automation peers are NOT disconnected from UIA (legacy behavior).
+ /// When false (default), removed automation peers are disconnected via UiaDisconnectProvider.
+ ///
+ public static bool UseLegacyAutomationPeerDisconnect
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return AccessibilitySwitches.UseLegacyAutomationPeerDisconnect;
+ }
+ }
+
+ #endregion
+
#endregion
#region ShouldRenderEvenWhenNoDisplayDevicesAreAvailable and ShouldNotRenderInNonInteractiveWindowStation
diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs
index aa24199a22a..b5afd3d1914 100644
--- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs
+++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs
@@ -1499,7 +1499,7 @@ private void EnsureChildren()
_childrenValid = true;
// Disconnect removed peers (same logic as UpdateChildrenInternal)
- if (oldChildren != null)
+ if (oldChildren != null && !CoreAppContextSwitches.UseLegacyAutomationPeerDisconnect)
{
HashSet newSet = _children != null ?
new HashSet(_children) : null;
@@ -2007,7 +2007,7 @@ internal void UpdateChildrenInternal(int invalidateLimit)
// never drops to zero, which prevents the managed peer (and its entire
// visual sub-tree) from being garbage collected.
// This must happen regardless of StructureChanged event registration.
- if (removedCount > 0)
+ if (removedCount > 0 && !CoreAppContextSwitches.UseLegacyAutomationPeerDisconnect)
{
foreach (AutomationPeer removedChild in hs)
{
diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs
index fc79753bb75..904363455d8 100644
--- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs
+++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs
@@ -147,6 +147,29 @@ public static bool ItemsControlDoesNotSupportAutomation
#endregion
+ #region UseLegacyAutomationPeerDisconnect
+
+ internal const string UseLegacyAutomationPeerDisconnectSwitchName = "Switch.System.Windows.Automation.Peers.UseLegacyAutomationPeerDisconnect";
+ private static int _UseLegacyAutomationPeerDisconnect;
+
+ ///
+ /// Switch to opt-out of the automation peer disconnect behavior.
+ /// When true, compatibility mode is enabled: removed automation peers are NOT
+ /// disconnected from UIA, preserving the legacy element lifetime semantics.
+ /// When false (default), removed automation peers are disconnected via
+ /// UiaDisconnectProvider, allowing CCWs and associated managed objects to be GC'd.
+ ///
+ public static bool UseLegacyAutomationPeerDisconnect
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return LocalAppContext.GetCachedSwitchValue(UseLegacyAutomationPeerDisconnectSwitchName, ref _UseLegacyAutomationPeerDisconnect);
+ }
+ }
+
+ #endregion
+
#endregion
#region Switch Functions
@@ -167,6 +190,7 @@ internal static void SetSwitchDefaults(string platformIdentifier, int targetFram
LocalAppContext.DefineSwitchDefault(UseLegacyAccessibilityFeatures3SwitchName, false);
LocalAppContext.DefineSwitchDefault(UseLegacyToolTipDisplaySwitchName, false);
LocalAppContext.DefineSwitchDefault(ItemsControlDoesNotSupportAutomationSwitchName, false);
+ LocalAppContext.DefineSwitchDefault(UseLegacyAutomationPeerDisconnectSwitchName, false);
}
///
@@ -218,6 +242,10 @@ internal static void VerifyDependencies(Dispatcher dispatcher)
{
DispatchOnError(dispatcher, String.Format(SR.AccessibilitySwitchDependencyNotSatisfied, ItemsControlDoesNotSupportAutomationSwitchName, UseLegacyAccessibilityFeatures3SwitchName, 3));
}
+ if (!UseLegacyAutomationPeerDisconnect && UseNetFx472CompatibleAccessibilityFeatures)
+ {
+ DispatchOnError(dispatcher, String.Format(SR.AccessibilitySwitchDependencyNotSatisfied, UseLegacyAutomationPeerDisconnectSwitchName, UseLegacyAccessibilityFeatures3SwitchName, 3));
+ }
}