UiaProvider implementation forces virtual ListView to retrieve ALL ListViewItems at WM_DESTROY
- Dominant language
- C#
- Stars
- 4.9k
- Forks
- 1.1k
- Avg merge
- 20h 23m
- Merged PRs (30d)
- 103
Description
### .NET version
9.0
### Did it work in .NET Framework?
Not tested/verified
### Did it work in any of the earlier releases of .NET Core or .NET 5+?
I saw in the source code a dependency to OsVersion.IsWindows8OrGreater()
### Issue description
I am using a ListView with **VirtualMode = true** to display a list a few thousands items.
When the ListView is destroyed, the OnRetrieveVirtualItem() is called for each index (even never retrieved ones).
This kills somehow the performance of the control.
_Analyses as far I saw it in the debugger:_
ListView overrides the `internal bool SupportsUiaProviders => true;`
**First problem:** I cannot change that because it's internal.
WM_DESTROY invokes on the Control class
```
private void WmDestroy(ref Message m)
{
if (!RecreatingHandle && !Disposing && !IsDisposed && GetState(States.TrackingMouseEvent))
{
OnMouseLeave(EventArgs.Empty);
UnhookMouseEvent();
}
if (SupportsUiaProviders) // <--- THIS RETURNS TRUE
{
ReleaseUiaProvider(HWNDInternal); // <--- THIS GETS CALLED
}
OnHandleDestroyed(EventArgs.Empty);
if (!Disposing)
{
if (!RecreatingHandle)
{
SetState(States.Created, value: false);
}
}
else
{
SetState(States.Visible, value: false);
}
DefWndProc(ref m);
}
```
This invokes on the ListView class
```
internal override bool SupportsUiaProviders => true;
internal override void ReleaseUiaProvider(HWND handle)
{
if (!OsVersion.IsWindows8OrGreater())
{
return;
}
for (int i = 0; i < Items.Count; i++) // <--- THIS ITERATES OVER ALL INDICES IN THE VIRTUAL LIST VIEW
{
Items.GetItemByIndex(i)?.ReleaseUiaProvider(); // <--- THIS INVOKES OnRetrieveVirtualItem(e)
}
if (_defaultGroup != null)
{
DefaultGroup.ReleaseUiaProvider();
}
foreach (ListViewGroup group in Groups)
{
group.ReleaseUiaProvider();
}
foreach (ColumnHeader column in Columns)
{
column.ReleaseUiaProvider();
}
base.ReleaseUiaProvider(handle);
}
```
After this super expensive retrieval, ListViewItem then does nothing
```
internal void ReleaseUiaProvider()
{
if (!IsAccessibilityObjectCreated) // <--- RETURNS FALSE
{
return;
}
if (OsVersion.IsWindows8OrGreater())
{
if (_accessibilityObject is ListViewItemBaseAccessibleObject listViewItemBaseAccessibleObject)
{
listViewItemBaseAccessibleObject.ReleaseChildUiaProviders();
}
PInvoke.UiaDisconnectProvider(_accessibilityObject, skipOSCheck: true);
}
_accessibilityObject = null;
}
```
_Possible Resolutions_
- Make SupportsUiaProviders protected overrideable.
- Move the test `if (!IsAccessibilityObjectCreated)` higher up in the call hierachy.
- Invoke the `ReleaseUiaProvider()` only for really created ListViewItems.
_Disclaimer_
I'm not profient with UI Automation, so I do not know, what a proper implementation should excatly look like.
In my case I do not use this feature explicitly.
### Steps to reproduce
An example is difficult to post.
Please see the analysis above.
Contributor guide
Assessment
This issue has not been assessed yet.