From c0e5c7906344a285903c8b11afeeb47551bc3c11 Mon Sep 17 00:00:00 2001 From: "Leaf Shi (BEYONDSOFT CONSULTING INC)" Date: Sat, 11 Oct 2025 17:57:29 +0800 Subject: [PATCH 1/3] Improve UIA Provider Cleanup Logic for Virtual ListView Items --- .../Forms/Controls/ListView/ListView.cs | 35 +++++++++++++++++-- .../Forms/Controls/ListView/ListViewItem.cs | 3 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs index f290e8ce8eb..5356f9c0b78 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs @@ -208,6 +208,10 @@ public partial class ListView : Control // We cache the NewWidth supplied by the user and use it on HDN_ENDTRACK to set the final column width. private int _newWidthForColumnWidthChangingCancelled = -1; + // Tracks ListViewItems that have had their AccessibilityObject created in virtual mode, + // so their UIA providers can be properly released later without triggering item retrieval + private readonly HashSet _uiaCreatedItems = []; + /// /// Creates an empty ListView with default styles. /// @@ -5084,6 +5088,14 @@ public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly) } } + internal void NotifyUiaCreated(ListViewItem item) + { + if (VirtualMode) + { + _uiaCreatedItems.Add(item); + } + } + internal override void ReleaseUiaProvider(HWND handle) { if (!OsVersion.IsWindows8OrGreater()) @@ -5091,9 +5103,28 @@ internal override void ReleaseUiaProvider(HWND handle) return; } - for (int i = 0; i < Items.Count; i++) + if (VirtualMode) { - Items.GetItemByIndex(i)?.ReleaseUiaProvider(); + foreach (ListViewItem item in _uiaCreatedItems) + { + if (item.IsAccessibilityObjectCreated) + { + item.ReleaseUiaProvider(); + } + } + + _uiaCreatedItems.Clear(); + } + else + { + for (int i = 0; i < Items.Count; i++) + { + var item = Items.GetItemByIndex(i); + if (item?.IsAccessibilityObjectCreated == true) + { + item.ReleaseUiaProvider(); + } + } } if (_defaultGroup is not null) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs index 3c8bf59e6ee..330c020f0d8 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs @@ -265,11 +265,12 @@ internal virtual AccessibleObject AccessibilityObject }; } + owningListView.NotifyUiaCreated(this); return _accessibilityObject; } } - private bool IsAccessibilityObjectCreated => _accessibilityObject is not null; + internal bool IsAccessibilityObjectCreated => _accessibilityObject is not null; /// /// The font that this item will be displayed in. If its value is null, it will be displayed From 3cbdcdd96c4a816b8d378d34344be3ba41e0c9c2 Mon Sep 17 00:00:00 2001 From: Leaf Shi Date: Fri, 17 Oct 2025 14:49:16 +0800 Subject: [PATCH 2/3] Added _uiaAccessedIndices to track only the indices accessed by UIA in virtual mode. --- .../Forms/Controls/ListView/ListView.cs | 25 ++++++------------- .../Forms/Controls/ListView/ListViewItem.cs | 6 ++++- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs index 5356f9c0b78..35eea44b4d2 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs @@ -208,10 +208,9 @@ public partial class ListView : Control // We cache the NewWidth supplied by the user and use it on HDN_ENDTRACK to set the final column width. private int _newWidthForColumnWidthChangingCancelled = -1; - // Tracks ListViewItems that have had their AccessibilityObject created in virtual mode, - // so their UIA providers can be properly released later without triggering item retrieval - private readonly HashSet _uiaCreatedItems = []; - + // Tracks indices of ListViewItems that have been accessed by UIA in virtual mode, + // so their UIA providers can be properly released later without triggering item retrieval. + private readonly HashSet _uiaAccessedIndices = []; /// /// Creates an empty ListView with default styles. /// @@ -5088,13 +5087,7 @@ public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly) } } - internal void NotifyUiaCreated(ListViewItem item) - { - if (VirtualMode) - { - _uiaCreatedItems.Add(item); - } - } + internal void NotifyUiaCreated(int index) => _uiaAccessedIndices.Add(index); internal override void ReleaseUiaProvider(HWND handle) { @@ -5105,15 +5098,13 @@ internal override void ReleaseUiaProvider(HWND handle) if (VirtualMode) { - foreach (ListViewItem item in _uiaCreatedItems) + foreach (int index in _uiaAccessedIndices) { - if (item.IsAccessibilityObjectCreated) - { - item.ReleaseUiaProvider(); - } + var item = Items.GetItemByIndex(index); + item?.ReleaseUiaProvider(); } - _uiaCreatedItems.Clear(); + _uiaAccessedIndices.Clear(); } else { diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs index 330c020f0d8..64e8815f406 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs @@ -265,7 +265,11 @@ internal virtual AccessibleObject AccessibilityObject }; } - owningListView.NotifyUiaCreated(this); + if (owningListView.VirtualMode) + { + owningListView.NotifyUiaCreated(Index); + } + return _accessibilityObject; } } From 25d4f7691feff438b93cb39450f284f1f854295a Mon Sep 17 00:00:00 2001 From: "Leaf Shi (BEYONDSOFT CONSULTING INC)" Date: Mon, 20 Oct 2025 19:16:18 -0700 Subject: [PATCH 3/3] Moving the judgment for "VirtualMode" to method NotifyUiaCreated --- .../Windows/Forms/Controls/ListView/ListView.cs | 17 +++++++++-------- .../Forms/Controls/ListView/ListViewItem.cs | 7 ++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs index 35eea44b4d2..c1d2a1f03b9 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListView.cs @@ -5087,7 +5087,13 @@ public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly) } } - internal void NotifyUiaCreated(int index) => _uiaAccessedIndices.Add(index); + internal void NotifyUiaCreated(int index) + { + if (VirtualMode) + { + _uiaAccessedIndices.Add(index); + } + } internal override void ReleaseUiaProvider(HWND handle) { @@ -5100,8 +5106,7 @@ internal override void ReleaseUiaProvider(HWND handle) { foreach (int index in _uiaAccessedIndices) { - var item = Items.GetItemByIndex(index); - item?.ReleaseUiaProvider(); + Items.GetItemByIndex(index)?.ReleaseUiaProvider(); } _uiaAccessedIndices.Clear(); @@ -5110,11 +5115,7 @@ internal override void ReleaseUiaProvider(HWND handle) { for (int i = 0; i < Items.Count; i++) { - var item = Items.GetItemByIndex(i); - if (item?.IsAccessibilityObjectCreated == true) - { - item.ReleaseUiaProvider(); - } + Items.GetItemByIndex(i)?.ReleaseUiaProvider(); } } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs index 64e8815f406..a6e44dd7233 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListView/ListViewItem.cs @@ -265,16 +265,13 @@ internal virtual AccessibleObject AccessibilityObject }; } - if (owningListView.VirtualMode) - { - owningListView.NotifyUiaCreated(Index); - } + owningListView.NotifyUiaCreated(Index); return _accessibilityObject; } } - internal bool IsAccessibilityObjectCreated => _accessibilityObject is not null; + private bool IsAccessibilityObjectCreated => _accessibilityObject is not null; /// /// The font that this item will be displayed in. If its value is null, it will be displayed