File: winforms\Managed\System\WinForms\DataGridViewAccessibleObject.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="DataGridViewAccessibleObject.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>            
//------------------------------------------------------------------------------
 
using System.Security.Permissions;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
 
namespace System.Windows.Forms
{
    public partial class DataGridView
    {
        [
            System.Runtime.InteropServices.ComVisible(true)
        ]
        /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridView.DataGridViewAccessibleObject"]/*' />
        protected class DataGridViewAccessibleObject : ControlAccessibleObject
        {
            private int[] runtimeId = null; // Used by UIAutomation
 
            DataGridView owner;
            DataGridViewTopRowAccessibleObject topRowAccessibilityObject = null;
            DataGridViewSelectedCellsAccessibleObject selectedCellsAccessibilityObject = null;
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.DataGridViewAccessibleObject"]/*' />
            public DataGridViewAccessibleObject(DataGridView owner)
                : base(owner)
            {
                this.owner = owner;
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.Name"]/*' />
            public override string Name
            {
                [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] // Don't localize string "DataGridView".
                get
                {
                    string name = this.Owner.AccessibleName;
                    if (!String.IsNullOrEmpty(name))
                    {
                        return name;
                    }
                    else
                    {
                        // The default name should not be localized.
                        return "DataGridView";
                    }
                }
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.Role"]/*' />
            public override AccessibleRole Role
            {
                get
                {
                    AccessibleRole role = owner.AccessibleRole;
                    if (role != AccessibleRole.Default)
                    {
                        return role;
                    }
 
                    // the Default AccessibleRole is Table
                    return AccessibleRole.Table;
                }
            }
 
            private AccessibleObject TopRowAccessibilityObject
            {
                get
                {
                    if (this.topRowAccessibilityObject == null)
                    {
                        this.topRowAccessibilityObject = new DataGridViewTopRowAccessibleObject(this.owner);
                    }
 
                    return this.topRowAccessibilityObject;
                }
            }
 
            private AccessibleObject SelectedCellsAccessibilityObject
            {
                get
                {
                    if (this.selectedCellsAccessibilityObject == null)
                    {
                        this.selectedCellsAccessibilityObject = new DataGridViewSelectedCellsAccessibleObject(this.owner);
                    }
 
                    return this.selectedCellsAccessibilityObject;
                }
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.GetChild"]/*' />
            public override AccessibleObject GetChild(int index)
            {
                if (this.owner.Columns.Count == 0)
                {
                    System.Diagnostics.Debug.Assert(this.GetChildCount() == 0);
                    return null;
                }
 
                if (index < 1 && this.owner.ColumnHeadersVisible)
                {
                    return this.TopRowAccessibilityObject;
                }
 
                if (this.owner.ColumnHeadersVisible)
                {
                    index--;
                }
 
                if (index < this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible))
                {
                    int actualRowIndex = this.owner.Rows.DisplayIndexToRowIndex(index);
                    return this.owner.Rows[actualRowIndex].AccessibilityObject;
                }
 
                index -= this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible);
 
                if (this.owner.horizScrollBar.Visible)
                {
                    if (index == 0)
                    {
                        return this.owner.horizScrollBar.AccessibilityObject;
                    }
                    else
                    {
                        index--;
                    }
                }
 
                if (this.owner.vertScrollBar.Visible)
                {
                    if (index == 0)
                    {
                        return this.owner.vertScrollBar.AccessibilityObject;
                    }
                }
 
                return null;
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.GetChildCount"]/*' />
            public override int GetChildCount()
            {
                if (this.owner.Columns.Count == 0)
                {
                    return 0;
                }
 
                int childCount = this.owner.Rows.GetRowCount(DataGridViewElementStates.Visible);
 
                // the column header collection Accessible Object
                if (this.owner.ColumnHeadersVisible)
                {
                    childCount++;
                }
 
                if (this.owner.horizScrollBar.Visible)
                {
                    childCount++;
                }
 
                if (this.owner.vertScrollBar.Visible)
                {
                    childCount++;
                }
 
                return childCount;
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.GetFocused"]/*' />
            public override AccessibleObject GetFocused()
            {
                if (this.owner.Focused && this.owner.CurrentCell != null)
                {
                    return this.owner.CurrentCell.AccessibilityObject;
                }
                else
                {
                    return null;
                }
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.GetSelected"]/*' />
            public override AccessibleObject GetSelected()
            {
                return this.SelectedCellsAccessibilityObject;
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.HitTest"]/*' />
            public override AccessibleObject HitTest(int x, int y)
            {
                Point pt = this.owner.PointToClient(new Point(x, y));
                HitTestInfo hti = this.owner.HitTest(pt.X, pt.Y);
 
                switch (hti.Type)
                {
                    case DataGridViewHitTestType.Cell:
                        return this.owner.Rows[hti.RowIndex].Cells[hti.ColumnIndex].AccessibilityObject;
                    case DataGridViewHitTestType.ColumnHeader:
                        // map the column index to the actual display index
                        int actualDisplayIndex = this.owner.Columns.ColumnIndexToActualDisplayIndex(hti.ColumnIndex, DataGridViewElementStates.Visible);
                        if (this.owner.RowHeadersVisible)
                        {
                            // increment the childIndex because the first child in the TopRowAccessibleObject is the TopLeftHeaderCellAccObj
                            return this.TopRowAccessibilityObject.GetChild(actualDisplayIndex + 1);
                        }
                        else
                        {
                            return this.TopRowAccessibilityObject.GetChild(actualDisplayIndex);
                        }
                    case DataGridViewHitTestType.RowHeader:
                        return this.owner.Rows[hti.RowIndex].AccessibilityObject;
                    case DataGridViewHitTestType.TopLeftHeader:
                        return this.owner.TopLeftHeaderCell.AccessibilityObject;
                    case DataGridViewHitTestType.VerticalScrollBar:
                        return this.owner.VerticalScrollBar.AccessibilityObject;
                    case DataGridViewHitTestType.HorizontalScrollBar:
                        return this.owner.HorizontalScrollBar.AccessibilityObject;
                    default:
                        return null;
                }
            }
 
            /// <include file='doc\DataGridViewAccessibleObject.uex' path='docs/doc[@for="DataGridViewAccessibleObject.Navigate"]/*' />
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override AccessibleObject Navigate(AccessibleNavigation navigationDirection)
            {
                switch (navigationDirection)
                {
                    case AccessibleNavigation.FirstChild:
                        return this.GetChild(0);
                    case AccessibleNavigation.LastChild:
                        return this.GetChild(this.GetChildCount() - 1);
                    default:
                        return null;
                }
            }
 
            /* Microsoft: why is this method defined and not used?
            // this method is called when the accessible object needs to be reset
            // Example: when the user changes the display index on a column or when the user modifies the column collection
            internal void Reset()
            {
                this.NotifyClients(AccessibleEvents.Reorder);
            }
            */
 
            internal override int[] RuntimeId
            {
                get
                {
                    if (runtimeId == null)
                    {
                        runtimeId = new int[2];
                        runtimeId[0] = RuntimeIDFirstItem; // first item is static - 0x2a
                        runtimeId[1] = this.GetHashCode();
                    }
 
                    return runtimeId;
                }
            }
 
            internal override bool IsIAccessibleExSupported()
            {
                if (AccessibilityImprovements.Level2)
                {
                    return true;
                }
 
                return base.IsIAccessibleExSupported();
            }
 
            internal override object GetPropertyValue(int propertyID)
            {
                if (AccessibilityImprovements.Level3)
                {
                    switch (propertyID)
                    {
                        case NativeMethods.UIA_NamePropertyId:
                            return this.Name;
                        case NativeMethods.UIA_HasKeyboardFocusPropertyId:
                            return false; // Only inner cell should be announced as focused by Narrator but not entire DGV.
                        case NativeMethods.UIA_IsKeyboardFocusablePropertyId:
                            return owner.CanFocus;
                        case NativeMethods.UIA_IsEnabledPropertyId:
                            return owner.Enabled;
                        case NativeMethods.UIA_IsControlElementPropertyId:
                            return true;
                        case NativeMethods.UIA_IsTablePatternAvailablePropertyId:
                        case NativeMethods.UIA_IsGridPatternAvailablePropertyId:
                            return true;
                        case NativeMethods.UIA_ControlTypePropertyId:
                            return NativeMethods.UIA_TableControlTypeId;
                        case NativeMethods.UIA_ItemStatusPropertyId:
                            // Whether the owner DataGridView can be sorted by some column.
                            // If so, provide not-sorted/sorted-by item status.
                            bool canSort = false;
                            for (int i = 0; i < owner.Columns.Count; i++)
                            {
                                if (owner.IsSortable(owner.Columns[i]))
                                {
                                    canSort = true;
                                    break;
                                }
                            }
 
                            if (canSort)
                            {
                                switch (owner.SortOrder)
                                {
                                    case SortOrder.None:
                                        return SR.GetString(SR.NotSortedAccessibleStatus);
                                    case SortOrder.Ascending:
                                        return SR.GetString(SR.DataGridViewSortedAscendingAccessibleStatusFormat, owner.SortedColumn?.HeaderText);
                                    case SortOrder.Descending:
                                        return SR.GetString(SR.DataGridViewSortedDescendingAccessibleStatusFormat, owner.SortedColumn?.HeaderText);
                                }
                            }
 
                            break;
                    }
                }
 
                if (propertyID == NativeMethods.UIA_IsTablePatternAvailablePropertyId)
                {
                    return IsPatternSupported(NativeMethods.UIA_TablePatternId);
                }
                else if (propertyID == NativeMethods.UIA_IsGridPatternAvailablePropertyId)
                {
                    return IsPatternSupported(NativeMethods.UIA_GridPatternId);
                }
 
                return base.GetPropertyValue(propertyID);
            }
 
            internal override bool IsPatternSupported(int patternId)
            {
                if (patternId == NativeMethods.UIA_TablePatternId || 
                    patternId == NativeMethods.UIA_GridPatternId)
                {
                    return true;
                }
 
                return base.IsPatternSupported(patternId);
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaders()
            {
                if (!this.owner.RowHeadersVisible)
                {
                    return null;
                }
 
                UnsafeNativeMethods.IRawElementProviderSimple[] result = new UnsafeNativeMethods.IRawElementProviderSimple[this.owner.Rows.Count];
                for (int i = 0; i < this.owner.Rows.Count; i++)
                {
                    result[i] = this.owner.Rows[i].HeaderCell.AccessibilityObject;
                }
                return result;
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaders()
            {
                if (!this.owner.ColumnHeadersVisible)
                {
                    return null;
                }
 
                UnsafeNativeMethods.IRawElementProviderSimple[] result = new UnsafeNativeMethods.IRawElementProviderSimple[this.owner.Columns.Count];
                for (int i = 0; i < this.owner.Columns.Count; i++)
                {
                    result[i] = this.owner.Columns[i].HeaderCell.AccessibilityObject;
                }
                return result;
            }
 
            internal override UnsafeNativeMethods.RowOrColumnMajor RowOrColumnMajor
            {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get
                {
                    return UnsafeNativeMethods.RowOrColumnMajor.RowOrColumnMajor_RowMajor;
                }
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            internal override UnsafeNativeMethods.IRawElementProviderSimple GetItem(int row, int column)
            {
                if (row >= 0 && row < this.owner.Rows.Count &&
                    column >= 0 && column < this.owner.Columns.Count)
                {
                    return this.owner.Rows[row].Cells[column].AccessibilityObject;
                }
 
                return null;
            }
 
            internal override int RowCount
            {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get
                {
                    return this.owner.RowCount;
                }
            }
 
            internal override int ColumnCount
            {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get
                {
                    return this.owner.ColumnCount;
                }
            }
 
            #region IRawElementProviderFragment Implementation
 
            internal override Rectangle BoundingRectangle
            {
                get
                {
                    return this.Bounds;
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot
            {
                get
                {
                    return this;
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction)
            {
                switch (direction)
                {
                    case UnsafeNativeMethods.NavigateDirection.FirstChild:
                        int childCount = GetChildCount();
                        if (childCount > 0)
                        {
                            return GetChild(0);
                        }
                        break;
                    case UnsafeNativeMethods.NavigateDirection.LastChild:
                        childCount = GetChildCount();
                        if (childCount > 0)
                        {
                            int lastChildIndex = childCount - 1;
                            return GetChild(lastChildIndex);
                        }
                        break;
                }
 
                return null;
            }
 
            internal override void SetFocus()
            {
                if (owner.CanFocus)
                {
                    owner.Focus();
                }
            }
 
            #endregion
 
            #region IRawElementProviderFragmentRoot Implementation
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y)
            {
                return this.HitTest((int)x, (int)y);
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus()
            {
                return GetFocused();
            }
 
            #endregion
        }
        
        internal class DataGridViewEditingPanelAccessibleObject : ControlAccessibleObject
        {
            private DataGridView dataGridView;
            private Panel panel;
 
            public DataGridViewEditingPanelAccessibleObject(DataGridView dataGridView, Panel panel) : base(panel)
            {
                this.dataGridView = dataGridView;
                this.panel = panel;
            }
 
            #region IRawElementProviderFragment Implementation
 
            internal override Rectangle BoundingRectangle
            {
                get
                {
                    return panel.AccessibilityObject.Bounds;
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot
            {
                get
                {
                    return dataGridView.AccessibilityObject;
                }
            }
 
            internal override int[] RuntimeId
            {
                get
                {
                    return panel.AccessibilityObject.RuntimeId;
                }
            }
 
            internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction)
            {
                switch (direction)
                {
                    case UnsafeNativeMethods.NavigateDirection.Parent:
                        var currentCell = dataGridView.CurrentCell;
                        if (currentCell != null && dataGridView.IsCurrentCellInEditMode)
                        {
                            return currentCell.AccessibilityObject;
                        }
                        break;
                    case UnsafeNativeMethods.NavigateDirection.FirstChild:
                    case UnsafeNativeMethods.NavigateDirection.LastChild:
                        return dataGridView.EditingControlAccessibleObject;
                }
 
                return null;
            }
 
            internal override void SetFocus()
            {
                if (panel.CanFocus)
                {
                    panel.Focus();
                }
            }
 
            #endregion
 
            #region IRawElementProviderSimple Implementation
 
            internal override bool IsPatternSupported(int patternId)
            {
                return patternId.Equals(NativeMethods.UIA_LegacyIAccessiblePatternId);
            }
 
            internal override object GetPropertyValue(int propertyId)
            {
                switch (propertyId)
                {
                    case NativeMethods.UIA_NamePropertyId:
                        return SR.GetString(SR.DataGridView_AccEditingPanelAccName);
                    case NativeMethods.UIA_ControlTypePropertyId:
                        return NativeMethods.UIA_PaneControlTypeId;
                    case NativeMethods.UIA_IsKeyboardFocusablePropertyId:
                        return true;
                    case NativeMethods.UIA_HasKeyboardFocusPropertyId:
                        return this.dataGridView.CurrentCell != null;
                    case NativeMethods.UIA_IsEnabledPropertyId:
                        return dataGridView.Enabled;
                    case NativeMethods.UIA_IsOffscreenPropertyId:
                        return false;
                    case NativeMethods.UIA_IsControlElementPropertyId:
                    case NativeMethods.UIA_IsContentElementPropertyId:
                        return true;
                    case NativeMethods.UIA_IsPasswordPropertyId:
                        return false;
                    case NativeMethods.UIA_AccessKeyPropertyId:
                        return this.panel.AccessibilityObject.KeyboardShortcut;
                    case NativeMethods.UIA_HelpTextPropertyId:
                        return string.Empty;
                    case NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId:
                        return true;
                    case NativeMethods.UIA_ProviderDescriptionPropertyId:
                        return SR.GetString(SR.DataGridViewEditingPanelUiaProviderDescription);
                }
 
                return base.GetPropertyValue(propertyId);
            }
 
            #endregion
        }
    }
}