A day with .Net

My day to day experince in .net

Archive for January, 2015

UI Automation Provider for a GridView

Posted by vivekcek on January 13, 2015

The below code snippet can be used to support UI automation for a windows form gridview.
This is an open source code available in internet

Extending the gridview and implementing the provider.

public class CommonDataGridView : System.Windows.Forms.DataGridView,
           IRawElementProviderFragmentRoot,
           IGridProvider,
           ISelectionProvider
    {
        public const int WM_GETOBJECT = 0x3D;       // Windows Message GetObject

        /// <summary>
        /// Handles WM_GETOBJECT message; others are passed to base handler.
        /// </summary>
        /// <param name="m">Windows message.</param>
        /// <remarks>This method provides the link with UI Automation.</remarks>
        [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt64() == AutomationInteropProvider.RootObjectId))
            {
                m.Result = AutomationInteropProvider.ReturnRawElementProvider(
                    Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);

                return;
            }
            base.WndProc(ref m);
        }

        #region IRawElementProviderSimple Members

        /// <summary>
        /// Retrieves an object that provides support for a control  pattern on a UI Automation element.
        /// </summary>
        /// <param name="patternId">Identifier of the pattern.</param>
        /// <returns>
        /// Object that implements the pattern interface, or null if the pattern is not supported.
        /// </returns>
        object IRawElementProviderSimple.GetPatternProvider(int patternId)
        {
            if (patternId.Equals(GridPatternIdentifiers.Pattern.Id) ||
                patternId.Equals(SelectionPatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            return null;
        }

        /// <summary>
        /// Retrieves the value of a property supported by the UI Automation provider.
        /// </summary>
        /// <param name="propertyId">The property identifier.</param>
        /// <returns>
        /// The property value, or a null if the property is not supported by this provider, or <see cref="F:System.Windows.Automation.AutomationElementIdentifiers.NotSupported"/> if it is not supported at all.
        /// </returns>
        object IRawElementProviderSimple.GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.DataGrid.Id;
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id ||
                     propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return this.Name;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                bool canFocus = false;
                this.Invoke(new MethodInvoker(() => { canFocus = this.CanFocus; }));
                return canFocus;
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return this.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                return this.Enabled;
            }

            return null;
        }

        /// <summary>
        /// Gets a base provider for this element.
        /// </summary>
        /// <value></value>
        /// <returns>The base provider, or null.</returns>
        IRawElementProviderSimple IRawElementProviderSimple.HostRawElementProvider
        {
            get
            {
                IntPtr hwnd = IntPtr.Zero;
                Invoke(new MethodInvoker(() => { hwnd = this.Handle; }));
                return AutomationInteropProvider.HostProviderFromHandle(hwnd);
            }
        }

        /// <summary>
        /// Gets a value that specifies characteristics of the UI Automation provider; for example, whether it is a client-side or server-side provider.
        /// </summary>
        /// <value></value>
        /// <returns>Either <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ClientSideProvider"/> or <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ServerSideProvider"/>.</returns>
        ProviderOptions IRawElementProviderSimple.ProviderOptions
        {
            get
            {
                return ProviderOptions.ServerSideProvider;
            }
        }

        #endregion

        #region IRawElementProviderFragment Members

        /// <summary>
        /// Gets the bounding rectangle of this element.
        /// </summary>
        /// <value></value>
        /// <returns>The bounding rectangle, in screen coordinates.</returns>
        System.Windows.Rect IRawElementProviderFragment.BoundingRectangle
        {
            get
            {
                return System.Windows.Rect.Empty;
            }
        }

        /// <summary>
        /// Retrieves the root node of the fragment.
        /// </summary>
        /// <value></value>
        /// <returns>The root node. </returns>
        IRawElementProviderFragmentRoot IRawElementProviderFragment.FragmentRoot
        {
            get
            {
                return this;
            }
        }

        /// <summary>
        /// Retrieves an array of fragment roots that are embedded in the UI Automation element tree rooted at the current element.
        /// </summary>
        /// <returns>An array of root fragments, or null.</returns>
        IRawElementProviderSimple[] IRawElementProviderFragment.GetEmbeddedFragmentRoots()
        {
            List<IRawElementProviderSimple> embeddedFragmentRoots = new List<IRawElementProviderSimple>();
            foreach (DataGridViewRow row in this.Rows)
            {
                embeddedFragmentRoots.Add(new CommonDataGridViewRow(this, row));
            }

            return embeddedFragmentRoots.ToArray();
        }

        /// <summary>
        /// Retrieves the runtime identifier of an element.
        /// </summary>
        /// <returns>
        /// The unique run-time identifier of the element.
        /// </returns>
        int[] IRawElementProviderFragment.GetRuntimeId()
        {
            return null;
        }

        /// <summary>
        /// Retrieves the UI Automation element in a specified direction within the tree.
        /// </summary>
        /// <param name="direction">The direction in which to navigate.</param>
        /// <returns>
        /// The element in the specified direction, or null if there is no element in that direction
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragment.Navigate(NavigateDirection direction)
        {
            if (this.Rows.Count > 0)
            {
                switch (direction)
                {
                    case NavigateDirection.FirstChild:
                        return new CommonDataGridViewRow(this, Rows[0]);
                    case NavigateDirection.LastChild:
                        return new CommonDataGridViewRow(this, Rows[Rows.Count - 1]);
                }
            }

            return null;
        }

        /// <summary>
        /// Responds to a client request to set the focus to this control.
        /// </summary>
        /// <remarks>Setting focus to the control is handled by the parent window.</remarks>
        void IRawElementProviderFragment.SetFocus()
        {
            this.Invoke(new MethodInvoker(() =>
            {
                if (this.CanFocus)
                {
                    this.Focus();
                }
            }));
        }

        #endregion

        #region IRawElementProviderFragmentRoot Members

        /// <summary>
        /// Retrieves the element in this fragment that is at the specified point.
        /// </summary>
        /// <param name="x">The X coordinate,.</param>
        /// <param name="y">The Y coordinate.</param>
        /// <returns>
        /// The provider for the child element at the specified point, if one exists, or the root provider if the point is on this element but not on any child element. Otherwise returns null.
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y)
        {
            CommonDataGridViewRow returnRow = null;

            this.Invoke(new MethodInvoker(delegate()
            {
                System.Drawing.Point clientPoint = new System.Drawing.Point((int)x, (int)y);
                clientPoint = this.PointToClient(clientPoint);

                foreach (DataGridViewRow row in this.Rows)
                {
                    if (this.GetRowDisplayRectangle(row.Index, false).Contains(clientPoint))
                    {
                        returnRow = new CommonDataGridViewRow(this, row);
                        break;
                    }
                }
            }));

            return returnRow;
        }

        /// <summary>
        /// Retrieves the element in this fragment that has the input focus.
        /// </summary>
        /// <returns>
        /// The provider for the element in this fragment that has the input focus, if any; otherwise, a null reference (Nothing in Visual Basic).
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragmentRoot.GetFocus()
        {
            return new CommonDataGridViewRow(this, this.CurrentRow);
        }

        #endregion

        #region IGridProvider Members

        /// <summary>
        /// Gets the total number of columns in a grid.
        /// </summary>
        /// <value></value>
        /// <returns>The total number of columns in a grid.</returns>
        int IGridProvider.ColumnCount
        {
            get
            {
                return this.Columns.Count;
            }
        }

        /// <summary>
        /// Retrieves the UI Automation provider for the specified cell.
        /// </summary>
        /// <param name="row">The ordinal number of the row of interest.</param>
        /// <param name="column">The ordinal number of the column of interest.</param>
        /// <returns>
        /// The UI Automation provider for the specified cell.
        /// </returns>
        IRawElementProviderSimple IGridProvider.GetItem(int row, int column)
        {
            if (this.Rows.Count <= row)
            {
                throw new ArgumentException("Invalid row index specified.");
            }

            if (this.Columns.Count <= column)
            {
                throw new ArgumentException("Invalid column index spceified.");
            }

            return new CommonDataGridViewCell(this, new CommonDataGridViewRow(this, Rows[row]), Rows[row].Cells[column]);
        }

        /// <summary>
        /// Gets the number of rows excluding those that are hidden because the
        /// parent row is collapsed.
        /// </summary>
        /// <value></value>
        int IGridProvider.RowCount
        {
            get
            {
                return this.RowCount;
            }
        }

        #endregion

        #region ISelectionProvider Members

        /// <summary>
        /// Gets a value that specifies whether the UI Automation provider allows more than one child element to be selected concurrently.
        /// </summary>
        /// <value></value>
        /// <returns>true if multiple selection is allowed; otherwise false.</returns>
        bool ISelectionProvider.CanSelectMultiple
        {
            get
            {
                return this.MultiSelect;
            }
        }

        /// <summary>
        /// Retrieves a UI Automation provider for each child element that is selected.
        /// </summary>
        /// <returns>A collection of UI Automation providers.</returns>
        IRawElementProviderSimple[] ISelectionProvider.GetSelection()
        {
            List<IRawElementProviderSimple> selectedItems = new List<IRawElementProviderSimple>();

            foreach (DataGridViewRow selectedItem in this.SelectedRows)
            {
                selectedItems.Add(new CommonDataGridViewRow(this, selectedItem));
            }

            return selectedItems.ToArray();
        }

        /// <summary>
        /// Gets a value that specifies whether the UI Automation provider requires at least one child element to be selected.
        /// </summary>
        /// <value></value>
        /// <returns>true if selection is required; otherwise false.</returns>
        bool ISelectionProvider.IsSelectionRequired
        {
            get
            {
                return false;
            }
        }

        #endregion
    }

Class for the row of a gridview

 /// <summary>
    /// Common data grid view row class used for exposing each row to UI Automation.
    /// </summary>
    internal class CommonDataGridViewRow :
        IRawElementProviderFragmentRoot,
        ISelectionItemProvider
    {
        CommonDataGridView _grid;
        DataGridViewRow _row;

        /// <summary>
        /// Class Constructor.
        /// </summary>
        /// <param name="grid"></param>
        /// <param name="row"></param>
        public CommonDataGridViewRow(CommonDataGridView grid, DataGridViewRow row)
        {
            if (grid == null)
            {
                throw new ArgumentNullException("grid");
            }

            if (row == null)
            {
                throw new ArgumentNullException("row");
            }

            _grid = grid;
            _row = row;

        }

        #region IRawElementProviderFragmentRoot Members

        /// <summary>
        /// Retrieves the element in this fragment that is at the specified point.
        /// </summary>
        /// <param name="x">The X coordinate,.</param>
        /// <param name="y">The Y coordinate.</param>
        /// <returns>
        /// The provider for the child element at the specified point, if one exists, or the root provider if the point is on this element but not on any child element. Otherwise returns null.
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y)
        {
            // return cell from point value here
            System.Drawing.Point clientPoint = new System.Drawing.Point((int)x, (int)y);

            _grid.Invoke(new MethodInvoker(delegate() { clientPoint = _grid.PointToClient(clientPoint); }));

            foreach (DataGridViewCell cell in _row.Cells)
            {
                Rectangle cellBounds = new Rectangle();
                _grid.Invoke(new MethodInvoker(delegate() { cellBounds = _grid.GetCellDisplayRectangle(cell.ColumnIndex, cell.RowIndex, false); }));

                if (cellBounds.Contains(clientPoint))
                {
                    return new CommonDataGridViewCell(_grid, this, cell);
                }
            }

            return null;
        }

        /// <summary>
        /// Retrieves the element in this fragment that has the input focus.
        /// </summary>
        /// <returns>
        /// The provider for the element in this fragment that has the input focus, if any; otherwise, a null reference (Nothing in Visual Basic).
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragmentRoot.GetFocus()
        {
            // Return the cell that has focus here
            return new CommonDataGridViewCell(_grid, this, _grid.CurrentCell);
        }

        #endregion

        #region IRawElementProviderFragment Members

        /// <summary>
        /// Gets the bounding rectangle of this element.
        /// </summary>
        /// <value></value>
        /// <returns>The bounding rectangle, in screen coordinates.</returns>
        System.Windows.Rect IRawElementProviderFragment.BoundingRectangle
        {
            get
            {
                return System.Windows.Rect.Empty;
            }
        }

        /// <summary>
        /// Retrieves the root node of the fragment.
        /// </summary>
        /// <value></value>
        /// <returns>The root node. </returns>
        IRawElementProviderFragmentRoot IRawElementProviderFragment.FragmentRoot
        {
            get
            {
                return _grid;
            }
        }

        /// <summary>
        /// Retrieves an array of fragment roots that are embedded in the UI Automation element tree rooted at the current element.
        /// </summary>
        /// <returns>An array of root fragments, or null.</returns>
        IRawElementProviderSimple[] IRawElementProviderFragment.GetEmbeddedFragmentRoots()
        {
            return null;
        }

        /// <summary>
        /// Retrieves the runtime identifier of an element.
        /// </summary>
        /// <returns>
        /// The unique run-time identifier of the element.
        /// </returns>
        int[] IRawElementProviderFragment.GetRuntimeId()
        {
            return new int[] { AutomationInteropProvider.AppendRuntimeId, _row.Index };
        }

        /// <summary>
        /// Retrieves the UI Automation element in a specified direction within the tree.
        /// </summary>
        /// <param name="direction">The direction in which to navigate.</param>
        /// <returns>
        /// The element in the specified direction, or null if there is no element in that direction
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragment.Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.FirstChild:
                    return new CommonDataGridViewCell(_grid, this, _row.Cells[0]);

                case NavigateDirection.LastChild:
                    return new CommonDataGridViewCell(_grid, this, _row.Cells[_row.Cells.Count - 1]);

                case NavigateDirection.NextSibling:
                    if (_row.Index < _grid.Rows.Count - 1)
                    {
                        return new CommonDataGridViewRow(_grid, _grid.Rows[_row.Index + 1]);
                    }
                    break;

                case NavigateDirection.PreviousSibling:
                    if (_row.Index > 0)
                    {
                        return new CommonDataGridViewRow(_grid, _grid.Rows[_row.Index - 1]);
                    }
                    break;

                case NavigateDirection.Parent:
                    return _grid;
            }

            return null;
        }

        /// <summary>
        /// Sets the focus to this element.
        /// </summary>
        void IRawElementProviderFragment.SetFocus()
        {
            _grid.Invoke(new MethodInvoker(delegate() { _row.Selected = true; }));
        }

        #endregion

        #region IRawElementProviderSimple Members

        /// <summary>
        /// Retrieves an object that provides support for a control pattern on a UI Automation element.
        /// </summary>
        /// <param name="patternId">Identifier of the pattern.</param>
        /// <returns>
        /// Object that implements the pattern interface, or null if the pattern is not supported.
        /// </returns>
        object IRawElementProviderSimple.GetPatternProvider(int patternId)
        {
            if (patternId.Equals(SelectionItemPatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            return null;
        }

        /// <summary>
        /// Retrieves the value of a property supported by the UI Automation provider.
        /// </summary>
        /// <param name="propertyId">The property identifier.</param>
        /// <returns>
        /// The property value, or a null if the property is not supported by this provider, or <see cref="F:System.Windows.Automation.AutomationElementIdentifiers.NotSupported"/> if it is not supported at all.
        /// </returns>
        object IRawElementProviderSimple.GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                return true;
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return this.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id ||
                     propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.DataItem.Id;
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                return String.Format("Row {0}", _row.Index.ToString());
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                IRawElementProviderSimple provider = _grid as IRawElementProviderSimple;
                return ((bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id));
            }

            return null;
        }

        /// <summary>
        /// Gets a base provider for this element.
        /// </summary>
        /// <value></value>
        /// <returns>The base provider, or null.</returns>
        IRawElementProviderSimple IRawElementProviderSimple.HostRawElementProvider
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets a value that specifies characteristics of the UI Automation provider; for example, whether it is a client-side or server-side provider.
        /// </summary>
        /// <value></value>
        /// <returns>Either <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ClientSideProvider"/> or <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ServerSideProvider"/>.</returns>
        ProviderOptions IRawElementProviderSimple.ProviderOptions
        {
            get
            {
                return ProviderOptions.ServerSideProvider;
            }
        }

        #endregion

        #region ISelectionItemProvider Members

        /// <summary>
        /// Adds the current element to the collection of selected items.
        /// </summary>
        void ISelectionItemProvider.AddToSelection()
        {
            ((ISelectionItemProvider)this).Select();
        }

        /// <summary>
        /// Gets a value that indicates whether an item is selected.
        /// </summary>
        /// <value></value>
        /// <returns>true if the element is selected; otherwise false.</returns>
        bool ISelectionItemProvider.IsSelected
        {
            get
            {
                return _row.Selected;
            }
        }

        /// <summary>
        /// Removes the current element from the collection of selected items.
        /// </summary>
        void ISelectionItemProvider.RemoveFromSelection()
        {
            _grid.Invoke(new MethodInvoker(() =>
            {
                if (_row.Selected)
                {
                    _row.Selected = false;
                }
            }));
        }

        /// <summary>
        /// Deselects any selected items and then selects the current element.
        /// </summary>
        void ISelectionItemProvider.Select()
        {
            IRawElementProviderSimple provider = _grid as IRawElementProviderSimple;
            if (!(bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id))
            {
                throw new ElementNotEnabledException();
            }

            if (!((ISelectionItemProvider)this).IsSelected)
            {
                _grid.Invoke(new MethodInvoker(() =>
                {
                    _row.Selected = true;
                }));
            }
        }

        /// <summary>
        /// Gets the UI Automation provider that implements <see cref="T:System.Windows.Automation.Provider.ISelectionProvider"/> and acts as the container for the calling object.
        /// </summary>
        /// <value></value>
        /// <returns>The provider that supports <see cref="T:System.Windows.Automation.Provider.ISelectionProvider"/>. </returns>
        IRawElementProviderSimple ISelectionItemProvider.SelectionContainer
        {
            get
            {
                return _grid;
            }
        }

        #endregion
    }

Class for the cell of a gridview.

 /// <summary>
    /// Class that represents a GridCell for the purpose of Automation.
    /// </summary>
    internal class CommonDataGridViewCell :
        IRawElementProviderFragment,
        IGridItemProvider,
        IValueProvider,
        ISelectionItemProvider
    {
        private CommonDataGridView _grid;
        private CommonDataGridViewRow _row;
        private DataGridViewCell _cell;

        public CommonDataGridViewCell(CommonDataGridView grid, CommonDataGridViewRow row, DataGridViewCell cell)
        {
            if (grid == null)
            {
                throw new ArgumentNullException("grid");
            }

            if (row == null)
            {
                throw new ArgumentNullException("row");
            }

            if (cell == null)
            {
                throw new ArgumentNullException("cell");
            }

            _grid = grid;
            _row = row;
            _cell = cell;
        }

        #region IRawElementProviderSimple Members

        /// <summary>
        /// Retrieves an object that provides support for a control pattern on a UI Automation element.
        /// </summary>
        /// <param name="patternId">Identifier of the pattern.</param>
        /// <returns>
        /// Object that implements the pattern interface, or null if the pattern is not supported.
        /// </returns>
        object IRawElementProviderSimple.GetPatternProvider(int patternId)
        {
            if (patternId.Equals(GridItemPatternIdentifiers.Pattern.Id) ||
                patternId.Equals(ValuePatternIdentifiers.Pattern.Id) ||
                patternId.Equals(SelectionItemPatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Retrieves the value of a property supported by the UI Automation provider.
        /// </summary>
        /// <param name="propertyId">The property identifier.</param>
        /// <returns>
        /// The property value, or a null if the property is not supported by this provider, or <see cref="F:System.Windows.Automation.AutomationElementIdentifiers.NotSupported"/> if it is not supported at all.
        /// </returns>
        object IRawElementProviderSimple.GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id ||
                propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id)
            {
                return ControlType.DataItem.Id;
            }
            else if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return _grid.Columns[_cell.ColumnIndex].HeaderText;
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                return _cell.Value;
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return this.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.HasKeyboardFocusProperty.Id)
            {
                return _cell.IsInEditMode;
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                return _grid.Enabled;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                return _grid.Enabled && _grid.Visible;
            }

            return null;
        }

        /// <summary>
        /// Gets a base provider for this element.
        /// </summary>
        /// <value></value>
        /// <returns>The base provider, or null.</returns>
        IRawElementProviderSimple IRawElementProviderSimple.HostRawElementProvider
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets a value that specifies characteristics of the UI Automation provider; for example, whether it is a client-side or server-side provider.
        /// </summary>
        /// <value></value>
        /// <returns>Either <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ClientSideProvider"/> or <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ServerSideProvider"/>.</returns>
        ProviderOptions IRawElementProviderSimple.ProviderOptions
        {
            get
            {
                return ProviderOptions.ServerSideProvider;
            }
        }

        #endregion

        #region IRawElementProviderFragment Members

        /// <summary>
        /// Gets the bounding rectangle of this element.
        /// </summary>
        /// <value></value>
        /// <returns>The bounding rectangle, in screen coordinates.</returns>
        System.Windows.Rect IRawElementProviderFragment.BoundingRectangle
        {
            get
            {
                return System.Windows.Rect.Empty;
            }
        }

        /// <summary>
        /// Retrieves the root node of the fragment.
        /// </summary>
        /// <value></value>
        /// <returns>The root node. </returns>
        IRawElementProviderFragmentRoot IRawElementProviderFragment.FragmentRoot
        {
            get
            {
                return _row;
            }
        }

        /// <summary>
        /// Retrieves an array of fragment roots that are embedded in the UI Automation element tree rooted at the current element.
        /// </summary>
        /// <returns>An array of root fragments, or null.</returns>
        IRawElementProviderSimple[] IRawElementProviderFragment.GetEmbeddedFragmentRoots()
        {
            return null;
        }

        /// <summary>
        /// Retrieves the runtime identifier of an element.
        /// </summary>
        /// <returns>
        /// The unique run-time identifier of the element.
        /// </returns>
        int[] IRawElementProviderFragment.GetRuntimeId()
        {
            return new int[] { AutomationInteropProvider.AppendRuntimeId, _cell.ColumnIndex };
        }

        /// <summary>
        /// Retrieves the UI Automation element in a specified direction within the tree.
        /// </summary>
        /// <param name="direction">The direction in which to navigate.</param>
        /// <returns>
        /// The element in the specified direction, or null if there is no element in that direction
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragment.Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.NextSibling:
                    if (_cell.ColumnIndex < _grid.Columns.Count - 1)
                    {
                        return new CommonDataGridViewCell(_grid, _row, _cell.OwningRow.Cells[_cell.ColumnIndex + 1]);
                    }
                    break;
                case NavigateDirection.PreviousSibling:
                    if (_cell.ColumnIndex > 0)
                    {
                        return new CommonDataGridViewCell(_grid, _row, _cell.OwningRow.Cells[_cell.ColumnIndex - 1]);
                    }
                    break;
                case NavigateDirection.Parent:
                    return new CommonDataGridViewRow(_grid, _cell.OwningRow);
            }

            return null;
        }

        /// <summary>
        /// Sets the focus to this element.
        /// </summary>
        void IRawElementProviderFragment.SetFocus()
        {
            _grid.Invoke(new MethodInvoker(delegate()
            {
                _grid.CurrentCell = _cell;
            }));
        }

        #endregion

        #region IGridItemProvider Members

        /// <summary>
        /// Gets the ordinal number of the column that contains the cell or item.
        /// </summary>
        /// <value></value>
        /// <returns>A zero-based ordinal number that identifies the column containing the cell or item.</returns>
        int IGridItemProvider.Column
        {
            get
            {
                return _cell.ColumnIndex;
            }
        }

        /// <summary>
        /// Gets the number of columns spanned by a cell or item.
        /// </summary>
        /// <value></value>
        /// <returns>The number of columns spanned. </returns>
        int IGridItemProvider.ColumnSpan
        {
            get
            {
                return 1;
            }
        }

        /// <summary>
        /// Gets a UI Automation provider that implements <see cref="T:System.Windows.Automation.Provider.IGridProvider"/> and represents the container of the cell or item.
        /// </summary>
        /// <value></value>
        /// <returns>A UI Automation provider that implements the <see cref="T:System.Windows.Automation.GridPattern"/> and represents the cell or item container. </returns>
        IRawElementProviderSimple IGridItemProvider.ContainingGrid
        {
            get
            {
                return _grid;
            }
        }

        /// <summary>
        /// Gets the ordinal number of the row that contains the cell or item.
        /// </summary>
        /// <value></value>
        /// <returns>A zero-based ordinal number that identifies the row containing the cell or item. </returns>
        int IGridItemProvider.Row
        {
            get
            {
                return _cell.RowIndex;
            }
        }

        /// <summary>
        /// Gets the number of rows spanned by a cell or item.
        /// </summary>
        /// <value></value>
        /// <returns>The number of rows spanned. </returns>
        int IGridItemProvider.RowSpan
        {
            get
            {
                return 1;
            }
        }

        #endregion

        #region IValueProvider Members

        /// <summary>
        /// Gets a value that specifies whether the value of a control is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the value is read-only; false if it can be modified. </returns>
        bool IValueProvider.IsReadOnly
        {
            get
            {
                return _cell.State == DataGridViewElementStates.ReadOnly;
            }
        }

        /// <summary>
        /// Sets the value of a control.
        /// </summary>
        /// <param name="value"></param>
        /// <exception cref="T:System.InvalidOperationException">If locale-specific information is passed to a control in an incorrect format such as an incorrectly formatted date. </exception>
        /// <exception cref="T:System.ArgumentException">If a new value cannot be converted from a string to a format the control recognizes.</exception>
        /// <exception cref="T:System.Windows.Automation.ElementNotEnabledException">When an attempt is made  to manipulate a control that is not enabled.</exception>
        void IValueProvider.SetValue(string value)
        {
            // Check if we are enabled
            if (!(bool)((IRawElementProviderSimple)this).GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id))
            {
                throw new ElementNotEnabledException();
            }

            // Check if we are read only
            if (((IValueProvider)this).IsReadOnly)
            {
                throw new InvalidOperationException("Cannot set the value on a ReadOnly field!");
            }

            // Set Value
            _grid.Invoke(new MethodInvoker(delegate()
            {
                _grid.BeginEdit(false);
                _cell.Value = value;
                _grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
                _grid.EndEdit();
            }));
        }

        /// <summary>
        /// Gets the value of the control.
        /// </summary>
        /// <value></value>
        /// <returns>The value of the control as a string. </returns>
        string IValueProvider.Value
        {
            get
            {
                return _cell.Value == null ? null : _cell.Value.ToString();
            }
        }

        #endregion

        #region ISelectionItemProvider Members

        /// <summary>
        /// Adds the current element to the collection of selected items.
        /// </summary>
        void ISelectionItemProvider.AddToSelection()
        {
            _grid.Invoke(new MethodInvoker(() =>
            {
                _cell.Selected = true;
            }));
        }

        /// <summary>
        /// Gets a value that indicates whether an item is selected.
        /// </summary>
        /// <value></value>
        /// <returns>true if the element is selected; otherwise false.</returns>
        bool ISelectionItemProvider.IsSelected
        {
            get
            {
                bool selected = false;
                _grid.Invoke(new MethodInvoker(() => { selected = _grid.CurrentCell.Equals(_cell); }));
                return selected;
            }
        }

        /// <summary>
        /// Removes the current element from the collection of selected items.
        /// </summary>
        void ISelectionItemProvider.RemoveFromSelection()
        {
            _grid.Invoke(new MethodInvoker(() =>
            {
                _cell.Selected = false;
            }));
        }

        /// <summary>
        /// Deselects any selected items and then selects the current element.
        /// </summary>
        void ISelectionItemProvider.Select()
        {
            _grid.Invoke(new MethodInvoker(() =>
            {
                foreach (DataGridViewCell cell in _grid.SelectedCells)
                {
                    cell.Selected = false;
                }
                _cell.Selected = true;
            }));
        }

        /// <summary>
        /// Gets the UI Automation provider that implements <see cref="T:System.Windows.Automation.Provider.ISelectionProvider"/> and acts as the container for the calling object.
        /// </summary>
        /// <value></value>
        /// <returns>The provider that supports <see cref="T:System.Windows.Automation.Provider.ISelectionProvider"/>. </returns>
        IRawElementProviderSimple ISelectionItemProvider.SelectionContainer
        {
            get
            {
                return _grid;
            }
        }

        #endregion
    }
Advertisements

Posted in UI Automation | Tagged: , , , , | Leave a Comment »

UI Automation Provider for a Bar Chart

Posted by vivekcek on January 13, 2015

This post is based on an article in MSDN (https://code.msdn.microsoft.com/C-Windows-7-UIA-Provider-af7a6e31).

I made the source code available at MSDN, straight forward for better understnading.

Extending the chart control.

 public class MyChart : Chart
    {
        public MyChart(string labelX, string labelY, int yUpperValue)
            : base(labelX, labelY, yUpperValue)
        {

        }

        private ChartProvider provider;

        protected override void WndProc(ref Message m)
        {
            const int WM_GETOBJECT = 0x003D;

            if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() ==
                AutomationInteropProvider.RootObjectId))
            {
                m.Result = AutomationInteropProvider.ReturnRawElementProvider(
                        this.Handle, m.WParam, m.LParam,
                        this.Provider);
                return;
            }
            base.WndProc(ref m);
        }

        private ChartProvider Provider
        {
            get
            {
                if (this.provider == null)
                {
                    this.provider = new ChartProvider(this);
                }

                return this.provider;
            }
        }
    }

The chart provider, our root.

public class ChartProvider : IRawElementProviderFragmentRoot
    {

        MyChart _chart;

        public ChartProvider(MyChart chart)
        {
            _chart = chart;
        }

        #region IRawElementProviderSimple Members

        /// <summary>
        /// Retrieves an object that provides support for a control  pattern on a UI Automation element.
        /// </summary>
        /// <param name="patternId">Identifier of the pattern.</param>
        /// <returns>
        /// Object that implements the pattern interface, or null if the pattern is not supported.
        /// </returns>
        object IRawElementProviderSimple.GetPatternProvider(int patternId)
        {
            return null;
        }

        /// <summary>
        /// Retrieves the value of a property supported by the UI Automation provider.
        /// </summary>
        /// <param name="propertyId">The property identifier.</param>
        /// <returns>
        /// The property value, or a null if the property is not supported by this provider, or <see cref="F:System.Windows.Automation.AutomationElementIdentifiers.NotSupported"/> if it is not supported at all.
        /// </returns>
        object IRawElementProviderSimple.GetPropertyValue(int propertyId)
        {

            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.Custom.Id;
            }

            if (propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id)
            {
                return "Bar Chart";
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id ||
                     propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return _chart.Name;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                bool canFocus = false;
                _chart.Invoke(new MethodInvoker(() => { canFocus = _chart.CanFocus; }));
                return canFocus;
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return _chart.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                return _chart.Enabled;
            }

            else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                return true;
            }

            else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                return true;
            }

            return null;
        }

        /// <summary>
        /// Gets a base provider for this element.
        /// </summary>
        /// <value></value>
        /// <returns>The base provider, or null.</returns>
        IRawElementProviderSimple IRawElementProviderSimple.HostRawElementProvider
        {
            get
            {
                IntPtr hwnd = IntPtr.Zero;
                _chart.Invoke(new MethodInvoker(() => { hwnd = _chart.Handle; }));
                return AutomationInteropProvider.HostProviderFromHandle(hwnd);
            }
        }

        /// <summary>
        /// Gets a value that specifies characteristics of the UI Automation provider; for example, whether it is a client-side or server-side provider.
        /// </summary>
        /// <value></value>
        /// <returns>Either <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ClientSideProvider"/> or <see cref="F:System.Windows.Automation.Provider.ProviderOptions.ServerSideProvider"/>.</returns>
        ProviderOptions IRawElementProviderSimple.ProviderOptions
        {
            get
            {
                return ProviderOptions.ServerSideProvider | ProviderOptions.UseComThreading;
            }
        }

        #endregion

        #region IRawElementProviderFragment Members

        /// <summary>
        /// Gets the bounding rectangle of this element.
        /// </summary>
        /// <value></value>
        /// <returns>The bounding rectangle, in screen coordinates.</returns>
        System.Windows.Rect IRawElementProviderFragment.BoundingRectangle
        {
            get
            {
                return System.Windows.Rect.Empty;
            }
        }

        /// <summary>
        /// Retrieves the root node of the fragment.
        /// </summary>
        /// <value></value>
        /// <returns>The root node. </returns>
        IRawElementProviderFragmentRoot IRawElementProviderFragment.FragmentRoot
        {
            get
            {
                return this;
            }
        }

        /// <summary>
        /// Retrieves an array of fragment roots that are embedded in the UI Automation element tree rooted at the current element.
        /// </summary>
        /// <returns>An array of root fragments, or null.</returns>
        IRawElementProviderSimple[] IRawElementProviderFragment.GetEmbeddedFragmentRoots()
        {
            return null;
        }

        /// <summary>
        /// Retrieves the runtime identifier of an element.
        /// </summary>
        /// <returns>
        /// The unique run-time identifier of the element.
        /// </returns>
        int[] IRawElementProviderFragment.GetRuntimeId()
        {
            return null;
        }

        /// <summary>
        /// Retrieves the UI Automation element in a specified direction within the tree.
        /// </summary>
        /// <param name="direction">The direction in which to navigate.</param>
        /// <returns>
        /// The element in the specified direction, or null if there is no element in that direction
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragment.Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.FirstChild:
                    {
                        int cBars = _chart.GetBarCount();
                        if (cBars > 0)
                        {
                            return new ChartBarProvider(_chart, this, 0);
                        }

                        break;
                    }
                case NavigateDirection.LastChild:
                    {
                        // Return the last child, which is the last bar in the chart.
                        int cBars = _chart.GetBarCount();
                        if (cBars > 0)
                        {
                            return new ChartBarProvider(_chart, this, cBars - 1);
                        }

                        break;
                    }

            }

            return null;
        }

        /// <summary>
        /// Responds to a client request to set the focus to this control.
        /// </summary>
        /// <remarks>Setting focus to the control is handled by the parent window.</remarks>
        void IRawElementProviderFragment.SetFocus()
        {
            _chart.Invoke(new MethodInvoker(() =>
            {
                if (_chart.CanFocus)
                {
                    _chart.Focus();
                }
            }));
        }


        #endregion

        #region IRawElementProviderFragmentRoot Members

        /// <summary>
        /// Retrieves the element in this fragment that is at the specified point.
        /// </summary>
        /// <param name="x">The X coordinate,.</param>
        /// <param name="y">The Y coordinate.</param>
        /// <returns>
        /// The provider for the child element at the specified point, if one exists, or the root provider if the point is on this element but not on any child element. Otherwise returns null.
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragmentRoot.ElementProviderFromPoint(double x, double y)
        {
            // Convert screen point to client point.
            System.Drawing.Point clientPoint = _chart.PointToClient(new System.Drawing.Point((int)x, (int)y));

            int idxBar;
            int idxSection;

            if (_chart.GetBarSectionFromPoint(clientPoint, out idxBar, out idxSection))
            {
                ChartBarProvider barProvider = new ChartBarProvider(_chart, this, idxBar);

                // idxSection  is returned -1 if the point lies on a bar, but not on a section in the bar.
                if (idxSection == -1)
                {
                    return barProvider;
                }
                else
                {
                    // The BarSectionProvider must be aware of the bar it lies in, in order for
                    // UIA to be able to navigate up through the UIA tree from the Bar Section.
                    return new ChartBarSectionProvider(_chart, barProvider, idxBar, idxSection);
                }
            }

            return null;
        }

        /// <summary>
        /// Retrieves the element in this fragment that has the input focus.
        /// </summary>
        /// <returns>
        /// The provider for the element in this fragment that has the input focus, if any; otherwise, a null reference (Nothing in Visual Basic).
        /// </returns>
        IRawElementProviderFragment IRawElementProviderFragmentRoot.GetFocus()
        {
            return null;
        }

        #endregion


    }

The chart bar provider, this is one of the fragment.

public class ChartBarProvider : IRawElementProviderFragment, IValueProvider
    {
        private MyChart control;
        private int _idxBar;
        string strNameBar;
        protected IRawElementProviderFragment parent;
        protected IRawElementProviderFragmentRoot fragmentRoot;
        public ChartBarProvider(MyChart control, IRawElementProviderFragmentRoot root, int idxBar)
        {
            this.control = control;
            this._idxBar = idxBar;
            this.parent = root;
            this.fragmentRoot = root;
            strNameBar = control.GetNameFromBar(idxBar);
        }



        public System.Windows.Rect BoundingRectangle
        {
            get
            {
                System.Drawing.Rectangle screenRect = this.control.RectangleToScreen(this.control.RectFromBar(_idxBar));

                Rect result = new Rect();
                result.X = screenRect.X;
                result.Y = screenRect.Y;
                result.Width = screenRect.Width;
                result.Height = screenRect.Height;

                return result;
            }
        }

        public IRawElementProviderFragmentRoot FragmentRoot
        {
            get { return this.fragmentRoot; }
        }

        public IRawElementProviderSimple[] GetEmbeddedFragmentRoots()
        {
            return null;
        }

        public int[] GetRuntimeId()
        {
            int[] runtimeId = new int[2];

            runtimeId[0] = 3;
            runtimeId[1] = _idxBar;

            return runtimeId;
        }

        public IRawElementProviderFragment Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.FirstChild:
                    {
                        int cSections = control.GetBarSectionCount(_idxBar);
                        if (cSections > 0)
                        {
                            return new ChartBarSectionProvider(this.control, this, _idxBar, 0);
                        }

                        return null;

                    }

                case NavigateDirection.LastChild:
                    {
                        // Return our last child, which is the last section in the bar.
                        int cSections = control.GetBarSectionCount(_idxBar);
                        if (cSections > 0)
                        {
                            return new ChartBarSectionProvider(this.control, this, _idxBar, Math.Max(cSections - 1, 0));
                        }
                        return null;
                    }

                case NavigateDirection.NextSibling:
                    {

                        // Return the fragment for the next bar in the chart.
                        if (_idxBar < control.GetBarCount() - 1)
                        {
                            return new ChartBarProvider(this.control, this.fragmentRoot, _idxBar + 1);
                        }
                        return null;
                    }

                case NavigateDirection.PreviousSibling:
                    {
                        // Return the fragment for the previous bar in the chart.
                        if (_idxBar > 0)
                        {
                            return new ChartBarProvider(this.control, this.fragmentRoot, _idxBar - 1);
                        }
                        return null;
                    }

                

            }

            return null;
        }

        public void SetFocus()
        {
            throw new NotImplementedException();
        }

        public object GetPatternProvider(int patternId)
        {
            if (patternId.Equals(ValuePatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            else
            {
                return null;
            }
        }

        public object GetPropertyValue(int propertyId)
        {

            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.Custom.Id;
            }

            if (propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id)
            {
                return "Chart Bar";
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                return strNameBar;
            }
            else if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return "ChartBar" + strNameBar;
            }
            else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                return true;
            }

            else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                return false;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                bool canFocus = false;
                return canFocus;
            }
            return null;
        }

        public IRawElementProviderSimple HostRawElementProvider
        {
            get { return null; }
        }

        public ProviderOptions ProviderOptions
        {
            get
            {
                return ProviderOptions.ServerSideProvider|ProviderOptions.UseComThreading;
            }
        }

        // IValuePattern members.
        public bool IsReadOnly
        {
            get { return true; }
        }

        public string Value
        {
            get { return control.GetValueFromBar(_idxBar).ToString(); }
        }

        public void SetValue(string value)
        {
            throw new InvalidOperationException("Chart bar is read-only.");
        }
    }

The chart section provider, this is another fragment

public class ChartBarSectionProvider : IRawElementProviderFragment, IValueProvider
    {
        private MyChart _chart;
        private int _idxBar;
        private int _idxSection;
        string strNameBar, strNameSection;
        protected IRawElementProviderFragment parent;
        protected IRawElementProviderFragmentRoot fragmentRoot;
        public ChartBarSectionProvider(MyChart chart, IRawElementProviderFragment parent, int idxBar, int idxSection)
        {
            this._chart = chart;
            this._idxBar = idxBar;
            this._idxSection = idxSection;
            strNameBar = _chart.GetNameFromBar(idxBar);
            strNameSection = _chart.GetNameFromBarSection(idxBar, idxSection);
            this.parent = parent;
            this.fragmentRoot = parent.FragmentRoot;
        }

        public Rect BoundingRectangle
        {
            get
            {
                System.Drawing.Rectangle screenRect = _chart.RectangleToScreen(_chart.RectFromSection(_idxBar, _idxSection));

                Rect result = new Rect();
                result.X = screenRect.X;
                result.Y = screenRect.Y;
                result.Width = screenRect.Width;
                result.Height = screenRect.Height;

                return result;
            }
        }

        public IRawElementProviderFragmentRoot FragmentRoot
        {
            get { return this.fragmentRoot; }
        }

        public IRawElementProviderSimple[] GetEmbeddedFragmentRoots()
        {
            return null;
        }

        public int[] GetRuntimeId()
        {
            int[] runtimeId = new int[3];

            runtimeId[0] = 3;
            runtimeId[1] = _idxBar;
            runtimeId[2] = _idxSection;

            return runtimeId;
        }

        public IRawElementProviderFragment Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                

                case NavigateDirection.NextSibling:
                    {

                        // Return the fragment for the next section in the bar.
                        int cSections = _chart.GetBarSectionCount(_idxBar);
                        if (_idxSection < cSections - 1)
                        {
                            return new ChartBarSectionProvider(_chart, this.parent, _idxBar, _idxSection + 1);
                        }
                        return null;
                    }

                case NavigateDirection.PreviousSibling:
                    {
                        // Return the fragment for the previous section in the bar.
                        int cSections = _chart.GetBarSectionCount(_idxBar);
                        if (cSections > 0)
                        {
                            return new ChartBarSectionProvider(_chart, this.parent, _idxBar, cSections - 1);
                        }
                        return null;
                    }



            }

            return null;
        }

        public void SetFocus()
        {
            throw new NotImplementedException();
        }

        public object GetPatternProvider(int patternId)
        {
            if (patternId.Equals(ValuePatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            else
            {
                return null;
            }
        }

        public object GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.Custom.Id;
            }

            if (propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id)
            {
                return "Chart Bar Section";
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                return strNameSection;
            }
            else if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return "ChartBar" + strNameBar + strNameSection;
            }
            else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                return true;
            }

            else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
            {
                return false;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                bool canFocus = false;
                return canFocus;
            }
            return null;
        }

        public IRawElementProviderSimple HostRawElementProvider
        {
            get { return null; }
        }

        public ProviderOptions ProviderOptions
        {
            get { return ProviderOptions.ServerSideProvider | ProviderOptions.UseComThreading; }
        }

        // IValuePattern members.
        public bool IsReadOnly
        {
            get { return true; }
        }

        public string Value
        {
            get { return _chart.GetValueFromBarSection(_idxBar, _idxSection).ToString(); }
        }

        public void SetValue(string value)
        {
            throw new InvalidOperationException("Chart bar section is read-only.");
        }
    }

Posted in UI Automation | Tagged: , , | Leave a Comment »

White UI to access a Grid – UI Automation

Posted by vivekcek on January 9, 2015

This code can be used to access a grid cells via White UI.
The grid should implement GridPattern and TablePattern.

var application = Application.Attach("Process");
var window = application.GetWindow("Window Title");
var datagrid = window.Get<Table>("GridName").AutomationElement;
var cacheRequest = new CacheRequest { AutomationElementMode = AutomationElementMode.Full, TreeScope = TreeScope.Descendants };
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(ValuePattern.Pattern);
cacheRequest.Add(TablePattern.Pattern);
cacheRequest.Push();
var gridLines = datagrid.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem));
cacheRequest.Pop();
var list = datagrid.GetSupportedPatterns();
var data = (datagrid.GetCurrentPattern(GridPattern.Pattern) as GridPattern).GetItem(1, 1);
var final = (data.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern).Current.Value;

Posted in UI Automation | Tagged: | Leave a Comment »

UI Automation Provider For a Custom Control

Posted by vivekcek on January 9, 2015

I hope the reader have sound knowledge about Microsoft UI Automation Framework (UIA).

Tools Required?

Visual Studio 2010/2012/2013.
Visual UI Spy (Link)
VIBlend Custom Grid Control (Link).

Libraries needed.

UIAutomationProvider.dll
UIAutomationTypes.dll
WindowsBase.dll

Look at the below screen, which contain two grid controls, one is a custom VIBlend grid ,another a default gridview in .NET.

Here is the Problem.

When we inspect both controls with Visual UI Spy,found that our custom VI Blend Grid control is identified as pane, also it’s rows and cells are not recognized.
But look at our default grid view, it is identified as table and its rows and cells are accessible.

1

Why this happen?

Because the default grid view come with .NET support UI automation, but custom controls may or may not support UI automation.

How to solve this?

Give server side UI automation support for the custom control (Refer MSDN if you don’t know about Server Side Provider).

Implementation

We use the concept of sub-classing here, as the source code of the custom control is not available with us.
We use this extended control in our forms.

Inside our custom control we override WndProc and catch the WM_GETOBJECT message and return our Server Side Provider implementation.

Code snippet to extend control.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Automation.Provider;
using System.Windows.Forms;

namespace VIBlendUIAutomation
{
    public class CustomGrid : VIBlend.WinForms.DataGridView.vDataGridView
    {
        private CustomGridProvider provider;

        protected override void WndProc(ref Message m)
        {
            const int WM_GETOBJECT = 0x003D;

            if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() ==
                AutomationInteropProvider.RootObjectId))
            {
                m.Result = AutomationInteropProvider.ReturnRawElementProvider(
                        this.Handle, m.WParam, m.LParam,
                        this.Provider);
                return;
            }
            base.WndProc(ref m);
        }

        private CustomGridProvider Provider
        {
            get
            {
                if (this.provider == null)
                {
                    this.provider = new CustomGridProvider(this);
                }

                return this.provider;
            }
        }
    }
}

Code snippet to use the control in Form.

private CustomGrid vDataGridView1;
this.vDataGridView1 = new CustomGrid();

The core concept behind Server Side automation provider implementation is, convert the control into fragments and construct a tree.
Here we construct a tree with our Grid as root, rows and cells as it children.

Implementation of provider class, the important method here is Navigate(), which is responsible for constructing the tree.
This provider class implement ‘IRawElementProviderFragmentRoot’ , means provider is our root.
Provider also implement ‘IGridProvider’, which helps us to access cells with index.

Code snippet of provider class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows.Forms;


namespace VIBlendUIAutomation
{
    public class CustomGridProvider : IRawElementProviderFragmentRoot, IGridProvider
    {
        private CustomGrid _grid;

        public CustomGridProvider(CustomGrid grid)
        {
            _grid = grid;
        }

        #region IRawElementProviderFragmentRoot

        public IRawElementProviderFragment ElementProviderFromPoint(double x, double y)
        {
            throw new NotImplementedException();
        }

        public IRawElementProviderFragment GetFocus()
        {
            throw new NotImplementedException();
        }

        public System.Windows.Rect BoundingRectangle
        {
            get
            {
                return System.Windows.Rect.Empty;
            }
        }

        public IRawElementProviderFragmentRoot FragmentRoot
        {
            get
            {
                return this;
            }
        }

        public IRawElementProviderSimple[] GetEmbeddedFragmentRoots()
        {
            List<IRawElementProviderSimple> embeddedFragmentRoots = new List<IRawElementProviderSimple>();
            for (int i = 0; i < this._grid.RowsHierarchy.Items.Count; i++)
            {
                embeddedFragmentRoots.Add(new CustomGridRow(this._grid, this, i));
            }

            return embeddedFragmentRoots.ToArray();

        }

        public int[] GetRuntimeId()
        {
            return null;
        }

        public IRawElementProviderFragment Navigate(NavigateDirection direction)
        {
            if (this._grid.RowsHierarchy.Items.Count > 0)
            {
                switch (direction)
                {
                    case NavigateDirection.FirstChild:
                        return new CustomGridRow(this._grid, this, 0);
                    case NavigateDirection.LastChild:
                        return new CustomGridRow(this._grid, this, this._grid.RowsHierarchy.Items.Count - 1);
                }
            }

            return null;
        }

        public void SetFocus()
        {
            throw new NotImplementedException();
        }

        public object GetPatternProvider(int patternId)
        {
            if (patternId.Equals(GridPatternIdentifiers.Pattern.Id) ||
                patternId.Equals(SelectionPatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            return null;
        }

        public object GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.DataGrid.Id;
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id ||
                     propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return _grid.Name;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                bool canFocus = false;
                _grid.Invoke(new MethodInvoker(() => { canFocus = _grid.CanFocus; }));
                return canFocus;
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return this.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                return _grid.Enabled;
            }

            return null;
        }

        public IRawElementProviderSimple HostRawElementProvider
        {
            get
            {
                IntPtr hwnd = IntPtr.Zero;
                _grid.Invoke(new MethodInvoker(() => { hwnd = _grid.Handle; }));
                return AutomationInteropProvider.HostProviderFromHandle(hwnd);
            }
        }

        public ProviderOptions ProviderOptions
        {
            get
            {
                return ProviderOptions.ServerSideProvider | ProviderOptions.UseComThreading;
            }
        }

        #endregion

        #region IGridProvider
        public int ColumnCount
        {
            get { return this._grid.ColumnsHierarchy.Items.Count; }
        }

        public IRawElementProviderSimple GetItem(int row, int column)
        {
            if (this._grid.RowsHierarchy.Items.Count <= row)
            {
                throw new ArgumentException("Invalid row index specified.");
            }

            if (this._grid.ColumnsHierarchy.Items.Count <= column)
            {
                throw new ArgumentException("Invalid column index spceified.");
            }

            return new CustomGridCell(this._grid, this, row, column);
        }

        public int RowCount
        {
            get { return this._grid.RowsHierarchy.Items.Count; }
        }
        #endregion

        #region ISelectionProvider
        public bool CanSelectMultiple
        {
            get { return true; }
        }

        public IRawElementProviderSimple[] GetSelection()
        {
            List<IRawElementProviderSimple> selectedItems = new List<IRawElementProviderSimple>();

            foreach (var selectedItem in this._grid.RowsHierarchy.SelectedItems)
            {
                selectedItems.Add(new CustomGridRow(this._grid,this,selectedItem.ItemIndex));
            }

            return selectedItems.ToArray();
        }

        public bool IsSelectionRequired
        {
            get { return false; }
        }
        #endregion
    }
}

Rows are considered as the children of provider, This class implement two interfaces IRawElementProviderFragmentRoot and ISelectionItemProvider

Code snippet of custom row class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Automation.Provider;

namespace VIBlendUIAutomation
{
    public class CustomGridRow : IRawElementProviderFragmentRoot, ISelectionItemProvider
    {
        private CustomGrid _grid;
        private int _rowIndex;
        protected IRawElementProviderFragment parent;
        protected IRawElementProviderFragmentRoot fragmentRoot;

        public CustomGridRow(CustomGrid grid, IRawElementProviderFragmentRoot root, int rowIndex)
        {
            _grid = grid;
            _rowIndex = rowIndex;
            this.parent = root;
            this.fragmentRoot = root;
        }

        #region IRawElementProviderFragmentRoot

        public IRawElementProviderFragment ElementProviderFromPoint(double x, double y)
        {
            throw new NotImplementedException();
        }

        public IRawElementProviderFragment GetFocus()
        {
            throw new NotImplementedException();
        }

        public System.Windows.Rect BoundingRectangle
        {
            get { return System.Windows.Rect.Empty; }
        }

        public IRawElementProviderFragmentRoot FragmentRoot
        {
            get { return this.fragmentRoot; }
        }

        public IRawElementProviderSimple[] GetEmbeddedFragmentRoots()
        {
            return null;
        }

        public int[] GetRuntimeId()
        {
            return new int[] { AutomationInteropProvider.AppendRuntimeId, _rowIndex };
        }

        public IRawElementProviderFragment Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.FirstChild:
                    return new CustomGridCell(_grid, this, _rowIndex, 0);

                case NavigateDirection.LastChild:
                    return new CustomGridCell(_grid, this, _rowIndex, this._grid.ColumnsHierarchy.Items.Count - 1);

                case NavigateDirection.NextSibling:
                    if (_rowIndex < _grid.RowsHierarchy.Items.Count - 1)
                    {
                        return new CustomGridRow(_grid, this.fragmentRoot, _rowIndex + 1);
                    }
                    break;

                case NavigateDirection.PreviousSibling:
                    if (_rowIndex > 0)
                    {
                        return new CustomGridRow(_grid, this.fragmentRoot, _rowIndex - 1);
                    }
                    break;

                case NavigateDirection.Parent:
                    return this.parent;
            }

            return null;
        }

        public void SetFocus()
        {
            throw new NotImplementedException();
        }

        public object GetPatternProvider(int patternId)
        {

            if (patternId.Equals(SelectionItemPatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            return null;
        }

        public object GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                return true;
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return this.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id ||
                     propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
            {
                return ControlType.DataItem.Id;
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                return String.Format("Row {0}", _rowIndex.ToString());
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                IRawElementProviderSimple provider = this.parent as IRawElementProviderSimple;
                return ((bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id));
            }

            return null;
        }

        public IRawElementProviderSimple HostRawElementProvider
        {
            get { return null; }
        }

        public ProviderOptions ProviderOptions
        {
            get { return System.Windows.Automation.Provider.ProviderOptions.ServerSideProvider | System.Windows.Automation.Provider.ProviderOptions.UseComThreading; }
        }

        #endregion

        #region ISelectionItemProvider
        public void AddToSelection()
        {
            this._grid.RowsHierarchy.Items[_rowIndex].Selected = true;
        }

        public bool IsSelected
        {
            get { return this._grid.RowsHierarchy.Items[_rowIndex].Selected; }
        }

        public void RemoveFromSelection()
        {
            this._grid.RowsHierarchy.Items[_rowIndex].Selected = false;
        }

        public void Select()
        {
            this._grid.RowsHierarchy.Items[_rowIndex].Selected = true;
        }

        public IRawElementProviderSimple SelectionContainer
        {
            get { return this.parent; }
        }
        #endregion


    }
}

Cells are considered as the children of rows, This class implement the following interface IRawElementProviderFragment, IGridItemProvider, IValueProvider, ISelectionItemProvider.

Code snippet of custom cell class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Automation.Provider;

namespace VIBlendUIAutomation
{
    public class CustomGridCell : IRawElementProviderFragment, IGridItemProvider, IValueProvider, ISelectionItemProvider
    {

        private CustomGrid _grid;
        private int _rowIndex;
        private int _cellIndex;
        protected IRawElementProviderFragment parent;
        protected IRawElementProviderFragmentRoot fragmentRoot;

        public CustomGridCell(CustomGrid grid, IRawElementProviderFragmentRoot root, int rowIndex, int cellIndex)
        {
            _grid = grid;
            _rowIndex = rowIndex;
            _cellIndex = cellIndex;
            this.parent = root;
            this.fragmentRoot = root;
        }

        #region IRawElementProviderFragment
        public System.Windows.Rect BoundingRectangle
        {
            get { return System.Windows.Rect.Empty; }
        }

        public IRawElementProviderFragmentRoot FragmentRoot
        {
            get { return this.fragmentRoot; }
        }

        public IRawElementProviderSimple[] GetEmbeddedFragmentRoots()
        {
            return null;
        }

        public int[] GetRuntimeId()
        {
            return new int[] { AutomationInteropProvider.AppendRuntimeId, _rowIndex, _cellIndex };
        }

        public IRawElementProviderFragment Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.NextSibling:
                    if (_cellIndex < _grid.ColumnsHierarchy.Items.Count - 1)
                    {
                        return new CustomGridCell(_grid, this.fragmentRoot, _rowIndex, _cellIndex + 1);
                    }
                    break;
                case NavigateDirection.PreviousSibling:
                    if (_cellIndex > 0)
                    {
                        return new CustomGridCell(_grid, this.fragmentRoot, _rowIndex, _cellIndex - 1);
                    }
                    break;
                case NavigateDirection.Parent:
                    return new CustomGridRow(_grid, this.parent.FragmentRoot, _rowIndex);
            }

            return null;
        }

        public void SetFocus()
        {
            throw new NotImplementedException();
        }

        public object GetPatternProvider(int patternId)
        {

            if (patternId.Equals(GridItemPatternIdentifiers.Pattern.Id) ||
               patternId.Equals(ValuePatternIdentifiers.Pattern.Id) ||
               patternId.Equals(SelectionItemPatternIdentifiers.Pattern.Id))
            {
                return this;
            }
            else
            {
                return null;
            }

        }

        public object GetPropertyValue(int propertyId)
        {
            if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id ||
               propertyId == AutomationElementIdentifiers.LocalizedControlTypeProperty.Id)
            {
                return ControlType.DataItem.Id;
            }
            else if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
            {
                return "Cell[" + _rowIndex.ToString() + "][" + _cellIndex.ToString() + "]"; ;
            }
            else if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
            {
                return this._grid.CellsArea.GetCellValue(this._grid.RowsHierarchy.Items[_rowIndex], this._grid.ColumnsHierarchy.Items[_cellIndex]);
            }
            else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
            {
                return this.GetType().ToString();
            }
            else if (propertyId == AutomationElementIdentifiers.HasKeyboardFocusProperty.Id)
            {
                return false;
            }
            else if (propertyId == AutomationElementIdentifiers.IsEnabledProperty.Id)
            {
                return _grid.Enabled;
            }
            else if (propertyId == AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id)
            {
                return _grid.Enabled && _grid.Visible;
            }

            return null;
        }

        public IRawElementProviderSimple HostRawElementProvider
        {
            get { return null; }
        }

        public ProviderOptions ProviderOptions
        {
            get { return System.Windows.Automation.Provider.ProviderOptions.ServerSideProvider | System.Windows.Automation.Provider.ProviderOptions.UseComThreading; }
        }
        #endregion

        #region IGridItemProvider
        public int Column
        {
            get { throw new NotImplementedException(); }
        }

        public int ColumnSpan
        {
            get { throw new NotImplementedException(); }
        }

        public IRawElementProviderSimple ContainingGrid
        {
            get { throw new NotImplementedException(); }
        }

        public int Row
        {
            get { throw new NotImplementedException(); }
        }

        public int RowSpan
        {
            get { throw new NotImplementedException(); }
        }
        #endregion

        #region IValueProvider
        public bool IsReadOnly
        {
            get { return false; }
        }

        public void SetValue(string value)
        {
            this._grid.CellsArea.SetCellValue(this._grid.RowsHierarchy.Items[_rowIndex], this._grid.ColumnsHierarchy.Items[_cellIndex], value);
        }

        public string Value
        {
            get { return this._grid.CellsArea.GetCellValue(this._grid.RowsHierarchy.Items[_rowIndex], this._grid.ColumnsHierarchy.Items[_cellIndex]).ToString(); }
        }
        #endregion

        #region ISelectionItemProvider
        public void AddToSelection()
        {
            this._grid.ColumnsHierarchy.Items[_cellIndex].Selected = true;
        }

        public bool IsSelected
        {
            get { return this._grid.ColumnsHierarchy.Items[_cellIndex].Selected; }
        }

        public void RemoveFromSelection()
        {
            this._grid.ColumnsHierarchy.Items[_cellIndex].Selected = false;
        }

        public void Select()
        {
            this._grid.ColumnsHierarchy.Items[_cellIndex].Selected = true;
        }

        public IRawElementProviderSimple SelectionContainer
        {
            get { return this.parent.FragmentRoot; }
        }
        #endregion
    }
}

Now look at the pic below, UI Spy recognize our custom grid as grid and its rows and cells are listed.

Capture

Posted in UI Automation | Tagged: , , , , | Leave a Comment »