-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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.