File: System\Windows\Automation\Peers\GridViewAutomationPeer.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Specialized;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Media;
 
using MS.Internal;
using MS.Internal.Automation;
 
namespace System.Windows.Automation.Peers
{
    /// <summary>
    /// GridView automation peer
    /// </summary>
    /// <remarks>
    /// Basically, the idea is to add a virtual method called CreateAutomationPeer on ViewBase
    /// Any view can override this method to create its own automation peer.
    /// ListView will use this method to get an automation peer for a given view and default to
    /// the properties/methods/patterns implemented by the view before going to default fall-backs on it
    /// These view automation peer must implement IViewAutomationPeer interface
    /// </remarks>
    public class GridViewAutomationPeer : IViewAutomationPeer, ITableProvider
    {
        ///
        public GridViewAutomationPeer(GridView owner, ListView listview)
            : base()
        {
            Invariant.Assert(owner != null);
            Invariant.Assert(listview != null);
            _owner = owner;
            _listview = listview;
 
            //Remember the items/columns count when GVAP is created, this is used for firing RowCount/ColumnCount changed event
            _oldItemsCount = _listview.Items.Count;
            _oldColumnsCount = _owner.Columns.Count;
 
            ((INotifyCollectionChanged)_owner.Columns).CollectionChanged += new NotifyCollectionChangedEventHandler(OnColumnCollectionChanged);
        }
 
        ///
        AutomationControlType IViewAutomationPeer.GetAutomationControlType()
        {
            return AutomationControlType.DataGrid;
        }
 
        ///
        object IViewAutomationPeer.GetPattern(PatternInterface patternInterface)
        {
            object ret = null;
            switch (patternInterface)
            {
                case PatternInterface.Grid:
                case PatternInterface.Table:
                    ret = this;
                    break;
            }
 
            return ret;
        }
 
        ///
        List<AutomationPeer> IViewAutomationPeer.GetChildren(List<AutomationPeer> children)
        {
            //Add GridViewHeaderRowPresenter as the first child of ListView
            if (_owner.HeaderRowPresenter != null)
            {
                AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(_owner.HeaderRowPresenter);
                if (peer != null)
                {
                    //If children is null, we still need to create an empty list to insert HeaderRowPresenter
                    if (children == null)
                    {
                        children = new List<AutomationPeer>();
                    }
 
                    children.Insert(0, peer);
                }
            }
 
            return children;
        }
 
        ItemAutomationPeer IViewAutomationPeer.CreateItemAutomationPeer(object item)
        {
            ListViewAutomationPeer lvAP = UIElementAutomationPeer.FromElement(_listview) as ListViewAutomationPeer;
            return new GridViewItemAutomationPeer(item, lvAP);
        }
 
        ///
        void IViewAutomationPeer.ItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            ListViewAutomationPeer peer = UIElementAutomationPeer.FromElement(_listview) as ListViewAutomationPeer;
            if (peer != null)
            {
                if (_oldItemsCount != _listview.Items.Count)
                {
                    peer.RaisePropertyChangedEvent(GridPatternIdentifiers.RowCountProperty, _oldItemsCount, _listview.Items.Count);
                }
                _oldItemsCount = _listview.Items.Count;
            }
        }
 
        //Called when the view is detached from the listview
        // Note: see bug 1555137 for details.
        // Never inline, as we don't want to unnecessarily link the
        // automation DLL via the ITableProvider, IGridProvider interface type initialization.
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        void IViewAutomationPeer.ViewDetached()
        {
            ((INotifyCollectionChanged)_owner.Columns).CollectionChanged -= new NotifyCollectionChangedEventHandler(OnColumnCollectionChanged);
        }
 
        #region ITableProvider
 
        /// <summary>
        /// Indicates if the data is best presented by row or column
        /// </summary>
        RowOrColumnMajor ITableProvider.RowOrColumnMajor
        {
            get { return RowOrColumnMajor.RowMajor; }
        }
 
        /// <summary>
        /// Collection of column headers
        /// </summary>
        IRawElementProviderSimple[] ITableProvider.GetColumnHeaders()
        {
            if (_owner.HeaderRowPresenter != null)
            {
                List<IRawElementProviderSimple> array = new List<IRawElementProviderSimple>(_owner.HeaderRowPresenter.ActualColumnHeaders.Count);
                ListViewAutomationPeer lvpeer = UIElementAutomationPeer.FromElement(_listview) as ListViewAutomationPeer;
 
                if(lvpeer != null)
                {
                    foreach (UIElement e in _owner.HeaderRowPresenter.ActualColumnHeaders)
                    {
                        AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(e);
                        if (peer != null)
                        {
                            array.Add(ElementProxy.StaticWrap(peer, lvpeer));
                        }
                    }
                }
                return array.ToArray();
            }
 
            return new IRawElementProviderSimple[0] ;
        }
 
        /// <summary>
        /// Collection of row headers
        /// </summary>
        IRawElementProviderSimple[] ITableProvider.GetRowHeaders()
        {
            //If there are no row headers, return an empty array
            return Array.Empty<IRawElementProviderSimple>();
        }
 
        #endregion
 
        #region IGridProvider
 
        /// <summary>
        /// number of columns in the grid
        /// </summary>
        int IGridProvider.ColumnCount
        {
            get
            {
                if (_owner.HeaderRowPresenter != null)
                {
                    return _owner.HeaderRowPresenter.ActualColumnHeaders.Count;
                }
                return _owner.Columns.Count;
            }
        }
 
        /// <summary>
        /// number of rows in the grid
        /// </summary>
        int IGridProvider.RowCount
        {
            get { return _listview.Items.Count; }
        }
 
        /// <summary>
        /// Obtain the IRawElementProviderSimple at an absolute position
        /// </summary>
        IRawElementProviderSimple IGridProvider.GetItem(int row, int column)
        {
            ArgumentOutOfRangeException.ThrowIfNegative(row);
            ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(row, ((IGridProvider)this).RowCount);
            ArgumentOutOfRangeException.ThrowIfNegative(column);
            ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(column, ((IGridProvider)this).ColumnCount);
 
            ListViewItem lvi = _listview.ItemContainerGenerator.ContainerFromIndex(row) as ListViewItem;
            //If item is virtualized, try to de-virtualize it
            if (lvi == null)
            {
                VirtualizingPanel itemsHost = _listview.ItemsHost as VirtualizingPanel;
                if (itemsHost != null)
                {
                    itemsHost.BringIndexIntoView(row);
                }
 
                lvi = _listview.ItemContainerGenerator.ContainerFromIndex(row) as ListViewItem;
 
                if (lvi != null)
                {
                    //Must call Invoke here to force run the render process
                    _listview.Dispatcher.Invoke(
                        System.Windows.Threading.DispatcherPriority.Loaded,
                        (System.Windows.Threading.DispatcherOperationCallback)delegate(object arg)
                        {
                            return null;
                        },
                        null);
                }
            }
 
            //lvi is null, it is virtualized, so we can't return its cell
            if (lvi != null)
            {
                AutomationPeer lvpeer = UIElementAutomationPeer.FromElement(_listview);
                if(lvpeer != null)
                {
                    AutomationPeer peer = UIElementAutomationPeer.FromElement(lvi);
                    if (peer != null)
                    {
                        // use the GridViewItemAutomationPeer, if available
                        AutomationPeer eventSource = peer.EventsSource;
                        if (eventSource != null)
                        {
                            peer = eventSource;
                        }
 
                        List<AutomationPeer> columns = peer.GetChildren();
                        if (columns.Count > column)
                        {
                            return ElementProxy.StaticWrap(columns[column], lvpeer);
                        }
                    }
                }
            }
 
            return null;
        }
 
 
        /// <summary>
        /// Fire ColumnCount changed event for GridPattern
        /// </summary>
        private void OnColumnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (_oldColumnsCount != _owner.Columns.Count)
            {
                ListViewAutomationPeer peer = UIElementAutomationPeer.FromElement(_listview) as ListViewAutomationPeer;
                Invariant.Assert(peer != null);
                if (peer != null)
                {
                    peer.RaisePropertyChangedEvent(GridPatternIdentifiers.ColumnCountProperty, _oldColumnsCount, _owner.Columns.Count);
                }
            }
 
            _oldColumnsCount = _owner.Columns.Count;
 
            AutomationPeer lvPeer = UIElementAutomationPeer.FromElement(_listview);
            if (lvPeer != null)
            {
                List<AutomationPeer> list = lvPeer.GetChildren();
                if (list != null)
                {
                    foreach (AutomationPeer peer in list)
                    {
                        peer.InvalidatePeer();
                    }
                }
            }
        }
 
        #endregion
 
        #region Helper
 
        internal static Visual FindVisualByType(Visual parent, Type type)
        {
            if (parent != null)
            {
                int count = parent.InternalVisualChildrenCount;
                for (int i = 0; i < count; i++)
                {
                    Visual visual = parent.InternalGetVisualChild(i);
                    if (!type.IsInstanceOfType(visual))
                    {
                        visual = FindVisualByType(visual, type);
                    }
 
                    if (visual != null)
                    {
                        return visual;
                    }
                }
            }
 
            return null;
        }
 
        #endregion
 
        #region Private Fields
 
        private GridView _owner;
 
        private ListView _listview;
        //Store the old items/columns count, this is used for firing RowCount changed event in IGridProvider
        private int _oldItemsCount = 0;
        private int _oldColumnsCount = 0;
 
        #endregion
    }
}