diff --git a/src/System.Windows.Forms.Primitives/src/Interop/ComCtl32/Interop.LVGGR.cs b/src/System.Windows.Forms.Primitives/src/Interop/ComCtl32/Interop.LVGGR.cs new file mode 100644 index 00000000000..845b2b28d22 --- /dev/null +++ b/src/System.Windows.Forms.Primitives/src/Interop/ComCtl32/Interop.LVGGR.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + public enum LVGGR + { + GROUP = 0, + HEADER = 1, + LABEL = 2, + SUBSETLINK = 3 + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs index d50aca2789b..9d303f19a83 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs @@ -26,11 +26,11 @@ internal override bool CanSelectMultiple internal override int ColumnCount => _owningListView.Columns.Count; - private bool OwnerHasDefaultGroup + internal bool OwnerHasDefaultGroup { get { - if (!_owningListView.IsHandleCreated || !_owningListView.ShowGroups || _owningListView.VirtualMode) + if (!_owningListView.ShowGroups || _owningListView.VirtualMode) { return false; } @@ -77,7 +77,7 @@ internal override int[]? RuntimeId } // ListViewGroup are not displayed when the ListView is in "List" view - private bool ShowGroupAccessibleObject => _owningListView.View != View.List && _owningListView.GroupsEnabled; + internal bool ShowGroupAccessibleObject => _owningListView.View != View.List && _owningListView.GroupsEnabled; internal override UiaCore.IRawElementProviderFragment? ElementProviderFromPoint(double x, double y) { diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs index bfaa746bac8..200dba05d23 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs @@ -3501,22 +3501,6 @@ internal int GetColumnIndex(ColumnHeader ch) return -1; } - internal int GetGroupIndex(ListViewGroup owningGroup) - { - if (Groups.Count == 0) - { - return -1; - } - - // Default group it's last group in accessibility and not specified in Groups collection, therefore default group index = Groups.Count - if (DefaultGroup == owningGroup) - { - return Groups.Count; - } - - return Groups.IndexOf(owningGroup); - } - /// /// Returns the current ListViewItem corresponding to the specific /// x,y co-ordinate. diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObject.cs index b7acce3b000..27963072b01 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObject.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Drawing; +using System.Runtime.InteropServices; using static System.Windows.Forms.ListView; using static Interop; using static Interop.ComCtl32; @@ -42,28 +43,55 @@ public override Rectangle Bounds { get { - if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode) + if (!_owningListView.IsHandleCreated || !_owningListViewAccessibilityObject.ShowGroupAccessibleObject) { return Rectangle.Empty; } - RECT groupRect = new RECT(); - User32.SendMessageW(_owningListView, (User32.WM)ComCtl32.LVM.GETGROUPRECT, (IntPtr)CurrentIndex, ref groupRect); + if (GetVisibleItems().Count == 0) + { + return Rectangle.Empty; + } + + int nativeGroupId = GetNativeGroupId(); + if (nativeGroupId == -1) + { + return Rectangle.Empty; + } - return new Rectangle( - _owningListViewAccessibilityObject.Bounds.X + groupRect.left, - _owningListViewAccessibilityObject.Bounds.Y + groupRect.top, - groupRect.right - groupRect.left, - groupRect.bottom - groupRect.top); + LVGGR rectType = _owningGroup.CollapsedState == ListViewGroupCollapsedState.Collapsed + ? LVGGR.HEADER + : LVGGR.GROUP; + + // Get the native rectangle + RECT groupRect = new(); + + // Using the "top" property, we set which rectangle type of the group we want to get + // This is described in more detail in https://docs.microsoft.com/windows/win32/controls/lvm-getgrouprect + groupRect.top = (int)rectType; + User32.SendMessageW(_owningListView, (User32.WM)LVM.GETGROUPRECT, (IntPtr)nativeGroupId, ref groupRect); + + // Using the following code, we limit the size of the ListViewGroup rectangle + // so that it does not go beyond the rectangle of the ListView + Rectangle listViewBounds = _owningListView.AccessibilityObject.Bounds; + groupRect = _owningListView.RectangleToScreen(groupRect); + groupRect.top = Math.Max(listViewBounds.Top, groupRect.top); + groupRect.bottom = Math.Min(listViewBounds.Bottom, groupRect.bottom); + groupRect.left = Math.Max(listViewBounds.Left, groupRect.left); + groupRect.right = Math.Min(listViewBounds.Right, groupRect.right); + + return groupRect; } } - private int CurrentIndex + internal int CurrentIndex + // The default group has 0 index, as it is always displayed first. => _owningGroupIsDefault - // Default group has the last index out of the Groups.Count - // upper bound: so the DefaultGroup.Index == Groups.Count. - ? _owningListView.Groups.Count - : _owningListView.Groups.IndexOf(_owningGroup); + ? 0 + // When calculating the index of other groups, we add a shift if the default group is displayed + : _owningListViewAccessibilityObject.OwnerHasDefaultGroup + ? _owningListView.Groups.IndexOf(_owningGroup) + 1 + : _owningListView.Groups.IndexOf(_owningGroup); public override string DefaultAction => SR.AccessibleActionDoubleClick; @@ -73,6 +101,8 @@ internal override UiaCore.ExpandCollapseState ExpandCollapseState ? UiaCore.ExpandCollapseState.Collapsed : UiaCore.ExpandCollapseState.Expanded; + internal override UiaCore.IRawElementProviderFragmentRoot FragmentRoot => _owningListView.AccessibilityObject; + public override string Name => _owningGroup.Header; @@ -131,7 +161,26 @@ private bool GetNativeFocus() return false; } - return LVGS.FOCUSED == unchecked((LVGS)(long)User32.SendMessageW(_owningListView, (User32.WM)LVM.GETGROUPSTATE, (IntPtr)CurrentIndex, (IntPtr)LVGS.FOCUSED)); + int nativeGroupId = GetNativeGroupId(); + if (nativeGroupId == -1) + { + return false; + } + + return LVGS.FOCUSED == unchecked((LVGS)(long)User32.SendMessageW(_owningListView, (User32.WM)LVM.GETGROUPSTATE, (IntPtr)nativeGroupId, (IntPtr)LVGS.FOCUSED)); + } + + private unsafe int GetNativeGroupId() + { + LVGROUPW lvgroup = new LVGROUPW + { + cbSize = (uint)sizeof(LVGROUPW), + mask = LVGF.GROUPID, + }; + + return User32.SendMessageW(_owningListView, (User32.WM)LVM.GETGROUPINFOBYINDEX, (IntPtr)CurrentIndex, ref lvgroup) == IntPtr.Zero + ? -1 + : lvgroup.iGroupId; } internal override object? GetPropertyValue(UiaCore.UIA propertyID) @@ -157,6 +206,19 @@ private bool GetNativeFocus() internal IReadOnlyList GetVisibleItems() { List visibleItems = new(); + if (_owningGroupIsDefault) + { + foreach (ListViewItem? listViewItem in _owningListView.Items) + { + if (listViewItem is not null && listViewItem.Group is null) + { + visibleItems.Add(listViewItem); + } + } + + return visibleItems; + } + foreach (ListViewItem listViewItem in _owningGroup.Items) { if (listViewItem.ListView is not null) @@ -213,26 +275,13 @@ internal IReadOnlyList GetVisibleItems() return null; } - if (!_owningGroupIsDefault) + IReadOnlyList visibleItems = GetVisibleItems(); + if (index < 0 || index >= visibleItems.Count) { - IReadOnlyList visibleItems = GetVisibleItems(); - if (index < 0 || index >= visibleItems.Count) - { - return null; - } - - return visibleItems[index].AccessibilityObject; - } - - foreach (ListViewItem? item in _owningListView.Items) - { - if (item is not null && item.Group is null && index-- == 0) - { - return item.AccessibilityObject; - } + return null; } - return null; + return visibleItems[index].AccessibilityObject; } private int GetChildIndex(AccessibleObject child) @@ -300,23 +349,7 @@ public override int GetChildCount() return -1; } - if (_owningGroupIsDefault) - { - int count = 0; - foreach (ListViewItem? item in _owningListView.Items) - { - if (item is not null && item.Group is null) - { - count++; - } - } - - return count; - } - else - { - return GetVisibleItems().Count; - } + return GetVisibleItems().Count; } internal override bool IsPatternSupported(UiaCore.UIA patternId) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObject.cs index 5cbc246108c..53d81d9e155 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObject.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObject.cs @@ -179,7 +179,10 @@ internal override int[]? RuntimeId runtimeId[0] = owningListViewRuntimeId[0]; runtimeId[1] = owningListViewRuntimeId[1]; runtimeId[2] = 4; // Win32-control specific RuntimeID constant, is used in similar Win32 controls and is used in WinForms controls for consistency. - runtimeId[3] = _owningListView.GetGroupIndex(_owningGroup); + runtimeId[3] = _owningGroup.AccessibilityObject is ListViewGroupAccessibleObject listViewGroupAccessibleObject + ? listViewGroupAccessibleObject.CurrentIndex + : -1; + runtimeId[4] = CurrentIndex; return runtimeId; diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListVIew.ListViewAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListVIew.ListViewAccessibleObjectTests.cs index 85e21241ad7..1f01803052b 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListVIew.ListViewAccessibleObjectTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListVIew.ListViewAccessibleObjectTests.cs @@ -424,7 +424,7 @@ public static IEnumerable ListViewAccessibleObject_OwnerHasDefaultGrou { foreach (bool createHandle in new[] { true, false }) { - bool expected = showGroups && createHandle; + bool expected = showGroups; yield return new object[] { showGroups, createHandle, expected }; } } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObjectTests.cs index 73b5d465ee1..ccd30b7ed9e 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObjectTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewGroup.ListViewGroupAccessibleObjectTests.cs @@ -50,7 +50,7 @@ public void ListViewGroupAccessibleObject_Ctor_Default() } [WinFormsFact] - public void ListViewGroupAccessibleObject_GetPropertyValue_returns_correct_values() + public void ListViewGroupAccessibleObject_GetPropertyValue_ReturnsExpected_WithoutDefaultGroup() { using ListView list = new ListView(); ListViewGroup listGroup = new ListViewGroup("Group1"); @@ -71,6 +71,34 @@ public void ListViewGroupAccessibleObject_GetPropertyValue_returns_correct_value Assert.Equal(expected, controlType); Assert.True((bool)accessibleObject.GetPropertyValue(UiaCore.UIA.IsLegacyIAccessiblePatternAvailablePropertyId)); + Assert.False(list.IsHandleCreated); + } + + [WinFormsFact] + public void ListViewGroupAccessibleObject_GetPropertyValue_ReturnsExpected_WithDefaultGroup() + { + using ListView list = new ListView(); + ListViewGroup listGroup = new ListViewGroup("Group1"); + listGroup.Items.Add(new ListViewItem()); + list.Items.Add(new ListViewItem()); + list.Groups.Add(listGroup); + + AccessibleObject defaultGroupAccessibleObject = list.DefaultGroup.AccessibilityObject; + AccessibleObject groupAccessibleObject = listGroup.AccessibilityObject; + + Assert.Equal(list.DefaultGroup.Header, defaultGroupAccessibleObject.GetPropertyValue(UiaCore.UIA.NamePropertyId)); + Assert.Equal("Group1", groupAccessibleObject.GetPropertyValue(UiaCore.UIA.NamePropertyId)); + + Assert.Equal("ListViewGroup-0", defaultGroupAccessibleObject.GetPropertyValue(UiaCore.UIA.AutomationIdPropertyId)); + Assert.Equal("ListViewGroup-1", groupAccessibleObject.GetPropertyValue(UiaCore.UIA.AutomationIdPropertyId)); + + Assert.Equal(UiaCore.UIA.GroupControlTypeId, groupAccessibleObject.GetPropertyValue(UiaCore.UIA.ControlTypePropertyId)); + Assert.Equal(UiaCore.UIA.GroupControlTypeId, defaultGroupAccessibleObject.GetPropertyValue(UiaCore.UIA.ControlTypePropertyId)); + + Assert.True((bool)defaultGroupAccessibleObject.GetPropertyValue(UiaCore.UIA.IsLegacyIAccessiblePatternAvailablePropertyId)); + Assert.True((bool)groupAccessibleObject.GetPropertyValue(UiaCore.UIA.IsLegacyIAccessiblePatternAvailablePropertyId)); + + Assert.False(list.IsHandleCreated); } [WinFormsFact] @@ -646,7 +674,7 @@ public void ListViewGroupAccessibleObject_ExpandCollapseState_ReturnExpected(Vie [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleGroups(View view) + public void ListViewGroupAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleGroups(View view) { if (!Application.UseVisualStyles) { @@ -670,7 +698,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_Inv [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_ReturnsExpected_Sibling_InvisibleGroups_AfterAddingItems(View view) + public void ListViewGroupAccessibleObject_FragmentNaviage_ReturnsExpected_Sibling_InvisibleGroups_AfterAddingItems(View view) { if (!Application.UseVisualStyles) { @@ -710,7 +738,7 @@ public void ListViewAccessibleObject_FragmentNaviage_ReturnsExpected_Sibling_Inv [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleGroups_AfterRemovingItems(View view) + public void ListViewGroupAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleGroups_AfterRemovingItems(View view) { if (!Application.UseVisualStyles) { @@ -739,7 +767,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_Inv [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems(View view) + public void ListViewGroupAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems(View view) { if (!Application.UseVisualStyles) { @@ -759,7 +787,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_Invis [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + public void ListViewGroupAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) { if (!Application.UseVisualStyles) { @@ -785,7 +813,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_Invis [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + public void ListViewGroupAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) { if (!Application.UseVisualStyles) { @@ -810,7 +838,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_Invis [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems(View view) + public void ListViewGroupAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems(View view) { if (!Application.UseVisualStyles) { @@ -829,7 +857,7 @@ public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItem [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + public void ListViewGroupAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) { if (!Application.UseVisualStyles) { @@ -853,7 +881,7 @@ public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItem [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + public void ListViewGroupAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) { if (!Application.UseVisualStyles) { @@ -880,7 +908,7 @@ public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItem [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems(View view) + public void ListViewGroupAccessibleObject_GetChild_ReturnsExpected_InvisibleItems(View view) { if (!Application.UseVisualStyles) { @@ -901,7 +929,7 @@ public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems(Vie [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + public void ListViewGroupAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) { if (!Application.UseVisualStyles) { @@ -927,7 +955,7 @@ public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_Aft [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + public void ListViewGroupAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) { if (!Application.UseVisualStyles) { @@ -944,6 +972,85 @@ public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_Aft Assert.True(listView.IsHandleCreated); } + [WinFormsTheory] + [MemberData(nameof(ListViewGroupAccessibleObject_TestData))] + public void ListViewGroupAccessibleObject_FragmentRoot_Returns_ListViewAccessibleObject(View view, bool showGroups, bool createHandle) + { + using ListView listView = new() + { + View = view, + ShowGroups = showGroups, + }; + + ListViewGroup listViewGroup = new(); + listView.Groups.Add(listViewGroup); + + if (createHandle) + { + listView.CreateControl(); + } + + Assert.Equal(listView.AccessibilityObject, listViewGroup.AccessibilityObject.FragmentRoot); + Assert.Equal(createHandle, listView.IsHandleCreated); + } + + [WinFormsTheory] + [MemberData(nameof(ListViewGroup_GroupAddedWithItem_AccessibleObject_TestData))] + public void ListViewGroupAccessibleObject_Bounds_ReturnsExpected(View view, bool showGroups, bool createHandle, bool virtualMode) + { + using ListView listView = GetListViewWithGroups(view, showGroups, createHandle, virtualMode); + ListView.ListViewAccessibleObject accessibleObject = listView.AccessibilityObject as ListView.ListViewAccessibleObject; + bool showBounds = listView.IsHandleCreated && accessibleObject.ShowGroupAccessibleObject; + + Assert.Equal(showBounds, !listView.DefaultGroup.AccessibilityObject.Bounds.IsEmpty); + Assert.Equal(showBounds, !listView.Groups[0].AccessibilityObject.Bounds.IsEmpty); + Assert.Equal(createHandle, listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewGroupAccessibleObject_Bounds_LocatedInsideListViewBounds(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewWithGroups(view, showGroups: true, createHandle: true, virtualMode: false); + ListView.ListViewAccessibleObject accessibleObject = listView.AccessibilityObject as ListView.ListViewAccessibleObject; + Rectangle listViewBounds = listView.AccessibilityObject.Bounds; + + Assert.True(listViewBounds.Contains(listView.DefaultGroup.AccessibilityObject.Bounds)); + Assert.True(listViewBounds.Contains(listView.Groups[0].AccessibilityObject.Bounds)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewGroupAccessibleObject_Bounds_ReturnEmptyRectangle_ForEmptyGroup(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = new ListView() { View = view, ShowGroups = true }; + listView.Columns.Add(new ColumnHeader()); + listView.CreateControl(); + listView.Groups.Add(new ListViewGroup()); + listView.Items.Add(new ListViewItem()); + + Assert.False(listView.DefaultGroup.AccessibilityObject.Bounds.IsEmpty); + Assert.True(listView.Groups[0].AccessibilityObject.Bounds.IsEmpty); + Assert.True(listView.IsHandleCreated); + } + private ListView GetListViewItemWithEmptyGroups(View view) { ListView listView = new ListView() { View = view }; @@ -988,5 +1095,52 @@ private ListView GetListViewItemWithInvisibleItems(View view) return listView; } + + private ListView GetListViewWithGroups(View view, bool showGroups, bool createHandle, bool virtualMode) + { + ListView listView = new() + { + View = view, + ShowGroups = showGroups, + VirtualListSize = 2, + VirtualMode = virtualMode, + Size = new Size(200, 200) + }; + + listView.Columns.Add(new ColumnHeader()); + + var listViewGroup = new ListViewGroup("Test Group"); + listView.Groups.Add(listViewGroup); + var listViewItem1 = new ListViewItem("Test item 1", listViewGroup); + var listViewItem2 = new ListViewItem("Test item 2"); + + if (virtualMode) + { + listView.RetrieveVirtualItem += (s, e) => + { + e.Item = e.ItemIndex switch + { + 0 => listViewItem1, + 1 => listViewItem2, + _ => throw new NotImplementedException() + }; + }; + + listViewItem1.SetItemIndex(listView, 0); + listViewItem2.SetItemIndex(listView, 1); + } + else + { + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + } + + if (createHandle) + { + Assert.NotEqual(IntPtr.Zero, listView.Handle); + } + + return listView; + } } } diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObjectTests.cs index b13f0b31814..5a6d79b704f 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObjectTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ListViewItem.ListViewItemAccessibleObjectTests.cs @@ -1488,7 +1488,7 @@ public void ListViewItemAccessibleObject_Toggle_Invoke(View view, bool showGroup [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems(View view) + public void ListViewItemAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems(View view) { if (!Application.UseVisualStyles) { @@ -1511,7 +1511,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_Inv [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + public void ListViewItemAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) { if (!Application.UseVisualStyles) { @@ -1546,7 +1546,7 @@ public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_Inv [InlineData(View.LargeIcon)] [InlineData(View.SmallIcon)] [InlineData(View.Tile)] - public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + public void ListViewItemAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) { if (!Application.UseVisualStyles) {