File: System\Windows\Forms\Controls\TreeView\TreeNode.TreeNodeAccessibleObject.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.System.Variant;
using Windows.Win32.UI.Accessibility;
 
namespace System.Windows.Forms;
 
public partial class TreeNode
{
    internal sealed class TreeNodeAccessibleObject : AccessibleObject
    {
        private readonly TreeNode _owningTreeNode;
        private readonly TreeView _owningTreeView;
 
        public TreeNodeAccessibleObject(TreeNode owningTreeNode, TreeView owningTreeView)
        {
            _owningTreeNode = owningTreeNode.OrThrowIfNull();
            _owningTreeView = owningTreeView.OrThrowIfNull();
        }
 
        public override Rectangle Bounds
        {
            get
            {
                if (!_owningTreeView.IsHandleCreated || !_owningTreeNode.IsVisible)
                {
                    return Rectangle.Empty;
                }
 
                return _owningTreeNode.RectangleToScreen(_owningTreeNode.Bounds);
            }
        }
 
        public override string DefaultAction
        {
            get
            {
                if (_owningTreeView.CheckBoxes)
                {
                    return _owningTreeNode.Checked
                        ? SR.AccessibleActionUncheck
                        : SR.AccessibleActionCheck;
                }
 
                ExpandCollapseState expandCollapseState = ExpandCollapseState;
                if (expandCollapseState == ExpandCollapseState.ExpandCollapseState_LeafNode)
                {
                    return string.Empty;
                }
 
                return expandCollapseState == ExpandCollapseState.ExpandCollapseState_Expanded
                    ? SR.AccessibleActionCollapse
                    : SR.AccessibleActionExpand;
            }
        }
 
        internal override bool CanGetDefaultActionInternal => false;
 
        public override void DoDefaultAction()
        {
            if (_owningTreeView.CheckBoxes)
            {
                Toggle();
            }
 
            if (_owningTreeNode.IsExpanded)
            {
                Collapse();
            }
            else
            {
                Expand();
            }
 
            return;
        }
 
        internal override IRawElementProviderFragmentRoot.Interface FragmentRoot => _owningTreeView.AccessibilityObject;
 
        internal override IRawElementProviderFragment.Interface? FragmentNavigate(NavigateDirection direction)
            => direction switch
            {
                NavigateDirection.NavigateDirection_Parent
                    => Parent ?? _owningTreeView.AccessibilityObject,
                NavigateDirection.NavigateDirection_FirstChild
                     => _owningTreeNode.IsEditing
                        ? _owningTreeView._labelEdit?.AccessibilityObject
                        : _owningTreeNode.IsExpanded
                            ? _owningTreeNode.FirstNode?.AccessibilityObject
                            : null,
                NavigateDirection.NavigateDirection_LastChild
                    => _owningTreeNode.IsExpanded
                        ? _owningTreeNode.LastNode?.AccessibilityObject
                        : null,
                NavigateDirection.NavigateDirection_NextSibling
                    => _owningTreeNode.NextNode?.AccessibilityObject,
                NavigateDirection.NavigateDirection_PreviousSibling
                    => _owningTreeNode.PrevNode?.AccessibilityObject,
                _ => base.FragmentNavigate(direction),
            };
 
        internal override void SelectItem()
        {
            _owningTreeView.SelectedNode = _owningTreeNode;
        }
 
        internal override VARIANT GetPropertyValue(UIA_PROPERTY_ID propertyID)
            => propertyID switch
            {
                UIA_PROPERTY_ID.UIA_ControlTypePropertyId => (VARIANT)(int)UIA_CONTROLTYPE_ID.UIA_TreeItemControlTypeId,
                UIA_PROPERTY_ID.UIA_HasKeyboardFocusPropertyId => (VARIANT)State.HasFlag(AccessibleStates.Focused),
                UIA_PROPERTY_ID.UIA_IsEnabledPropertyId => (VARIANT)_owningTreeView.Enabled,
                UIA_PROPERTY_ID.UIA_IsKeyboardFocusablePropertyId => (VARIANT)State.HasFlag(AccessibleStates.Focusable),
                UIA_PROPERTY_ID.UIA_LevelPropertyId => (VARIANT)(_owningTreeNode.Level + 1),
                _ => base.GetPropertyValue(propertyID)
            };
 
        public override AccessibleObject? HitTest(int x, int y) => _owningTreeView.AccessibilityObject.HitTest(x, y);
 
        internal int Index => _owningTreeView.Nodes.IndexOf(_owningTreeNode);
 
        internal override bool IsPatternSupported(UIA_PATTERN_ID patternId)
            => patternId switch
            {
                UIA_PATTERN_ID.UIA_ExpandCollapsePatternId => true,
                UIA_PATTERN_ID.UIA_LegacyIAccessiblePatternId => true,
                UIA_PATTERN_ID.UIA_ScrollItemPatternId => true,
                UIA_PATTERN_ID.UIA_SelectionItemPatternId => true,
                UIA_PATTERN_ID.UIA_TogglePatternId when _owningTreeView.CheckBoxes => true,
                UIA_PATTERN_ID.UIA_ValuePatternId when _owningTreeView.LabelEdit => true,
                _ => base.IsPatternSupported(patternId),
            };
 
        public override string? Name => _owningTreeNode.Text;
 
        internal override bool CanGetNameInternal => false;
 
        public override AccessibleObject? Parent => _owningTreeNode.Parent?.AccessibilityObject;
 
        private protected override bool IsInternal => true;
 
        public override AccessibleRole Role
            => _owningTreeView.CheckBoxes
                ? AccessibleRole.CheckButton
                : AccessibleRole.OutlineItem;
 
        internal override int[] RuntimeId =>
        [
            RuntimeIDFirstItem,
            (int)_owningTreeView.InternalHandle,
            _owningTreeNode.GetHashCode()
        ];
 
        public override AccessibleStates State
        {
            get
            {
                AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable;
 
                if (!_owningTreeNode.IsVisible)
                {
                    state |= AccessibleStates.Invisible | AccessibleStates.Offscreen;
                }
 
                if (ExpandCollapseState == ExpandCollapseState.ExpandCollapseState_Expanded)
                {
                    state |= AccessibleStates.Expanded;
                }
                else if (ExpandCollapseState == ExpandCollapseState.ExpandCollapseState_Collapsed)
                {
                    state |= AccessibleStates.Collapsed;
                }
 
                if (_owningTreeNode.IsSelected)
                {
                    state |= AccessibleStates.Focused | AccessibleStates.Selected;
                }
 
                if (!_owningTreeView.Enabled)
                {
                    state |= AccessibleStates.Unavailable;
                }
 
                return state;
            }
        }
 
        #region Expand-Collapse Pattern
 
        internal override void Expand()
        {
            if (_owningTreeNode.Nodes.Count == 0)
            {
                return;
            }
 
            _owningTreeNode.Expand();
        }
 
        internal override void Collapse()
        {
            if (_owningTreeNode.Nodes.Count == 0)
            {
                return;
            }
 
            _owningTreeNode.Collapse();
        }
 
        internal override ExpandCollapseState ExpandCollapseState
        {
            get
            {
                if (_owningTreeNode.Nodes.Count == 0)
                {
                    return ExpandCollapseState.ExpandCollapseState_LeafNode;
                }
 
                return _owningTreeNode.IsExpanded
                    ? ExpandCollapseState.ExpandCollapseState_Expanded
                    : ExpandCollapseState.ExpandCollapseState_Collapsed;
            }
        }
 
        #endregion
 
        #region Scroll Item Pattern
 
        internal override void ScrollIntoView()
        {
            if (!_owningTreeView.IsHandleCreated || !_owningTreeView.Enabled)
            {
                return;
            }
 
            // We don't need to scroll if the item is visible.
            if (_owningTreeNode.IsVisible)
            {
                return;
            }
 
            _owningTreeView.TopNode = _owningTreeNode;
        }
 
        #endregion
 
        #region Selection Item Pattern
 
        internal override bool IsItemSelected => _owningTreeNode.IsSelected;
 
        internal override IRawElementProviderSimple.Interface? ItemSelectionContainer
            => _owningTreeView.AccessibilityObject;
 
        #endregion
 
        #region Toggle Pattern
 
        internal override void Toggle() => _owningTreeNode.Checked = !_owningTreeNode.Checked;
 
        internal override ToggleState ToggleState
            => _owningTreeNode.Checked ? ToggleState.ToggleState_On : ToggleState.ToggleState_Off;
 
        #endregion
 
        #region Value Pattern
 
        public override string? Value => _owningTreeNode.Text;
 
        internal override bool CanGetValueInternal => false;
 
        #endregion
    }
}