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 c54c09fdaaa..d50aca2789b 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Drawing; using static Interop; @@ -108,7 +109,13 @@ internal override int[]? RuntimeId public override AccessibleObject? GetChild(int index) { - if (!_owningListView.IsHandleCreated || index < 0 || index >= GetChildCount()) + if (!_owningListView.IsHandleCreated || index < 0) + { + return null; + } + + IReadOnlyList visibleGroups = GetVisibleGroups(); + if (index >= GetChildCount(visibleGroups)) { return null; } @@ -120,7 +127,7 @@ internal override int[]? RuntimeId if (!OwnerHasDefaultGroup) { - return _owningListView.Groups[index].AccessibilityObject; + return visibleGroups[index].AccessibilityObject; } // Default group has the last index out of the Groups.Count @@ -129,7 +136,7 @@ internal override int[]? RuntimeId // default group is the first before other groups. return index == 0 ? _owningListView.DefaultGroup.AccessibilityObject - : _owningListView.Groups[index - 1].AccessibilityObject; + : visibleGroups[index - 1].AccessibilityObject; } public override int GetChildCount() @@ -139,9 +146,14 @@ public override int GetChildCount() return 0; } + return GetChildCount(GetVisibleGroups()); + } + + private int GetChildCount(IReadOnlyList visibleGroups) + { if (ShowGroupAccessibleObject) { - return OwnerHasDefaultGroup ? _owningListView.Groups.Count + 1 : _owningListView.Groups.Count; + return OwnerHasDefaultGroup ? visibleGroups.Count + 1 : visibleGroups.Count; } return _owningListView.Items.Count; @@ -282,6 +294,26 @@ internal override UiaCore.IRawElementProviderSimple[] GetSelection() return selectedItemProviders; } + internal IReadOnlyList GetVisibleGroups() + { + List list = new(); + if (!ShowGroupAccessibleObject) + { + return list; + } + + foreach (ListViewGroup listViewGroup in _owningListView.Groups) + { + if (listViewGroup.AccessibilityObject is ListViewGroup.ListViewGroupAccessibleObject listViewGroupAccessibleObject + && listViewGroupAccessibleObject.GetVisibleItems().Count > 0) + { + list.Add(listViewGroup); + } + } + + return list; + } + public override AccessibleObject? HitTest(int x, int y) { if (!_owningListView.IsHandleCreated) 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 f22b9bc71b7..b7acce3b000 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 @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Drawing; +using static System.Windows.Forms.ListView; using static Interop; using static Interop.ComCtl32; @@ -13,6 +15,7 @@ public partial class ListViewGroup internal class ListViewGroupAccessibleObject : AccessibleObject { private readonly ListView _owningListView; + private readonly ListViewAccessibleObject _owningListViewAccessibilityObject; private readonly ListViewGroup _owningGroup; private readonly bool _owningGroupIsDefault; @@ -26,6 +29,9 @@ public ListViewGroupAccessibleObject(ListViewGroup owningGroup, bool owningGroup ? _owningGroup.Items[0].ListView : throw new InvalidOperationException(nameof(owningGroup.ListView))); + _owningListViewAccessibilityObject = _owningListView.AccessibilityObject as ListView.ListViewAccessibleObject + ?? throw new InvalidOperationException(nameof(_owningListView.AccessibilityObject)); + _owningGroupIsDefault = owningGroupIsDefault; } @@ -45,8 +51,8 @@ public override Rectangle Bounds User32.SendMessageW(_owningListView, (User32.WM)ComCtl32.LVM.GETGROUPRECT, (IntPtr)CurrentIndex, ref groupRect); return new Rectangle( - _owningListView.AccessibilityObject.Bounds.X + groupRect.left, - _owningListView.AccessibilityObject.Bounds.Y + groupRect.top, + _owningListViewAccessibilityObject.Bounds.X + groupRect.left, + _owningListViewAccessibilityObject.Bounds.Y + groupRect.top, groupRect.right - groupRect.left, groupRect.bottom - groupRect.top); } @@ -77,7 +83,7 @@ internal override int[]? RuntimeId { get { - var owningListViewRuntimeId = _owningListView.AccessibilityObject.RuntimeId; + var owningListViewRuntimeId = _owningListViewAccessibilityObject.RuntimeId; if (owningListViewRuntimeId is null) { return base.RuntimeId; @@ -148,6 +154,20 @@ private bool GetNativeFocus() _ => base.GetPropertyValue(propertyID) }; + internal IReadOnlyList GetVisibleItems() + { + List visibleItems = new(); + foreach (ListViewItem listViewItem in _owningGroup.Items) + { + if (listViewItem.ListView is not null) + { + visibleItems.Add(listViewItem); + } + } + + return visibleItems; + } + internal override UiaCore.IRawElementProviderFragment? FragmentNavigate(UiaCore.NavigateDirection direction) { if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode) @@ -158,11 +178,11 @@ private bool GetNativeFocus() switch (direction) { case UiaCore.NavigateDirection.Parent: - return _owningListView.AccessibilityObject; + return _owningListViewAccessibilityObject; case UiaCore.NavigateDirection.NextSibling: - return (_owningListView.AccessibilityObject as ListView.ListViewAccessibleObject)?.GetNextChild(this); + return _owningListViewAccessibilityObject.GetNextChild(this); case UiaCore.NavigateDirection.PreviousSibling: - return (_owningListView.AccessibilityObject as ListView.ListViewAccessibleObject)?.GetPreviousChild(this); + return _owningListViewAccessibilityObject.GetPreviousChild(this); case UiaCore.NavigateDirection.FirstChild: int childCount = GetChildCount(); if (childCount > 0) @@ -195,12 +215,13 @@ private bool GetNativeFocus() if (!_owningGroupIsDefault) { - if (index < 0 || index >= _owningGroup.Items.Count) + IReadOnlyList visibleItems = GetVisibleItems(); + if (index < 0 || index >= visibleItems.Count) { return null; } - return _owningGroup.Items[index].AccessibilityObject; + return visibleItems[index].AccessibilityObject; } foreach (ListViewItem? item in _owningListView.Items) @@ -294,7 +315,7 @@ public override int GetChildCount() } else { - return _owningGroup.Items.Count; + return GetVisibleItems().Count; } } 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 d8f87d45247..85e21241ad7 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 @@ -760,6 +760,307 @@ public void ListViewAccessibleObject_GetPropertyValue_ControlType_IsExpected_For Assert.Equal(expected, actual); Assert.False(listView.IsHandleCreated); } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNavigate_ReturnExpected_InvisibleGroups(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + ListViewGroup listViewGroupWithItems1 = listView.Groups[1]; + ListViewGroup listViewGroupWithItems2 = listView.Groups[2]; + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(listViewGroupWithItems1.AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.FirstChild)); + + Assert.Equal(listViewGroupWithItems2.AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.LastChild)); + + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNavigate_ReturnExpected_InvisibleGroups_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(listView.Groups[1].AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.FirstChild)); + + Assert.Equal(listView.Groups[2].AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.LastChild)); + + ListViewItem listViewItem1 = new(); + ListViewItem listViewItem2 = new(); + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + listView.Groups[0].Items.Add(listViewItem1); + listView.Groups[3].Items.Add(listViewItem2); + + Assert.Equal(listView.Groups[0].AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.FirstChild)); + + Assert.Equal(listView.Groups[3].AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.LastChild)); + + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNavigate_ReturnExpected_InvisibleGroups_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(listView.Groups[1].AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.FirstChild)); + + Assert.Equal(listView.Groups[2].AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.LastChild)); + + listView.Groups[1].Items.RemoveAt(0); + listView.Groups[2].Items.RemoveAt(0); + + Assert.Equal(listView.DefaultGroup.AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.FirstChild)); + + Assert.Equal(listView.DefaultGroup.AccessibilityObject, + accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.LastChild)); + + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnExpected_InvisibleGroups(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(2, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnExpected_InvisibleGroups_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(2, accessibleObject.GetChildCount()); + + ListViewItem listViewItem1 = new(); + ListViewItem listViewItem2 = new(); + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + listView.Groups[0].Items.Add(listViewItem1); + listView.Groups[3].Items.Add(listViewItem2); + + Assert.Equal(4, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnExpected_InvisibleGroups_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(2, accessibleObject.GetChildCount()); + + listView.Groups[1].Items.RemoveAt(0); + listView.Groups[2].Items.RemoveAt(0); + + Assert.Equal(1, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnExpected_GroupWithInvalidAccessibleObject(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + Assert.Equal(2, accessibleObject.GetChildCount()); + + listView.Groups[1].TestAccessor().Dynamic._accessibilityObject = new AccessibleObject(); + + Assert.Equal(1, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChild_ReturnExpected_InvisibleGroups(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + ListViewGroup listViewGroupWithItems1 = listView.Groups[1]; + ListViewGroup listViewGroupWithItems2 = listView.Groups[2]; + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(listViewGroupWithItems1.AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Equal(listViewGroupWithItems2.AccessibilityObject, accessibleObject.GetChild(1)); + Assert.Null(accessibleObject.GetChild(2)); + Assert.Null(accessibleObject.GetChild(3)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChild_ReturnExpected_InvisibleGroups_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(listView.Groups[1].AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Equal(listView.Groups[2].AccessibilityObject, accessibleObject.GetChild(1)); + Assert.Null(accessibleObject.GetChild(2)); + Assert.Null(accessibleObject.GetChild(3)); + + ListViewItem listViewItem1 = new(); + ListViewItem listViewItem2 = new(); + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + listView.Groups[0].Items.Add(listViewItem1); + listView.Groups[3].Items.Add(listViewItem2); + + Assert.Equal(listView.Groups[0].AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Equal(listView.Groups[1].AccessibilityObject, accessibleObject.GetChild(1)); + Assert.Equal(listView.Groups[2].AccessibilityObject, accessibleObject.GetChild(2)); + Assert.Equal(listView.Groups[3].AccessibilityObject, accessibleObject.GetChild(3)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChild_ReturnExpected_InvisibleGroups_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Equal(listView.Groups[1].AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Equal(listView.Groups[2].AccessibilityObject, accessibleObject.GetChild(1)); + Assert.Null(accessibleObject.GetChild(2)); + Assert.Null(accessibleObject.GetChild(3)); + + listView.Groups[1].Items.RemoveAt(0); + listView.Groups[2].Items.RemoveAt(0); + + Assert.Equal(listView.DefaultGroup.AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Null(accessibleObject.GetChild(1)); + Assert.Null(accessibleObject.GetChild(2)); + Assert.Null(accessibleObject.GetChild(3)); + Assert.True(listView.IsHandleCreated); + } + + private ListView GetListViewItemWithEmptyGroups(View view) + { + ListView listView = new ListView() { View = view}; + listView.CreateControl(); + ListViewGroup listViewGroupWithoutItems = new("Group without items"); + ListViewGroup listViewGroupWithItems1 = new("Group with item 1"); + ListViewGroup listViewGroupWithItems2 = new("Group with item 2"); + ListViewGroup listViewGroupWithInvisibleItems = new("Group with invisible item"); + listView.Groups.Add(listViewGroupWithoutItems); + listView.Groups.Add(listViewGroupWithItems1); + listView.Groups.Add(listViewGroupWithItems2); + listView.Groups.Add(listViewGroupWithInvisibleItems); + ListViewItem listViewItem1 = new(); + ListViewItem listViewItem2 = new(); + ListViewItem listViewItem3 = new(); + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + listViewGroupWithItems1.Items.Add(listViewItem1); + listViewGroupWithItems2.Items.Add(listViewItem2); + listViewGroupWithInvisibleItems.Items.Add(listViewItem3); + + return listView; + } } } 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 13ee620bfe6..73b5d465ee1 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 @@ -640,5 +640,353 @@ public void ListViewGroupAccessibleObject_ExpandCollapseState_ReturnExpected(Vie Assert.Equal(ExpandCollapseState.Expanded, listView.DefaultGroup.AccessibilityObject.ExpandCollapseState); Assert.Equal(createHandle, listView.IsHandleCreated); } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleGroups(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + AccessibleObject listViewGroupWithItems1 = listView.Groups[1].AccessibilityObject; + AccessibleObject listViewGroupWithItems2 = listView.Groups[2].AccessibilityObject; + + Assert.Null(listViewGroupWithItems1.FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Equal(listViewGroupWithItems2, listViewGroupWithItems1.FragmentNavigate(NavigateDirection.NextSibling)); + Assert.Equal(listViewGroupWithItems1, listViewGroupWithItems2.FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Null(listViewGroupWithItems2.FragmentNavigate(NavigateDirection.NextSibling)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_ReturnsExpected_Sibling_InvisibleGroups_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + + Assert.Null(GetAccessibleObject(1).FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NextSibling)); + + ListViewItem listViewItem1 = new(); + ListViewItem listViewItem2 = new(); + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + listView.Groups[0].Items.Add(listViewItem1); + listView.Groups[3].Items.Add(listViewItem2); + + Assert.Null(GetAccessibleObject(0).FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(0).FragmentNavigate(NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(0), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(3), GetAccessibleObject(2).FragmentNavigate(NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(3).FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(3).FragmentNavigate(NavigateDirection.NextSibling)); + Assert.True(listView.IsHandleCreated); + + AccessibleObject GetAccessibleObject(int index) => listView.Groups[index].AccessibilityObject; + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleGroups_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithEmptyGroups(view); + AccessibleObject accessibleObject = listView.AccessibilityObject; + AccessibleObject listViewGroupWithItems1 = listView.Groups[1].AccessibilityObject; + AccessibleObject listViewGroupWithItems2 = listView.Groups[2].AccessibilityObject; + + Assert.Null(listViewGroupWithItems1.FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Equal(listViewGroupWithItems2, listViewGroupWithItems1.FragmentNavigate(NavigateDirection.NextSibling)); + Assert.Equal(listViewGroupWithItems1, listViewGroupWithItems2.FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Null(listViewGroupWithItems2.FragmentNavigate(NavigateDirection.NextSibling)); + + listView.Groups[2].Items.RemoveAt(0); + + Assert.Equal(listView.DefaultGroup.AccessibilityObject, listViewGroupWithItems1.FragmentNavigate(NavigateDirection.PreviousSibling)); + Assert.Null(listViewGroupWithItems1.FragmentNavigate(NavigateDirection.NextSibling)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.FirstChild)); + Assert.Equal(listView.Groups[0].Items[2].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.LastChild)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.FirstChild)); + Assert.Equal(listView.Groups[0].Items[2].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.LastChild)); + + listView.Items.Add(listView.Groups[0].Items[0]); + listView.Items.Add(listView.Groups[0].Items[3]); + + Assert.Equal(listView.Groups[0].Items[0].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.FirstChild)); + Assert.Equal(listView.Groups[0].Items[3].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.LastChild)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Child_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.FirstChild)); + Assert.Equal(listView.Groups[0].Items[2].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.LastChild)); + + listView.Items.RemoveAt(1); + + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.FirstChild)); + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.FragmentNavigate(NavigateDirection.LastChild)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(2, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(2, accessibleObject.GetChildCount()); + + listView.Items.Add(listView.Groups[0].Items[0]); + listView.Items.Add(listView.Groups[0].Items[3]); + + Assert.Equal(4, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChildCount_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(2, accessibleObject.GetChildCount()); + + listView.Items.RemoveAt(1); + + Assert.Equal(1, accessibleObject.GetChildCount()); + + listView.Items.RemoveAt(0); + + Assert.Equal(0, accessibleObject.GetChildCount()); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Equal(listView.Groups[0].Items[2].AccessibilityObject, accessibleObject.GetChild(1)); + Assert.Null(accessibleObject.GetChild(2)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + listView.Items.Add(listView.Groups[0].Items[0]); + listView.Items.Add(listView.Groups[0].Items[3]); + + Assert.Equal(listView.Groups[0].Items[0].AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.GetChild(1)); + Assert.Equal(listView.Groups[0].Items[2].AccessibilityObject, accessibleObject.GetChild(2)); + Assert.Equal(listView.Groups[0].Items[3].AccessibilityObject, accessibleObject.GetChild(3)); + Assert.Null(accessibleObject.GetChild(4)); + Assert.True(listView.IsHandleCreated); + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_GetChild_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + AccessibleObject accessibleObject = listView.Groups[0].AccessibilityObject; + + listView.Items.RemoveAt(1); + + Assert.Equal(listView.Groups[0].Items[1].AccessibilityObject, accessibleObject.GetChild(0)); + Assert.Null(accessibleObject.GetChild(1)); + Assert.True(listView.IsHandleCreated); + } + + private ListView GetListViewItemWithEmptyGroups(View view) + { + ListView listView = new ListView() { View = view }; + listView.CreateControl(); + ListViewGroup listViewGroupWithoutItems = new("Group without items"); + ListViewGroup listViewGroupWithItems1 = new("Group with item 1"); + ListViewGroup listViewGroupWithItems2 = new("Group with item 2"); + ListViewGroup listViewGroupWithInvisibleItems = new("Group with invisible item"); + listView.Groups.Add(listViewGroupWithoutItems); + listView.Groups.Add(listViewGroupWithItems1); + listView.Groups.Add(listViewGroupWithItems2); + listView.Groups.Add(listViewGroupWithInvisibleItems); + ListViewItem listViewItem1 = new(); + ListViewItem listViewItem2 = new(); + ListViewItem listViewItem3 = new(); + listView.Items.Add(listViewItem1); + listView.Items.Add(listViewItem2); + listViewGroupWithItems1.Items.Add(listViewItem1); + listViewGroupWithItems2.Items.Add(listViewItem2); + listViewGroupWithInvisibleItems.Items.Add(listViewItem3); + + return listView; + } + + private ListView GetListViewItemWithInvisibleItems(View view) + { + ListView listView = new ListView() { View = view }; + listView.CreateControl(); + ListViewGroup listViewGroup = new("Test group"); + ListViewItem listViewInvisibleItem1 = new ListViewItem("Invisible item 1"); + ListViewItem listViewVisibleItem1 = new ListViewItem("Visible item 1"); + ListViewItem listViewInvisibleItem2 = new ListViewItem("Invisible item 1"); + ListViewItem listViewVisibleItem2 = new ListViewItem("Visible item 1"); + + listView.Groups.Add(listViewGroup); + listView.Items.AddRange(new ListViewItem[] { listViewVisibleItem1, listViewVisibleItem2 }); + listViewGroup.Items.AddRange(new ListViewItem[] + { + listViewInvisibleItem1, listViewVisibleItem1, + listViewVisibleItem2, listViewInvisibleItem2 + }); + + 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 c7d2fa5fbc7..b13f0b31814 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 @@ -1482,5 +1482,112 @@ public void ListViewItemAccessibleObject_Toggle_Invoke(View view, bool showGroup Assert.False(listViewItem.Checked); Assert.Equal(createHandle, listView.IsHandleCreated); } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + + Assert.Null(GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.True(listView.IsHandleCreated); + + AccessibleObject GetAccessibleObject(int index) => listView.Groups[0].Items[index].AccessibilityObject; + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems_AfterAddingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + + Assert.Null(GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + + listView.Items.Add(listView.Groups[0].Items[0]); + listView.Items.Add(listView.Groups[0].Items[3]); + + Assert.Null(GetAccessibleObject(0).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(0).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(0), GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(3), GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(3).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(3).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.True(listView.IsHandleCreated); + + AccessibleObject GetAccessibleObject(int index) => listView.Groups[0].Items[index].AccessibilityObject; + } + + [WinFormsTheory] + [InlineData(View.Details)] + [InlineData(View.LargeIcon)] + [InlineData(View.SmallIcon)] + [InlineData(View.Tile)] + public void ListViewAccessibleObject_FragmentNaviage_Sibling_ReturnsExpected_InvisibleItems_AfterRemovingItems(View view) + { + if (!Application.UseVisualStyles) + { + return; + } + + using ListView listView = GetListViewItemWithInvisibleItems(view); + + Assert.Null(GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Equal(GetAccessibleObject(2), GetAccessibleObject(1).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.Equal(GetAccessibleObject(1), GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(2).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + + listView.Items.RemoveAt(1); + + Assert.Null(GetAccessibleObject(0).FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling)); + Assert.Null(GetAccessibleObject(0).FragmentNavigate(UiaCore.NavigateDirection.NextSibling)); + Assert.True(listView.IsHandleCreated); + + AccessibleObject GetAccessibleObject(int index) => listView.Groups[0].Items[index].AccessibilityObject; + } + + private ListView GetListViewItemWithInvisibleItems(View view) + { + ListView listView = new ListView() { View = view }; + listView.CreateControl(); + ListViewGroup listViewGroup = new("Test group"); + ListViewItem listViewInvisibleItem1 = new ListViewItem("Invisible item 1"); + ListViewItem listViewVisibleItem1 = new ListViewItem("Visible item 1"); + ListViewItem listViewInvisibleItem2 = new ListViewItem("Invisible item 1"); + ListViewItem listViewVisibleItem2 = new ListViewItem("Visible item 1"); + + listView.Groups.Add(listViewGroup); + listView.Items.AddRange(new ListViewItem[] { listViewVisibleItem1, listViewVisibleItem2 }); + listViewGroup.Items.AddRange(new ListViewItem[] + { + listViewInvisibleItem1, listViewVisibleItem1, + listViewVisibleItem2, listViewInvisibleItem2 + }); + + return listView; + } } }