| File: System\Windows\Forms\Controls\ListBoxes\ListBox.ItemAccessibleObject.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\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.System.Variant; using Windows.Win32.UI.Accessibility; namespace System.Windows.Forms; public partial class ListBox { /// <summary> /// ListBox item control accessible object with UI Automation provider functionality. /// </summary> internal class ListBoxItemAccessibleObject : AccessibleObject { private readonly ItemArray.Entry _itemEntry; private readonly ListBoxAccessibleObject _owningAccessibleObject; private readonly ListBox _owningListBox; public ListBoxItemAccessibleObject(ListBox owningListBox, ItemArray.Entry itemEntry, ListBoxAccessibleObject owningAccessibleObject) { _owningListBox = owningListBox.OrThrowIfNull(); _itemEntry = itemEntry.OrThrowIfNull(); _owningAccessibleObject = owningAccessibleObject.OrThrowIfNull(); } protected int CurrentIndex => _owningListBox.Items.InnerArray.IndexOf(_itemEntry); internal override IRawElementProviderFragmentRoot.Interface FragmentRoot => _owningAccessibleObject; internal override bool IsItemSelected => State.HasFlag(AccessibleStates.Selected); internal override IRawElementProviderSimple.Interface ItemSelectionContainer => _owningAccessibleObject; public override AccessibleObject Parent => _owningAccessibleObject; private protected override bool IsInternal => true; internal override int[] RuntimeId { get { int[] id = _owningAccessibleObject.RuntimeId; Debug.Assert(id.Length >= 3); return [id[0], id[1], id[2], _itemEntry.GetHashCode()]; } } public override Rectangle Bounds { get { if (!_owningListBox.IsHandleCreated) { return Rectangle.Empty; } Rectangle bounds = _owningListBox.GetItemRectangle(CurrentIndex); if (bounds.IsEmpty) { return bounds; } bounds = _owningListBox.RectangleToScreen(bounds); Rectangle visibleArea = _owningListBox.RectangleToScreen(_owningListBox.ClientRectangle); if (visibleArea.Bottom < bounds.Bottom) { bounds.Height = visibleArea.Bottom - bounds.Top; } bounds.Width = visibleArea.Width; return bounds; } } public override string? DefaultAction => GetDefaultActionInternal().ToNullableStringAndFree(); internal override BSTR GetDefaultActionInternal() => _owningAccessibleObject.SystemIAccessible.TryGetDefaultAction(GetChildId()); public override string? Help => GetHelpInternal().ToNullableStringAndFree(); internal override BSTR GetHelpInternal() => _owningAccessibleObject.SystemIAccessible.TryGetHelp(GetChildId()); public override string? Name => _owningListBox.GetItemText(_itemEntry.Item); internal override bool CanGetNameInternal => false; public override AccessibleRole Role => _owningAccessibleObject.SystemIAccessible.TryGetRole(GetChildId()); public override AccessibleStates State { get { AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable; if (_owningListBox.SelectedIndex == CurrentIndex) { return state |= AccessibleStates.Selected | AccessibleStates.Focused; } return state |= _owningAccessibleObject.SystemIAccessible.TryGetState(GetChildId()); } } internal override void AddToSelection() { if (_owningListBox.IsHandleCreated) { SelectItem(); } } public override void DoDefaultAction() { if (_owningListBox.IsHandleCreated) { SetFocus(); } } internal override IRawElementProviderFragment.Interface? FragmentNavigate(NavigateDirection direction) { int firstItemIndex = 0; int lastItemIndex = _owningListBox.Items.Count - 1; int currentIndex = CurrentIndex; switch (direction) { case NavigateDirection.NavigateDirection_Parent: return _owningListBox.AccessibilityObject; case NavigateDirection.NavigateDirection_PreviousSibling: if (currentIndex > firstItemIndex && currentIndex <= lastItemIndex) { return _owningAccessibleObject.GetChild(currentIndex - 1); } return null; case NavigateDirection.NavigateDirection_NextSibling: if (currentIndex >= firstItemIndex && currentIndex < lastItemIndex) { return _owningAccessibleObject.GetChild(currentIndex + 1); } return null; } return base.FragmentNavigate(direction); } internal override int GetChildId() { // Index is zero-based, Child ID is 1-based. return CurrentIndex + 1; } internal override VARIANT GetPropertyValue(UIA_PROPERTY_ID propertyID) => propertyID switch { UIA_PROPERTY_ID.UIA_ControlTypePropertyId => (VARIANT)(int)UIA_CONTROLTYPE_ID.UIA_ListItemControlTypeId, UIA_PROPERTY_ID.UIA_HasKeyboardFocusPropertyId => (VARIANT)(_owningListBox.Focused && _owningListBox.FocusedIndex == CurrentIndex), UIA_PROPERTY_ID.UIA_IsEnabledPropertyId => (VARIANT)_owningListBox.Enabled, UIA_PROPERTY_ID.UIA_IsKeyboardFocusablePropertyId => (VARIANT)State.HasFlag(AccessibleStates.Focusable), UIA_PROPERTY_ID.UIA_NativeWindowHandlePropertyId => UIAHelper.WindowHandleToVariant(HWND.Null), _ => base.GetPropertyValue(propertyID) }; internal override bool IsPatternSupported(UIA_PATTERN_ID patternId) => patternId switch { UIA_PATTERN_ID.UIA_ScrollItemPatternId or UIA_PATTERN_ID.UIA_LegacyIAccessiblePatternId or UIA_PATTERN_ID.UIA_SelectionItemPatternId => true, _ => base.IsPatternSupported(patternId), }; internal override void RemoveFromSelection() { if (!_owningListBox.IsHandleCreated || _owningListBox.SelectionMode == SelectionMode.None || _owningListBox.SelectionMode == SelectionMode.One || !IsItemSelected) { return; } _owningListBox.SetSelected(CurrentIndex, value: false); } internal override void ScrollIntoView() { if (!_owningListBox.IsHandleCreated) { return; } int currentIndex = CurrentIndex; if (_owningListBox.SelectedIndex == -1) // no item selected { PInvokeCore.SendMessage(_owningListBox, PInvoke.LB_SETCARETINDEX, (WPARAM)currentIndex); return; } int firstVisibleIndex = (int)PInvokeCore.SendMessage(_owningListBox, PInvoke.LB_GETTOPINDEX); if (currentIndex < firstVisibleIndex) { PInvokeCore.SendMessage(_owningListBox, PInvoke.LB_SETTOPINDEX, (WPARAM)currentIndex); return; } int itemsHeightSum = 0; int listBoxHeight = _owningListBox.ClientRectangle.Height; int itemsCount = _owningListBox.Items.Count; for (int i = firstVisibleIndex; i < itemsCount; i++) { int itemHeight = (int)PInvokeCore.SendMessage(_owningListBox, PInvoke.LB_GETITEMHEIGHT, (WPARAM)i); if ((itemsHeightSum += itemHeight) <= listBoxHeight) { continue; } int lastVisibleIndex = i - 1; // - 1 because last "i" index is invisible int visibleItemsCount = lastVisibleIndex - firstVisibleIndex + 1; // + 1 because array indexes begin with 0 if (currentIndex > lastVisibleIndex) { PInvokeCore.SendMessage(_owningListBox, PInvoke.LB_SETTOPINDEX, (WPARAM)(currentIndex - visibleItemsCount + 1)); } break; } } internal override unsafe void SelectItem() { if (!_owningListBox.IsHandleCreated) { return; } _owningListBox.SelectedIndex = CurrentIndex; PInvoke.InvalidateRect(_owningListBox, lpRect: null, bErase: false); RaiseAutomationEvent(UIA_EVENT_ID.UIA_AutomationFocusChangedEventId); RaiseAutomationEvent(UIA_EVENT_ID.UIA_SelectionItem_ElementSelectedEventId); } internal override void SetFocus() { if (!_owningListBox.IsHandleCreated) { return; } RaiseAutomationEvent(UIA_EVENT_ID.UIA_AutomationFocusChangedEventId); SelectItem(); } public override void Select(AccessibleSelection flags) => _owningAccessibleObject.SystemIAccessible.TrySelect(flags, GetChildId()); // In .NET Framework 1.1, the ListBox accessible children did not have any selection capability. // In .NET Framework 2.0, they delegate the selection capability to OLEACC. // However, OLEACC does not deal with several selection flags: // ExtendSelection, AddSelection, RemoveSelection. // OLEACC instead throws an ArgumentException. // Since .NET Framework 2.0 API's should not throw an exception in places where // .NET Framework 1.1 API's did not, we catch the ArgumentException and fail silently. } }