File: System\Windows\Forms\Controls\ListView\ListViewItem.ListViewItemTileAccessibleObject.cs
Web Access
Project: src\src\System.Windows.Forms\src\System.Windows.Forms.csproj (System.Windows.Forms)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Drawing;
using Windows.Win32.UI.Accessibility;
 
namespace System.Windows.Forms;
 
public partial class ListViewItem
{
    internal sealed class ListViewItemTileAccessibleObject : ListViewItemBaseAccessibleObject
    {
        private ListViewLabelEditAccessibleObject? _labelEditAccessibleObject;
        public ListViewItemTileAccessibleObject(ListViewItem owningItem) : base(owningItem)
        {
        }
 
        protected override View View => View.Tile;
 
        private ListViewLabelEditAccessibleObject? LabelEditAccessibleObject
            => _labelEditAccessibleObject ??= _owningListView._labelEdit is null
                ? null
                : new(_owningListView, _owningListView._labelEdit);
 
        internal override IRawElementProviderFragment.Interface? FragmentNavigate(NavigateDirection direction)
            => direction switch
            {
                NavigateDirection.NavigateDirection_FirstChild
                    => _owningListView._labelEdit is not null
                        ? LabelEditAccessibleObject
                        : GetChildInternal(1),
                NavigateDirection.NavigateDirection_LastChild => GetChildInternal(GetLastChildIndex()),
                _ => base.FragmentNavigate(direction),
            };
 
        // Only additional ListViewSubItem are displayed in the accessibility tree if the ListView
        // in the "Tile" view (the first ListViewSubItem is responsible for the ListViewItem)
        public override AccessibleObject? GetChild(int index)
        {
            if (_owningListView.View != View.Tile)
            {
                throw new InvalidOperationException(string.Format(SR.ListViewItemAccessibilityObjectInvalidViewException, nameof(View.Tile)));
            }
 
            return GetChildInternal(index + 1);
        }
 
        internal override AccessibleObject? GetChildInternal(int index)
        {
            // If the ListView does not support ListViewSubItems, the index is greater than the number of columns
            // or the index is negative, then we return null
            if (!_owningListView.SupportsListViewSubItems
                || index <= 0
                || _owningListView.Columns.Count <= index
                || _owningItem.SubItems.Count <= index
                || GetSubItemBounds(index).IsEmpty)
            {
                return null;
            }
 
            return _owningItem.SubItems[index].AccessibilityObject;
        }
 
        public override int GetChildCount()
        {
            if (_owningListView.View != View.Tile)
            {
                throw new InvalidOperationException(string.Format(SR.ListViewItemAccessibilityObjectInvalidViewException, nameof(View.Tile)));
            }
 
            if (!_owningListView.IsHandleCreated || !_owningListView.SupportsListViewSubItems)
            {
                return InvalidIndex;
            }
 
            if (_owningItem.SubItems.Count == 1)
            {
                return _owningListView._labelEdit is not null ? 1 : 0;
            }
 
            return _owningListView._labelEdit is not null ? GetLastChildIndex() + 1 : GetLastChildIndex();
        }
 
        internal override int GetChildIndex(AccessibleObject? child)
        {
            if (child is null
                || !_owningListView.SupportsListViewSubItems
                || child == _owningItem.SubItems[0].AccessibilityObject
                || child is not ListViewSubItem.ListViewSubItemAccessibleObject subItemAccessibleObject
                || subItemAccessibleObject.OwningSubItem is null)
            {
                return InvalidIndex;
            }
 
            int index = _owningItem.SubItems.IndexOf(subItemAccessibleObject.OwningSubItem);
            return index == -1 || index > GetLastChildIndex() ? InvalidIndex : index;
        }
 
        private int GetLastChildIndex()
        {
            // Data about the first ListViewSubItem is displayed in the ListViewItem.
            // Therefore, it is not displayed in the ListViewSubItems list
            if (_owningItem.SubItems.Count == 1)
            {
                return InvalidIndex;
            }
 
            // Only ListViewSubItems with the corresponding columns are displayed in the ListView
            int subItemCount = Math.Min(_owningListView.Columns.Count, _owningItem.SubItems.Count);
 
            // The ListView can be of limited TileSize, so some of the ListViewSubItems can be hidden.
            // sListViewSubItems that do not have enough space to display have an empty bounds
            for (int i = 1; i < subItemCount; i++)
            {
                if (GetSubItemBounds(i).IsEmpty)
                {
                    return i - 1;
                }
            }
 
            return subItemCount - 1;
        }
 
        internal override Rectangle GetSubItemBounds(int subItemIndex)
            => _owningListView.IsHandleCreated
                ? _owningListView.GetSubItemRect(_owningItem.Index, subItemIndex)
                : Rectangle.Empty;
    }
}