WindowsListView.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / WindowsListView.cs / 1305600 / WindowsListView.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: Win32 ListView proxy 
//
// History: 
//        Jean-Francois Peyroux, alexsn - Created (in DotNet)
//        2003/08/08 - alexsn Updated for WCP
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections; 
using System.Text;
using System.Runtime.InteropServices; 
using System.ComponentModel;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows; 
using MS.Win32;
 
namespace MS.Internal.AutomationProxies 
{
    // 
    // ListView Logical Tree Diagram
    //
    //  ListView
    //    --Group 1 (if in "Group" mode, if not LVI gets promoted up) 
    //      -----ListViewItem0...ListViewItemN
    //           ---------Cell (if in details mode) 
    //    --Group 2(if in "Group" mode, if not LVI gets promoted up) 
    //      -----ListViewItem0...ListViewItemN
    //           ---------Cell (if in details mode) 
    //    --Header (If in detail mode)
    //      ----Header Item0...X (If in detail mode)
    //    --ScrollBar
    //       -----SmallDecrement 
    //       -----LargeDecrement
    //       -----Thumb 
    //       -----LargeIncrement 
    //       -----SmallIncrement
    // 
    //  NOTE: Hwnd-based tree  will look like this
    //
    //              ListView
    //                 | 
    //                 |
    //                SysHeader32 (if available) 
    // 
    //   UIAutomation will discover a header for us and will hook it up
    //   to our navigation chain, nothing needs to be done on our side, it is just a magic 
    //   Please do not add ANY SysHeader32 specific navigation code
    class WindowsListView: ProxyHwnd, ISelectionProvider, IScrollProvider, IGridProvider, IMultipleViewProvider, ITableProvider
    {
 
        // -----------------------------------------------------
        // 
        //  Constructors 
        //
        //----------------------------------------------------- 

        #region Constructors

        static WindowsListView () 
        {
            _groupEvents = new WinEventTracker.EvtIdProperty [3]; 
            _groupEvents [0]._evtId = NativeMethods.EventObjectReorder; 
            _groupEvents [1]._evtId = NativeMethods.EventObjectHide;
            _groupEvents [2]._evtId = NativeMethods.EventObjectDestroy; 
            _groupEvents [0]._idProp = _groupEvents [1]._idProp = _groupEvents [2]._idProp = 0;
        }

        internal WindowsListView (IntPtr hwnd, ProxyFragment parent, int item) 
            : base( hwnd, parent, item )
        { 
            // Set the strings to return properly the properties. 
            if (IsDetailMode(hwnd))
            { 
                _cControlType = ControlType.DataGrid;
            }
            else
            { 
                _cControlType = ControlType.List;
            } 
 
            // Can be focused
            _fIsKeyboardFocusable = true; 

            // support for events
            _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents);
 
            // internally track some of the lv events
            WinEventTracker.AddToNotificationList (_hwnd, new WinEventTracker.ProxyRaiseEvents (WindowsListView.GroupSpecificEvents), _groupEvents, 3); 
        } 

        #endregion Constructors 

        #region Proxy Create

        // Static Create method called by UIAutomation to create this proxy. 
        // null if unsuccessful
        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild, int idObject) 
        { 
            return Create(hwnd, idChild);
        } 

        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild)
        {
            WindowsListView lv = new WindowsListView(hwnd, null, 0); 

            if( idChild == 0 ) 
                return lv; 
            else
                return lv.CreateListViewItemCheckIfInGroup (idChild - 1); 
        }

        internal static void RaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        { 
            switch (idObject)
            { 
                case NativeMethods.OBJID_CLIENT : 
                {
                    RaiseEventsOnClient(hwnd, eventId, idProp, idObject, idChild); 
                    return;
                }

                case NativeMethods.OBJID_WINDOW : 
                {
                    // Special case for logical element change for a list view item 
                    if ((eventId == NativeMethods.EventObjectReorder) && (idProp as AutomationEvent) == AutomationElement.StructureChangedEvent) 
                    {
                        WindowsListView wlv = new WindowsListView( hwnd, null, -1 ); 

                        AutomationInteropProvider.RaiseStructureChangedEvent( wlv, new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, wlv.MakeRuntimeId() ) );
                        return;
                    } 

                    break; 
                } 

                case NativeMethods.OBJID_VSCROLL: 
                case NativeMethods.OBJID_HSCROLL:
                    // The NonClientArea proxy handles these events
                    break;
 
                default :
                { 
                    ProxySimple el = new WindowsListView( hwnd, null, -1 ); 
                    if (el != null)
                    { 
                        el.DispatchEvents( eventId, idProp, idObject, idChild );
                    }
                    break;
                } 
            }
 
        } 

        #endregion 

        //------------------------------------------------------
        //
        //  Patterns Implementation 
        //
        //----------------------------------------------------- 
 
        #region ProxySimple Interface
 
        internal override object GetPatternProvider (AutomationPattern iid)
        {
            // selection is always supported
            if (iid == SelectionPattern.Pattern) 
            {
                return this; 
            } 

            // Note that condition for grid should be true when condition for table is also true: 
            // providers that implement table should impl grid at the same time.
            if (iid == GridPattern.Pattern &&
                    (IsDetailMode (_hwnd) ||
                     IsImplementingGrid (_hwnd) && GetItemCount (_hwnd) > 0)) 
            {
                return this; 
            } 

            if (iid == MultipleViewPattern.Pattern) 
            {
                return this;
            }
 
            // table is supported only in the detail mode
            if (iid == TablePattern.Pattern && IsDetailMode (_hwnd)) 
            { 
                return this;
            } 

            return null;
        }
 
        #endregion ProxySimple Interface
 
        #region ProxyFragment Interface 

        internal override ProxySimple GetNextSibling (ProxySimple child) 
        {
            bool hasGroup = IsGroupViewEnabled (_hwnd);
            int item = child._item;
            int count = -1; 

            if (!hasGroup) 
            { 
                // Determine how many items are in the list view.
                count = GetItemCount (_hwnd); 

                // Next for an item that does not exist in the list
                if (item >= count)
                { 
                    throw new ElementNotAvailableException ();
                } 
 
                // Check if index of the next item is in range
                if (item >= 0 && (item + 1) < count) 
                {
                    // return a node to represent the requested item.
                    return CreateListViewItem (item + 1);
                } 
            }
 
            if (hasGroup) 
            {
                if (child is ListViewItem) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed));
                }
 
                WindowsListViewGroup windowsListViewGroup = child as WindowsListViewGroup;
                if (windowsListViewGroup != null) 
                { 
                    // The group might have destroyed by an event between a first an now
                    _groupsCollection.EnsureCreation (_hwnd); 

                    GroupManager manager = _groupsCollection[_hwnd];
                    int [] groupIds = manager.GetGroupIds ();
                    int groupsCount = groupIds.Length; 

                    // if there are no groups this is an empty list 
                    if (groupIds.Length == 0) 
                    {
                        return null; 
                    }

                    // Navigation: from one group to another
                    int groupID = windowsListViewGroup.ID; 
                    int location = Array.IndexOf (groupIds, groupID);
                    if (location == -1) 
                        return null; // the ListView is updating so this group ID is no longer valid? 

                    if (location + 1 < groupsCount) 
                    {
                        return CreateListViewGroup (groupIds [location + 1]);
                    }
                } 
            }
 
            return null; 
        }
 
        internal override ProxySimple GetPreviousSibling (ProxySimple child)
        {
            // go to lv group if applicable
            // When to go to the Group: 
            // A. If navigating from the scrollbar
            // B. When navigating from the another Group 
            if (IsGroupViewEnabled (_hwnd)) 
            {
                // The group might have destroyed by an event between a first an now 
                _groupsCollection.EnsureCreation (_hwnd);

                // note: navigation is done from last group to the first
                GroupManager manager = _groupsCollection [_hwnd]; 
                int [] groupIds = manager.GetGroupIds ();
                int groupsCount = groupIds.Length; 
 
                // if there are no groups this is an empty list
                if (groupIds.Length == 0) 
                {
                    return null;
                }
 
                // check if current child is a group
                WindowsListViewGroup windowsListViewGroup = child as WindowsListViewGroup; 
                if (windowsListViewGroup != null) 
                {
                    int groupID = windowsListViewGroup.ID; 
                    int location = Array.IndexOf (groupIds, groupID);
                    if (location == -1)
                        return null; // the ListView is updating so this group ID is no longer valid?
 
                    if (location > 0)
                    { 
                        return CreateListViewGroup(groupIds [location - 1]); 
                    }
                } 
                else
                {
                    // return last group
                    return CreateListViewGroup(groupIds [groupsCount - 1]); 
                }
            } 
                // in the case when lv in the Group mode 
                // the lvitems will live under the corresponding group
                //if (!hasGroup) 
                else
            {
                // LVItem can be called by:
                // A. Scrollbar 
                // B. Another listviewitem
                int item = child._item; 
                int count = GetItemCount (_hwnd); 
                if (item > 0 && item < count)
                { 
                    // navigation from one lvitem to another
                    return CreateListViewItem (item - 1);
                }
 
                // we may need to retrun either last lv item
                // or give control back to UIAutomation (e.g. From 1st lvitem to header) 
                return item < 0 && count > 0 ? CreateListViewItem (count - 1) : null; 
            }
 
            return null;
        }

        internal override ProxySimple GetFirstChild () 
        {
            // if LV is in Group mode, lvitems will live under the corresponding group 
            bool hasGroup = IsGroupViewEnabled (_hwnd); 
            int itemCount = GetItemCount(_hwnd);
 
            if (itemCount > 0)
            {
                if (!hasGroup)
                { 
                    return CreateListViewItem(0);
                } 
 
                // Navigate to the first group
                if (hasGroup) 
                {
                    _groupsCollection.EnsureCreation(_hwnd);

                    GroupManager manager = _groupsCollection[_hwnd]; 
                    int[] groupIds = manager.GetGroupIds();
 
                    // if there are no groups this is an empty list 
                    if (groupIds.Length == 0)
                    { 
                        return null;
                    }

                    // return the first group 
                    return CreateListViewGroup(groupIds[0]);
                } 
            } 

            // no content 
            return null;
        }

        internal override ProxySimple GetLastChild () 
        {
            bool hasGroup = IsGroupViewEnabled (_hwnd); 
 
            // check for the group
            if (hasGroup) 
            {
                // group view is enabled and we got here
                // now return the last group
                _groupsCollection.EnsureCreation (_hwnd); 

                GroupManager manager = _groupsCollection [_hwnd]; 
                int [] groupIds = manager.GetGroupIds (); 
                int groupsCount = groupIds.Length;
 
                // if there are no groups this is an empty list
                if (groupIds.Length == 0)
                {
                    return null; 
                }
 
                return CreateListViewGroup (groupIds[groupsCount - 1]); 
            }
                // if !group 
                else
            {
                int count = GetItemCount (_hwnd);
                return count > 0 ? CreateListViewItem (count - 1) : null; 
            }
        } 
 
        internal override ProxySimple ElementProviderFromPoint (int x, int y)
        { 
            // The header is a window on top of the client area.
            // If the point is the non client area, skip
            // Must return null if hit tested.
            if (PtInListViewHeader (x, y) || !PtInClientRect (_hwnd, x, y)) 
            {
                return null; 
            } 

            // in the case of the lv in the group mode, return a group at point(x,y) 
            if (IsGroupViewEnabled (_hwnd))
            {
                _groupsCollection.EnsureCreation (_hwnd);
 
                // Let's locate the group that contains x,y
                GroupManager manager = WindowsListView._groupsCollection [_hwnd]; 
                int length = manager.GroupCount (); 
                for (int i = 0; i < length; i++)
                { 
                    NativeMethods.Win32Rect rc = manager.GetGroupRcByIndex (i);
                    if (Misc.PtInRect(ref rc, x, y))
                    {
                        // found a group where point belongs 
                        int groupID = manager.GetGroupIdByIndex (i);
                        ProxyFragment group = new WindowsListViewGroup (_hwnd, this, groupID); 
                        return ProxyFragment.DrillDownFragment (group, x, y); 
                    }
                } 
            }

            NativeMethods.LVHITTESTINFO_INTERNAL hitTest = WindowsListView.SubitemHitTest(_hwnd, new NativeMethods.Win32Point(x, y));
            if (hitTest.iItem >= 0) 
            {
                // create the item 
                ProxyFragment item = CreateListViewItemOrStartMenuItem(this, hitTest.iItem); 
                return ProxyFragment.DrillDownFragment (item, x, y);
            } 
            else if (hitTest.flags == NativeMethods.LVHT_NOWHERE && IsDetailMode(_hwnd))
            {
                // LVHT_NOWHERE means: The position is inside the list-view control's client window, but it is not over a list item.
                // The hit was in the non-client area of the ListView box, so adjust piont to get the point on a 
                // sub-item to find which ListViewItem to create.  There is not adjustment needed in the y-coordinate
                // spaces since ListView's do not apply a non-client area to the top or the bottom of items. 
                Rect boundingRectangle = BoundingRectangle; 
                int xAdjustment = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXBORDER) + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXFRAME);
 
                if (x - boundingRectangle.Left < xAdjustment)
                {
                    x += xAdjustment;
                } 
                else if (boundingRectangle.Left + boundingRectangle.Width - x < xAdjustment)
                { 
                    x -= xAdjustment; 
                }
 
                hitTest = WindowsListView.SubitemHitTest(_hwnd, new NativeMethods.Win32Point(x, y));
                if (hitTest.iItem >= 0)
                {
                    // Since this original point was in the non-client area of the listview, the item being looked for 
                    // is the item not one of it's subitems, so just return the ListViewItem.
                    return CreateListViewItemOrStartMenuItem(this, hitTest.iItem); 
                } 
            }
 
            // NOTE: Do not return this, since user might of specified point that belongs to the hwnd-control
            // that lives inside of the lv (e.g. header). If we return null UIAutomation will do the drilling
            return null;
        } 

        // Returns an item corresponding to the focused element (if there is one), or null otherwise. 
        internal override ProxySimple GetFocus () 
        {
            if (IsGroupViewEnabled (_hwnd)) 
            {
                _groupsCollection.EnsureCreation (_hwnd);
                return WindowsListViewGroup.GetFocusInGroup (_hwnd, this);
            } 

            // get the focused item 
            int index = GetItemNext(_hwnd, -1, NativeMethods.LVNI_FOCUSED); 

            if (index != -1) 
            {
                return CreateListViewItemCheckIfInGroup (index);
            }
 
            return this;
        } 
 
        #endregion
 
        #region ProxyHwnd Interface

        internal override void AdviseEventAdded (AutomationEvent eventId, AutomationProperty [] aidProps)
        { 
            if (aidProps != null)
            { 
                for (int i = 0, c = aidProps.Length; i < c; i++) 
                {
                    if (aidProps [i] == TablePattern.ColumnHeadersProperty) 
                    {
                        // Return array of the HeaderItems
                        IntPtr hwndHeader = ListViewGetHeader (_hwnd);
                        if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader)) 
                        {
                            WindowsSysHeader header = (WindowsSysHeader) WindowsSysHeader.Create (hwndHeader, 0); 
                            WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectCreate, TablePattern.ColumnHeadersProperty) }; 
                            WinEventTracker.AddToNotificationList(hwndHeader, header._createOnEvent, aEvents, 1);
                        } 
                    }
                }
            }
 
            if (eventId == InvokePattern.InvokedEvent)
            { 
                WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectSelection, eventId) }; 
                WinEventTracker.AddToNotificationList(_hwnd, _createOnEvent, aEvents, 1);
            } 

            base.AdviseEventAdded (eventId, aidProps);
        }
 
        internal override void AdviseEventRemoved (AutomationEvent eventId, AutomationProperty [] aidProps)
        { 
            if (aidProps != null) 
            {
                for (int i = 0, c = aidProps.Length; i < c; i++) 
                {
                    if (aidProps [i] == TablePattern.ColumnHeadersProperty)
                    {
                        // Return array of the HeaderItems 
                        IntPtr hwndHeader = ListViewGetHeader (_hwnd);
                        if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader)) 
                        { 
                            WindowsSysHeader header = (WindowsSysHeader) WindowsSysHeader.Create (hwndHeader, 0);
                            WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectCreate, TablePattern.ColumnHeadersProperty) }; 
                            WinEventTracker.RemoveToNotificationList (hwndHeader, aEvents, header._createOnEvent, 1);
                        }
                    }
                } 
            }
 
            if (eventId == InvokePattern.InvokedEvent) 
            {
                WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectSelection, eventId) }; 
                WinEventTracker.AddToNotificationList(_hwnd, _createOnEvent, aEvents, 1);
            }

            base.AdviseEventRemoved(eventId, aidProps); 
        }
 
        #endregion 

        #region SelectionPattern 

        // Returns an array of elements that are current selection.
        IRawElementProviderSimple[] ISelectionProvider.GetSelection()
        { 
            int count = GetItemCount (_hwnd);
            int countSelection = MultiSelected(_hwnd) ? GetSelectedItemCount(_hwnd) : 1; 
 
            if (count <= 0 || countSelection <= 0 )
            { 
                // this should be handled correctly in the framework
                return null;
            }
 
            IRawElementProviderSimple[] selection = new IRawElementProviderSimple[countSelection];
            int index = 0; 
 
            for (int itemPos = GetItemNext(_hwnd, -1, NativeMethods.LVNI_SELECTED); itemPos != -1; itemPos = GetItemNext(_hwnd, itemPos, NativeMethods.LVNI_SELECTED))
            { 
                selection[index] = CreateListViewItemCheckIfInGroup(itemPos);
                index++;
            }
 
            if (index == 0)
            { 
                return null; 
            }
 
            return selection;
        }

        // Returns whether the control supports multiple selection. 
        bool ISelectionProvider.CanSelectMultiple
        { 
            get 
            {
                // Get the style bits for the list view window. 
                return MultiSelected (_hwnd);
            }
        }
 
        // Returns whether the control requires a minimum of one selected element at all times.
        bool ISelectionProvider.IsSelectionRequired 
        { 
            get
            { 
                return false;
            }
        }
 

        #endregion SelectionPattern 
 
        #region ScrollPattern
 
        void IScrollProvider.Scroll (ScrollAmount horizontalAmount, ScrollAmount verticalAmount)
        {
            // Make sure that the control is enabled
            if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) 
            {
                throw new ElementNotEnabledException(); 
            } 

            WindowScroll.Scroll (_hwnd, horizontalAmount, verticalAmount, true); 
        }

        void IScrollProvider.SetScrollPercent (double horizontalPercent, double verticalPercent)
        { 
            // Make sure that the control is enabled
            if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) 
            { 
                throw new ElementNotEnabledException();
            } 

            // get the "full size" of the list-view
            int size = ApproximateViewRect (_hwnd);
 
            // ApproximateViewRect adds the window edge size, substract it
            int cx = NativeMethods.Util.LOWORD (size) /*- 2 * UnsafeNativeMethods.GetSystemMetrics (NativeMethods.SM_CXBORDER)*/; 
            int cy = NativeMethods.Util.HIWORD (size) /*- 2 * UnsafeNativeMethods.GetSystemMetrics (NativeMethods.SM_CYBORDER)*/; 

            // maximum width to fit all elements 
            int dx, dy;
            bool fHz = SetScrollPercent (horizontalPercent, NativeMethods.SB_HORZ, cx, out dx);
            bool fVt = SetScrollPercent (verticalPercent, NativeMethods.SB_VERT, cy, out dy);
 
            if (fHz || fVt)
            { 
                // scroll relative to the current thumb position 
                bool fScrollSuccess = true;
 
                // if there is no movement need do not call Scroll().
                if (dx != 0 || dy != 0)
                {
                    fScrollSuccess = Scroll(_hwnd, (IntPtr)dx, (IntPtr)dy); 

                    // On occasion in the listview control the new position of the scroll bar is off by 
                    // one column/row. To deal with that bug in listview, we query the value we just set. 
                    // If it differs then we try a second time to scroll the content. It is a scroll by
                    // just one column and this always succeeds. 
                    // It is done both on hz and vt as a safety measure.
                    if (fScrollSuccess && (((int)horizontalPercent != (int)ScrollPattern.NoScroll && (int)horizontalPercent != (int)WindowScroll.GetPropertyScroll(ScrollPattern.HorizontalScrollPercentProperty, _hwnd))
                    || ((int)verticalPercent != (int)ScrollPattern.NoScroll && (int)verticalPercent != (int)WindowScroll.GetPropertyScroll(ScrollPattern.VerticalScrollPercentProperty, _hwnd))))
                    { 
                        SetScrollPercent(horizontalPercent, NativeMethods.SB_HORZ, cx, out dx);
                        SetScrollPercent(verticalPercent, NativeMethods.SB_VERT, cy, out dy); 
 
                        // if there is no movement need do not call Scroll() again.
                        if (dx != 0 || dy != 0) 
                        {
                            Scroll(_hwnd, (IntPtr)dx, (IntPtr)dy);
                        }
                    } 
                }
 
                if (fHz && fVt && fScrollSuccess) 
                {
                    return; 
                }
            }

            throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); 
        }
 
        // Calc the position of the horizontal scroll bar thumb in the 0..100 % range 
        double IScrollProvider.HorizontalScrollPercent
        { 
            get
            {
                // use the common implementation for all the windows based controls
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.HorizontalScrollPercentProperty, _hwnd); 
            }
        } 
 
        // Calc the position of the Vertical scroll bar thumb in the 0..100 % range
        double IScrollProvider.VerticalScrollPercent 
        {
            get
            {
                // use the common implementation for all the windows based controls 
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.VerticalScrollPercentProperty, _hwnd);
            } 
        } 

        // Percentage of the window that is visible along the horizontal axis. 
        double IScrollProvider.HorizontalViewSize
        {
            get
            { 
                // use the common implementation for all the windows based controls
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.HorizontalViewSizeProperty, _hwnd); 
            } 
        }
 
        // Percentage of the window that is visible along the vertical axis.
        double IScrollProvider.VerticalViewSize
        {
            get 
            {
                // use the common implementation for all the windows based controls 
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.VerticalViewSizeProperty, _hwnd); 
            }
        } 

        // Can the element be horizontaly scrolled
        bool IScrollProvider.HorizontallyScrollable
        { 
            get
            { 
                // use the common implementation for all the windows based controls 
                return (bool) WindowScroll.GetPropertyScroll (ScrollPattern.HorizontallyScrollableProperty, _hwnd);
            } 
        }

        // Can the element be verticaly scrolled
        bool IScrollProvider.VerticallyScrollable 
        {
            get 
            { 
                return (bool) WindowScroll.GetPropertyScroll (ScrollPattern.VerticallyScrollableProperty, _hwnd);
            } 
        }

        #endregion ScrollPattern
 
        #region Grid Pattern
 
        // Obtain the AutomationElement at an zero based absolute position in the grid. 
        // Where 0,0 is top left
        IRawElementProviderSimple IGridProvider.GetItem(int row, int column) 
        {
            int maxRow = GetRowCount (_hwnd);
            int maxColumn = GetColumnCount (_hwnd);
 
            if (row < 0 || row >= maxRow)
            { 
                throw new ArgumentOutOfRangeException("row", row, SR.Get(SRID.GridRowOutOfRange)); 
            }
 
            if (column < 0 || column >= maxColumn)
            {
                throw new ArgumentOutOfRangeException("column", column, SR.Get(SRID.GridColumnOutOfRange));
            } 

            // GetCell 
            if (IsDetailMode (_hwnd)) 
            {
                return GetCellInDetailMode (row, column); 
            }

            return GetCellInOtherModes (row, column, maxColumn, maxRow);
        } 

        int IGridProvider.RowCount 
        { 
            get
            { 
                return GetRowCount (_hwnd);
            }
        }
 
        int IGridProvider.ColumnCount
        { 
            get 
            {
                return GetColumnCount (_hwnd); 
            }
        }

        #endregion #region Grid Pattern 

        #region Table Pattern 
 
        // Collection of all Row Headers associated with the Table. Order is consistent with the table
        IRawElementProviderSimple [] ITableProvider.GetRowHeaders () 
        {
            return null;
        }
 
        // Collection of all Column Headers associated with the Table. Order is consistent with the table
        IRawElementProviderSimple [] ITableProvider.GetColumnHeaders () 
        { 
            // Return array of the HeaderItems
            IntPtr hwndHeader = ListViewGetHeader (_hwnd); 
            if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader))
            {
                WindowsSysHeader header = (WindowsSysHeader) WindowsSysHeader.Create (hwndHeader, 0);
                int size = HeaderItemCount (hwndHeader); 
                if (size > 0)
                { 
                    IRawElementProviderSimple [] columns = new IRawElementProviderSimple [size]; 
                    for (ProxySimple headerItem = header.GetFirstChild (); headerItem != null; headerItem = header.GetNextSibling (headerItem))
                    { 
                        columns [headerItem._item] = headerItem;
                    }
                    return columns;
                } 
            }
            return null; 
        } 

        // Describe the best way to present the information within this table. 
        RowOrColumnMajor ITableProvider.RowOrColumnMajor
        {
            get
            { 
                return RowOrColumnMajor.RowMajor;
            } 
        } 

 
        #endregion Table Pattern

        #region MultipleViewPattern
 
        //
        string IMultipleViewProvider.GetViewName (int viewID) 
        { 
            if ( viewID < 0 || viewID > ListViewViews.Length )
            { 
                throw new ArgumentException( SR.Get( SRID.InvalidParameter ) );
            }
            return ListViewViews [viewID];
        } 

        void IMultipleViewProvider.SetCurrentView (int viewID) 
        { 
            if ( viewID < 0 || viewID > ListViewViews.Length )
            { 
                throw new ArgumentException( SR.Get( SRID.InvalidParameter ) );
            }
            // alexsn
 

 
            // If requested view is in array, than do a Set 
            // {
            //     return ListViewSetView(_hwnd, viewID); 
            // }

            // currently do nothing
        } 

        int [] IMultipleViewProvider.GetSupportedViews () 
        { 
            // alexsn
 


            return new int [] { ListViewGetView (_hwnd) };
        } 

        int IMultipleViewProvider.CurrentView 
        { 
            get
            { 
                return ListViewGetView (_hwnd);
            }
        }
 

        #endregion MultipleViewPattern 
 
        //------------------------------------------------------
        // 
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        // set focus to the specified item 
        static internal bool SetItemFocused (IntPtr hwnd, int item)
        { 
            return SetItemState(hwnd, item, NativeMethods.LVIS_FOCUSED, NativeMethods.LVIS_FOCUSED);
        }

        // set focus to the specified item 
        static internal bool IsItemFocused (IntPtr hwnd, int item)
        { 
            int state = GetItemState(hwnd, item, NativeMethods.LVIS_FOCUSED); 

            return (Misc.IsBitSet(state, NativeMethods.LVIS_FOCUSED)); 
        }

        // detect if listview is in detail mode
        static internal bool IsDetailMode (IntPtr hwnd) 
        {
            int view = ListViewGetView (hwnd); 
 
            // Current LH builds (4059) display the header even in tile view,
            // which makes us think we are in details view. 
            // Explicitly detect tile view and return false.
            if (view == NativeMethods.LV_VIEW_TILE)
            {
                return false; 
            }
 
            if (InReportView(hwnd) || (view == NativeMethods.LV_VIEW_DETAILS)) 
            {
                return true; 
            }

            if (Environment.OSVersion.Version.Major < 6)
            { 
                // handle lv that not LVS_REPORT but act like one
                // e.g. Window explorer 
                // NOTE: in XP version the NativeMethods.LV_VIEW_DETAILS check will cover Windows 
                // Explorer, but in order to work for before XP lv we need the code below.
                IntPtr hwndHeader = ListViewGetHeader(hwnd); 

                return SafeNativeMethods.IsWindowVisible(hwndHeader);
            }
            else 
            {
                // No need to examine the listview header on Vista, since 
                // ListViewGetView() suffices. 
                return false;
            } 

         }

        // detect if listview is in list mode 
        static internal bool IsListMode (IntPtr hwnd)
        { 
            if (ListViewList(hwnd) || (NativeMethods.LV_VIEW_LIST == ListViewGetView(hwnd))) 
            {
                return true; 
            }

            return false;
        } 

        // detect if given listview should support Grid pattern 
        static internal bool IsImplementingGrid (IntPtr hwnd) 
        {
            // in the case when Group is enabled Group will support 
            // Grid pattern rather than ListView
            if (IsGroupViewEnabled (hwnd))
            {
                return false; 
            }
 
            // Rules for supporting grid pattern: 
            // 1. ListView is in detail mode
            // 2. ListView is in the list mode 
            // 3. Any other modes and LVS_AUTOARRANGE is set
            if (IsDetailMode (hwnd) || IsListMode (hwnd) || ListViewAutoArrange (hwnd))
            {
                return true; 
            }
 
            return false; 
        }
 
        // retrieve count of columns in the listview
        static internal int GetColumnCount (IntPtr hwnd)
        {
            if (IsDetailMode (hwnd)) 
            {
                int column = ListViewItem.GetSubItemCount (hwnd); 
 
                return (column <= -1) ? 0 : column;
            } 

            return GetColumnCountOtherModes (hwnd);
        }
 
        // retrieve count of rows in the listview
        static internal int GetRowCount (IntPtr hwnd) 
        { 
            if (IsDetailMode (hwnd))
            { 
                int row = GetItemCount (hwnd);

                return (row <= -1) ? 0 : row;
            } 

            return GetRowCountOtherModes (hwnd); 
        } 

        // get count of column in the non-detail lv 
        static internal int GetColumnCountOtherModes (IntPtr hwnd)
        {
            // Check for empty list
            if (GetItemCount(hwnd) <= 0) 
            {
                return 0; 
            } 

            // Algorithm for non-list mode of ListView with at least one item: 
            // Starting from the first item, count the items to the right of it.
            //
            int columnCount = 0;
            int curItem = 0; 
            while (true)
            { 
                columnCount++; 
                int nextItem = GetItemNext(hwnd, curItem, NativeMethods.LVNI_TORIGHT);
                // Expect -1 when no more items to right of current item 
                if (nextItem < 0)
                    break;
                // Guard against infinite loop (getting back the same item LH
                if (nextItem == curItem) 
                    break;
 
                // Assumption: As long as nextItem is changing everything is OK 
                // Note: Docs imply it may be possible for nextItem < curItem at this
                // point so don't assume nextItem is always increasing. 
                curItem = nextItem;
            }

            return columnCount; 
        }
 
        // get count of row for the listview when it is in the list mode 
        static internal int GetRowCountListMode (IntPtr hwnd, int itemCount)
        { 
            // NOTE: ListView in the List mode is tricky
            // In the List mode during the navigation columns getting
            // wrapped hence the number of rows we'll get by simply doing
            // LVNI_BELOW will be same as maximum number of elements 
            // Algorithm: get the item position  while doing GetItemNext(,,LVNI_BELOW)
            // as long as pt.x is the same we on the same column 
            // as soon as pt.x is changed we know we jump to the different column and hence we know the number of rows 
            // This is true except:
            // If user had Groups shown, and than changed to the List mode (List mode does not have groups) 
            // the List will not be snaking anymore (Windows Explorer LV bug on XP), hence after we come to the end of the first column
            // the GetItemNext(,,LVNI_BELOW) will return -1. all other case list will snake
            // Lucky for us at this point rowCount will contain the number of rows
            int columnCount = GetColumnCountOtherModes (hwnd); 

            if (columnCount == 1) 
            { 
                // list does not snake, number of elements == number or rows
                return itemCount; 
            }

            // We know that list has at least itemCount/columnCount rows
            int rowCount = (int) System.Math.Ceiling (((double) itemCount) / columnCount); 

            NativeMethods.Win32Point pt = new NativeMethods.Win32Point (0, 0); 
 
            // items are 0-based
            int current = rowCount - 1; 
            if (!GetItemPosition(hwnd, current, out pt))
            {
                return 0;
            } 

            int pos = pt.x; 
            while (true) 
            {
                int next = GetItemNext(hwnd, current, NativeMethods.LVNI_BELOW); 
                // Expect -1 when no more items below
                if (next == -1)
                    return rowCount;
 
                // Guard against infinite loop (LH
                if (next == current) 
                    return rowCount; 

                // Get this next item's top-left coordinate 
                if (!GetItemPosition(hwnd, next, out pt))
                    return rowCount;

                // If we're not on the same left-most x-axis we've got the row count 
                if (pos != pt.x)
                    return rowCount; 
 
                ++rowCount;
                current = next; 
            }
        }

        // detect if group view is enabled 
        internal static bool IsGroupViewEnabled (IntPtr hwnd)
        { 
            return (ListViewIsGroupViewEnabled(hwnd) && NativeMethods.LV_VIEW_LIST != ListViewGetView(hwnd)); 
        }
 
        // Events produced by the LV, that will help us working with the Group
        internal static void GroupSpecificEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        {
            // NOTE: Whenever LV goes from Group mode to non-Group mode we will be getting an event 
            //       Whenver LV goes from one Group mode to another (arrange By: Name - Size) we will be getting an event
            // BUT 
            //      Whenver LV switched between Views : Icond - Detail, events are not always getting send -> This one is ok since 
            //      it does not affect Grouping (e.g. Groups do not change), except when switching from any View (with Group on) to
            //                                   List mode (List mode does not have group) 
            //
            //      Whenver LV goes from non group mode to Group mode, events pretty much never getting generated
            //
            //      We will detect, in the code, when user got a stale logical tree and tries to use element from this tree. We will throw an exception 
            //      indicating invalid operation (e.g. InvalidOperationException("Operation cannot be performed");)
            //      If user does listens to the events, and still got the stale tree (due to the LV control not raising needed events) 
            //      the exception will be thrown in any case. The events will be raised on LH as soon as LV event-related bugs are fixed. On XP 
            //      user can catch the exception and do the needed thing, or use the verification method (should be done in M7) on LE to validate it.
            switch (eventId) 
            {
                case NativeMethods.EventObjectReorder :
                    {
                        // First check if we are in the Groupmode 
                        if (IsGroupViewEnabled (hwnd))
                        { 
                            // reorder event may  mean: 
                            // 1. What lv is grouped by changed.... (Need an update and an event to be sent)
                            // 2. View changed (e.g.icone -> tiles)     (No action needed group did no change) 
                            // 3. something as simple as resize     (No action needed)
                            // To detect case 1 we need to make sure that our ids are still valid
                            if (_groupsCollection.Contains (hwnd))
                            { 
                                GroupManager manager = _groupsCollection [hwnd];
 
                                if (!manager.AreGroupsValid ()) 
                                {
                                    // went from one arrangement to another... (e.g. Name->Size) 
                                    RemoveGroupAndRaiseLogicalChangedEvent (hwnd);
                                }
                            }
                            else 
                            {
                                // New GroupManager showed in the LV 
                                RaiseLogicalChangedEvent (hwnd); 
                            }
                        } 
                        else
                        {
                            // This can mean that Group mode got disable
                            // we need to remove the corresponding GroupManager from collection 
                            // and raise an event
                            // NOTE: Unfortunately LV will not produce event on this consistently (e.g. Any mode->List) 
                            if (_groupsCollection.Contains (hwnd)) 
                            {
                                // groups were removed 
                                RemoveGroupAndRaiseLogicalChangedEvent (hwnd);
                            }
                        }
                    } 
                    break;
 
                case NativeMethods.EventObjectDestroy : 
                    {
                        // lv is being destroyed... 
                        if (_groupsCollection.Contains (hwnd))
                        {
                            _groupsCollection.Remove (hwnd);
                            WinEventTracker.RemoveToNotificationList (hwnd, _groupEvents, null, 3); 
                        }
                    } 
                    break; 

                case NativeMethods.EventObjectHide : 
                    {
                        // alexsn : Explorer LV does not send DESTROY event during destruction, instead EVENT_OBJECT_HIDE is getting send.
                        // OBJECT_HIDE can also be raised by Explorer during Arrange Icons By (e.g. Any->Type), we do not want to "remove" groupmanager
                        // and event notification if this is the case, hence we're are checking window's visible and enable state... 
                        // There is however some timing issue: The window handle sometimes still will be visible and enabled even though OBJECT_HIDE
                        // event was raised in the response to LV going away: 
                        // Hence our "remove" code may not be executing all the time 
                        if (_groupsCollection.Contains (hwnd) && !SafeNativeMethods.IsWindowVisible (hwnd) && !SafeNativeMethods.IsWindowEnabled (hwnd))
                        { 
                            _groupsCollection.Remove (hwnd);
                            WinEventTracker.RemoveToNotificationList (hwnd, _groupEvents, null, 3);
                        }
                    } 
                    break;
            } 
        } 

        // detect if the listview is in LVS_REPORT mode 
        static internal bool InReportView (IntPtr hwnd)
        {
            return ((Misc.GetWindowStyle(hwnd) & NativeMethods.LVS_TYPEMASK) == NativeMethods.LVS_REPORT);
        } 

        // Removes group from collection 
        // and notifies client about LV tree structure change 
        static internal void RemoveGroupAndRaiseLogicalChangedEvent (IntPtr hwnd)
        { 
            // Raise logical structure changed event
            RaiseLogicalChangedEvent (hwnd);
        }
 
        // Invalidate LV tree structure
        static internal void RaiseLogicalChangedEvent (IntPtr hwnd) 
        { 
            // remove groupmanager from collection
            _groupsCollection.Remove (hwnd); 

            // Raise logical structure changed event
            IRawElementProviderFragment wlv = (IRawElementProviderFragment) new WindowsListView (hwnd, null, -1);
 
            // Note we're using MakeRuntimeId() vs IRawElementProviderFragment.GetRuntimeId().  GetRuntimeId
            // only returns the part of the RuntimeId for the subtree this provider is handling.  When returning 
            // RuntimeId for an event the entire RuntimeId is required so use MakeRuntimeId(). 
            StructureChangedEventArgs change = new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, ( (WindowsListView)wlv ).MakeRuntimeId() );
            AutomationInteropProvider.RaiseStructureChangedEvent(wlv, change); 
        }

        // get listview item count
        static internal int GetItemCount (IntPtr hwnd) 
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero); 
        } 

        // get listview item count os selected items 
        static internal int GetSelectedItemCount (IntPtr hwnd)
        {
            if (GetItemCount (hwnd) <= 0)
                return 0; 

            int count = 0; 
            for (int index = GetItemNext(hwnd, -1, NativeMethods.LVNI_SELECTED); index != -1; index = GetItemNext(hwnd, index, NativeMethods.LVNI_SELECTED)) 
            {
                count++; 
            }

            return count;
        } 

        static internal int GetStartOfSelectedItems (IntPtr hwnd) 
        { 
            return GetItemNext(hwnd, -1, NativeMethods.LVNI_SELECTED);
        } 

        // Search for the next listview item based on the passed in properties
        // pass -1 for item in order to find the first item that matches condition
        // specified by flags 
        static internal int GetItemNext (IntPtr hwnd, int item, int flags)
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETNEXTITEM, new IntPtr(item), new IntPtr(flags)); 
        }
 
        static internal bool IsIconView(IntPtr hwnd)
        {
            return ListViewGetView(hwnd) == NativeMethods.LV_VIEW_ICON;
        } 

        // Retrieves the current view of the listview control 
        static internal int ListViewGetView (IntPtr hwnd) 
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETVIEW, IntPtr.Zero, IntPtr.Zero); 
        }

        // simple version of ApproxiamateViewRect
        static internal int ApproximateViewRect (IntPtr hwnd) 
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_APPROXIMATEVIEWRECT, new IntPtr(-1), NativeMethods.Util.MAKELPARAM(-1, -1)); 
        } 

        // Scroll the content of the listview control 
        static internal bool Scroll (IntPtr hwnd, IntPtr dx, IntPtr dy)
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_SCROLL, dx, dy) != 0;
        } 

        // get listview rectangle 
        static internal unsafe bool GetItemRect (IntPtr hwnd, int item, int lvir, out NativeMethods.Win32Rect itemRectangle) 
        {
            itemRectangle = NativeMethods.Win32Rect.Empty; 
            itemRectangle.left = lvir;

            fixed (int * location = &(itemRectangle.left))
            { 
                if (XSendMessage.XSend(hwnd, NativeMethods.LVM_GETITEMRECT, new IntPtr(item), new IntPtr(location), Marshal.SizeOf(itemRectangle.GetType())))
                { 
                    return Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref itemRectangle, 2); 
                }
 
                return false;
            }
        }
 
        // check if lv has a group view enabled
        internal static bool ListViewIsGroupViewEnabled (IntPtr hwnd) 
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_ISGROUPVIEWENABLED, IntPtr.Zero, IntPtr.Zero) != 0;
        } 

        // unselect all items in the listview
        static internal bool UnselectAll (IntPtr hwnd)
        { 
            return SetItemState(hwnd, -1, NativeMethods.LVIS_SELECTED, 0);
        } 
 
        // select specified listview item
        static internal bool SelectItem (IntPtr hwnd, int item) 
        {
            return SetItemState(hwnd, item, NativeMethods.LVIS_SELECTED, NativeMethods.LVIS_SELECTED);
        }
 
        // un-select specified listview item
        static internal bool UnSelectItem (IntPtr hwnd, int item) 
        { 
            return SetItemState(hwnd, item, NativeMethods.LVIS_SELECTED, 0);
        } 

        // detect if listview item selected
        static internal bool IsItemSelected (IntPtr hwnd, int listItem)
        { 
            return Misc.IsBitSet(GetItemState(hwnd, listItem, NativeMethods.LVIS_SELECTED), NativeMethods.LVIS_SELECTED);
        } 
 
        // detect if listviewitem has label that can be edited
        static internal bool ListViewEditable (IntPtr hwnd) 
        {
            return Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_EDITLABELS);
        }
 
        // detect if listviewitem can be invoked
        static internal bool ListViewInvokable(IntPtr hwnd) 
        { 
            int style = GetExtendedListViewStyle(hwnd);
 
            // Listview documentation suggests LVS_EX_ONECLICKACTIVATE or LVS_EX_TWOCLICKACTIVATE
            // indicate an item that can be activated, but Explorer listview items do not provide
            // either of these flags.  They do however contain the values LVS_EX_UNDERLINEHOT and
            // LVS_EX_UNDERLINECOLD.  Documentation for these flags indicate they're specifically 
            // for items that can be activated, and that they have no impact if one of the other
            // two values is not set: 
            // 
            // LVS_EX_UNDERLINEHOT
            // Causes those hot items that may be activated to be displayed with underlined text. 
            //
            // LV_EX_UNDERLINE_COLD
            // Causes those non-hot items that may be activated to be displayed with underlined text.
            // 
            // This code tests for both sets of styles since the presence of the HOT or COLD bits
            // implies items that can be activated and supports our observations of Explorer. 
 
            int flags = NativeMethods.LVS_EX_ONECLICKACTIVATE
                | NativeMethods.LVS_EX_TWOCLICKACTIVATE 
                | NativeMethods.LVS_EX_UNDERLINEHOT
                | NativeMethods.LVS_EX_UNDERLINECOLD;

            return ((style & flags) != 0); 
        }
 
        static internal IntPtr ListViewEditLabel(IntPtr hwnd, int item) 
        {
            return Misc.ProxySendMessage(hwnd, NativeMethods.LVM_EDITLABEL, new IntPtr(item), IntPtr.Zero); 
        }

        // detect if listview enables item activation with one click
        static internal bool ListViewSingleClickActivate (IntPtr hwnd) 
        {
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_ONECLICKACTIVATE); 
        } 

        // detect if listview supports multiple selection 
        static internal bool MultiSelected (IntPtr hwnd)
        {
            return !Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_SINGLESEL);
        } 

        // detect if listview contains or potential may contain scrollbar 
        static internal bool Scrollable (IntPtr hwnd) 
        {
            return !Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_NOSCROLL); 
        }

        // ensure listview item visibility
        static internal bool EnsureVisible (IntPtr hwnd, int item, bool partialOK) 
        {
            IntPtr partialVisible = (partialOK) ? IntPtr.Zero : new IntPtr (1); 
 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_ENSUREVISIBLE, new IntPtr(item), partialVisible) != 0;
        } 

        // return listview header
        static internal IntPtr ListViewGetHeader (IntPtr hwnd)
        { 
            return Misc.ProxySendMessage(hwnd, NativeMethods.LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
        } 
 
        // retrieve listview item text
        static internal string GetItemText (IntPtr hwnd, NativeMethods.LVITEM item) 
        {
            item.cchTextMax = Misc.MaxLengthNameProperty;

            return XSendMessage.GetItemText(hwnd, item); 
        }
 
        // perform a hit test on the specific point 
        // POINT is in screen coordinates
        static internal NativeMethods.LVHITTESTINFO_INTERNAL SubitemHitTest (IntPtr hwnd, NativeMethods.Win32Point pt) 
        {
            return SubitemHitTest (hwnd, 0, pt);
        }
 
        // perform a hit test on the specific point
        // POINT is in screen coordinates 
        static internal NativeMethods.LVHITTESTINFO_INTERNAL SubitemHitTest (IntPtr hwnd, int item, NativeMethods.Win32Point pt) 
        {
            // Allocate a local LVHITTESTINFO struct. 
            NativeMethods.LVHITTESTINFO_INTERNAL hitTest = new NativeMethods.LVHITTESTINFO_INTERNAL ();

            // Set the point of interest.
            hitTest.pt = pt; 
            hitTest.iItem = item;
 
            int result = -1; 

            // convert to client 
            if (Misc.MapWindowPoints(IntPtr.Zero, hwnd, ref hitTest.pt, 1))
            {
                unsafe
                { 
                    // Send the LVM_SUBITEMHITTEST message to the list view owner process.
                    // This is ok to do even for non LVS_REPORT listview, since in that case this 
                    // message will behaive like LVM_HITTEST 
                    if (Misc.IsComctrlV6OnOsVerV6orHigher(hwnd))
                    { 
                        NativeMethods.LVHITTESTINFO_V6 hitTestNative = new NativeMethods.LVHITTESTINFO_V6(hitTest);
                        result = XSendMessage.XSendGetIndex(hwnd, NativeMethods.LVM_SUBITEMHITTEST, IntPtr.Zero, new IntPtr(&hitTestNative), Marshal.SizeOf(hitTestNative.GetType()));
                        hitTest.flags = hitTestNative.flags;
                        hitTest.iItem = hitTestNative.iItem; 
                        hitTest.iGroup = hitTestNative.iGroup;
                    } 
                    else 
                    {
                        NativeMethods.LVHITTESTINFO hitTestNative = new NativeMethods.LVHITTESTINFO(hitTest); 
                        result = XSendMessage.XSendGetIndex(hwnd, NativeMethods.LVM_SUBITEMHITTEST, IntPtr.Zero, new IntPtr(&hitTestNative), Marshal.SizeOf(hitTestNative.GetType()));
                        hitTest.flags = hitTestNative.flags;
                        hitTest.iItem = hitTestNative.iItem;
                    } 
                }
            } 
 
            if (result == -1)
            { 
                hitTest.iSubItem = hitTest.iItem = -1;
            }

            return hitTest; 
        }
 
        // retrieve count of header items 
        static internal int HeaderItemCount (IntPtr hwnd)
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.HDM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
        }

        // detect if the listview support checkboxes 
        static internal bool CheckBoxes (IntPtr hwnd)
        { 
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_CHECKBOXES); 
        }
 
        // get listview item check state
        static internal int GetCheckedState (IntPtr hwnd, int item)
        {
            int state = GetItemState(hwnd, item, NativeMethods.LVIS_STATEIMAGEMASK); 

            return ((state >> 12) - 1); 
        } 

        // detect if listview is auto-arranged 
        static internal bool ListViewAutoArrange (IntPtr hwnd)
        {
            return Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_AUTOARRANGE);
        } 

        // detect if listview supports full row selection 
        static public bool FullRowSelect (IntPtr hwnd) 
        {
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_FULLROWSELECT); 
        }

        // detects if icons are lined up in columns that use up the whole view area
        static public bool HasJustifyColumnsExStyle(IntPtr hwnd) 
        {
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_JUSTIFYCOLUMNS); 
        } 

        // gets rectangle of the subitem. 
        // This method is inteded to be used with the LVS_REPORT lv
        static public unsafe bool GetSubItemRect (IntPtr hwnd, int item, int subItem, int lvir, out NativeMethods.Win32Rect itemRectangle)
        {
            itemRectangle = NativeMethods.Win32Rect.Empty; 
            itemRectangle.left = lvir;
            itemRectangle.top = subItem; 
 
            fixed (int * location = &(itemRectangle.left))
            { 
                if (XSendMessage.XSend(hwnd, NativeMethods.LVM_GETSUBITEMRECT, new IntPtr(item), new IntPtr(location), Marshal.SizeOf(itemRectangle.GetType())))
                {
                    return Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref itemRectangle, 2);
                } 

                return false; 
            } 
        }
 
        static internal string GetItemToolTipText(IntPtr hwnd)
        {
            IntPtr hwndToolTip = Misc.ProxySendMessage(hwnd, NativeMethods.LVM_GETTOOLTIPS, IntPtr.Zero, IntPtr.Zero);
 
            return Misc.GetItemToolTipText(hwnd, hwndToolTip, 0);
        } 
 
        #endregion Internal Methods
 
        //-----------------------------------------------------
        //
        //  Internal Fields
        // 
        //------------------------------------------------------
 
        #region Internal Fields 

        internal readonly static GroupManagerCollection _groupsCollection = new GroupManagerCollection(); 
        // alexsn Used for MultipleView Pattern, until official table
        // will not be finalyzed
        //
 
        internal static string [] ListViewViews = new string [] {
                "Icons", "Details", "Smallicon", "List", "Tiles" 
            }; 

        #endregion Internal Fields 

        //-----------------------------------------------------
        //
        //  Protected Methods 
        //
        //----------------------------------------------------- 
 
        #region Protected Methods
 
        // Picks a WinEvent to track for a UIA property
        protected override int [] PropertyToWinEvent (AutomationProperty idProp)
        {
            if (idProp == ValuePattern.ValueProperty) 
            {
                return CheckBoxes (_hwnd) ? new int [] { NativeMethods.EventObjectNameChange, NativeMethods.EventObjectStateChange } : new int [] { NativeMethods.EventObjectNameChange }; 
            } 
            else if (idProp == GridPattern.ColumnCountProperty || idProp == GridPattern.RowCountProperty || idProp == GridItemPattern.ColumnProperty || idProp == GridItemPattern.RowProperty)
            { 
                return new int [] { NativeMethods.EventObjectReorder };
            }
            return base.PropertyToWinEvent (idProp);
        } 

        #endregion Protected Methods 
 
        //-----------------------------------------------------
        // 
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        // Create a Listview item proxy 
        private ProxyFragment CreateListViewItem(int index)
        { 
            return CreateListViewItemOrStartMenuItem(this, index);
        }

        // Create a group for the listview 
        private ProxyFragment CreateListViewGroup (int groupID)
        { 
            return new WindowsListViewGroup (_hwnd, this, groupID); 
        }
 
        // Create a Listview item proxy, check if the item is enclosed in a group.
        // If it is the case, then create also the ListViewGroup and do the parenting
        // properly.
        private ProxySimple CreateListViewItemCheckIfInGroup (int item) 
        {
            // if LV is in Group mode, lvitems will live under the corresponding group 
            bool hasGroup = IsGroupViewEnabled (_hwnd); 

            if ((!hasGroup) && GetItemCount(_hwnd) > 0) 
            {
                return CreateListViewItemOrStartMenuItem(this, item);
            }
 
            // Navigate to the first group
            if (hasGroup) 
            { 
                _groupsCollection.EnsureCreation(_hwnd);
 
                GroupManager manager = _groupsCollection[_hwnd];
                int[] groupIds = manager.GetGroupIds();

                // if there are no groups this is an empty list 
                if (groupIds.Length == 0)
                { 
                    return null; 
                }
 
                // Loop through all the groups to figure out in which group this item belongs
                foreach (int groupId in groupIds)
                {
                    GroupManager.GroupInfo gi = manager.GetGroupInfo (groupId); 

                    if (gi.IndexOf (item) != -1) 
                    { 
                        return CreateListViewItemOrStartMenuItem(new WindowsListViewGroup(_hwnd, this, groupId), item);
                    } 
                }

                // Could not find an item, must be a groupId.
                return new WindowsListViewGroup (_hwnd, this, item); 
            }
 
            // no content go for the scrollbars 
            return null;
        } 

        private ProxyFragment CreateListViewItemOrStartMenuItem(ProxyFragment parent, int item)
        {
            // ListView items on the Start Menu are special.  The IAccessible is needed to get 
            // information from these special ListView items.  If a valid IAccessible can not be
            // obtained default to the normal ListView item. 
            if (InStartMenu() && AccessibleObject != null) 
            {
                ProxyFragment proxyFragment = new ListViewItemStartMenu(_hwnd, parent, item, AccessibleObject); 
                if (proxyFragment != null)
                {
                    proxyFragment.AccessibleObject = AccessibleObject;
                } 
                return proxyFragment;
            } 
            else 
            {
                return new ListViewItem(_hwnd, parent, item); 
            }
        }

        // This method maybe OS depended.  (Most likely will change in Longhorn.  In Longhorn we may not need this method.) 
        private bool InStartMenu()
        { 
            string className = Misc.GetClassName(Misc.GetParent(_hwnd)); 
            return string.Compare(className, "DesktopSFTBarHost", StringComparison.OrdinalIgnoreCase) == 0;
        } 

        private bool SetScrollPercent(double fScrollPos, int sbFlag, int cPelsAll, out int delta)
        {
            // in case of early exit no move 
            delta = 0;
 
            // Check param 
            if ((int)fScrollPos == (int)ScrollPattern.NoScroll)
            { 
                return true;
            }

            if (fScrollPos < 0 || fScrollPos > 100) 
            {
                throw new ArgumentOutOfRangeException(sbFlag == NativeMethods.SB_HORZ ? "horizontalPercent" : "verticalPercent", SR.Get(SRID.ScrollBarOutOfRange)); 
            } 

            int scrollBar = sbFlag == NativeMethods.SB_HORZ ? NativeMethods.OBJID_HSCROLL : NativeMethods.OBJID_VSCROLL; 

            NativeMethods.ScrollBarInfo scrollBarInfo = new NativeMethods.ScrollBarInfo();
            scrollBarInfo.cbSize = Marshal.SizeOf(scrollBarInfo.GetType());
            if (!Misc.GetScrollBarInfo(_hwnd, scrollBar, ref scrollBarInfo) || 
                (scrollBarInfo.scrollBarInfo & NativeMethods.STATE_SYSTEM_INVISIBLE) != 0 ||
                (scrollBarInfo.scrollBarInfo & NativeMethods.STATE_SYSTEM_UNAVAILABLE) != 0) 
            { 
                return false;
            } 

            // Get scroll range
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo (); // this is used all over
 
            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_ALL; 
 
            // if no scroll bar return false
            // on Win 6.0 success is false 
            // on other system check through the scroll info is a scroll bar is there
            if (!Misc.GetScrollInfo(_hwnd, sbFlag, ref si) ||
                !((si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1)))
            { 
                return false;
            } 
 
            // calculate user-requested thumb position
            int deltaPage = (si.nPage > 0) ? si.nPage - 1 : 0; 
            int future = (int) Math.Round (((si.nMax - deltaPage) - si.nMin) * fScrollPos / 100.0 + si.nMin);

            // delta between current and user-requested position in pixels
            // since the cPelsAll contains the dimension in pels for all items + the 2 pels of the border 
            // the operation below does a trunc on purpose
            delta = (future - si.nPos) * (cPelsAll / (si.nMax + 1 - si.nMin)); 
 
            return true;
        } 

        // Grid.GetCell implementation for detail mode
        private IRawElementProviderSimple GetCellInDetailMode (int row, int column)
        { 
            // NOTE: In Detail mode the is no empty cells
            ProxyFragment lvItem = CreateListViewItem (row); 
 
            return new ListViewSubItem (_hwnd, lvItem, column, row);
        } 
        // Grid.GetCell implementation for lv that is not in detail mode
        private IRawElementProviderSimple GetCellInOtherModes (int row, int column, int maxColumn, int maxRow)
        {
            // Assumption: passed in arguments were already verified 
            // NOTE: cells at the end might be empty
            int itemCount = GetItemCount (_hwnd); 
            int itemIndex = 0; 

            if (IsListMode (_hwnd)) 
            {
                // calculate item's index
                itemIndex = column * maxRow + row;
            } 
            else
            { 
                // SIcon, LIcon, Title, Thumbnails 
                itemIndex = row * maxColumn + column;
            } 

            // verify the cell exists
            if (itemIndex >= itemCount)
            { 
                // Return an empty cell
                return new EmptyGridItem (row, column, this); 
            } 

            // return cell 
            return CreateListViewItem (itemIndex);
        }

        // get count of rows in the non-detail lv 
        static private int GetRowCountOtherModes (IntPtr hwnd)
        { 
            // Assumption: items are autoarranged 
            int count = GetItemCount(hwnd);
 
            // Check for empty list
            if (count <= 0)
            {
                return 0; 
            }
 
            if (IsListMode(hwnd)) 
            {
                return GetRowCountListMode(hwnd, count); 
            }

            // Algorithm for non-list mode of ListView with at least one item:
            // Starting from the first item, count the items below it. 
            int rowCount = 0;
            int curItem = 0; 
            while (true) 
            {
                rowCount++; 
                int nextItem = GetItemNext(hwnd, curItem, NativeMethods.LVNI_BELOW);
                // Expect -1 when no more items below current item
                if (nextItem < 0)
                    break; 
                // Guard against infinite loop (getting back the same item LH
                if (nextItem == curItem) 
                    break; 

                // Assumption: As long as nextItem is changing everything is OK 
                // Note: Docs imply it may be possible for nextItem < curItem at this
                // point so don't assume nextItem is always increasing.
                curItem = nextItem;
            } 

            return rowCount; 
        } 

        // detect if the listview has a list style 
        static private bool ListViewList (IntPtr hwnd)
        {
            return ((Misc.GetWindowStyle(hwnd) & NativeMethods.LVS_TYPEMASK) == NativeMethods.LVS_LIST);
        } 

        // get top-left point of the listview item 
        static private unsafe bool GetItemPosition (IntPtr hwnd, int item, out NativeMethods.Win32Point pt) 
        {
            pt.x = 0; 
            pt.y = 0;

            fixed (int * location = &(pt.x))
            { 
                if (XSendMessage.XSend(hwnd, NativeMethods.LVM_GETITEMPOSITION, new IntPtr(item), new IntPtr(location), Marshal.SizeOf(pt.GetType())))
                { 
                    return Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref pt, 1); 
                }
 
                return false;
            }
        }
 
        // Get listview extended styles
        static private int GetExtendedListViewStyle (IntPtr hwnd) 
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETEXTENDEDLISTVIEWSTYLE, IntPtr.Zero, IntPtr.Zero);
        } 

        // retrieve specific "state" of the listview item
        private static int GetItemState (IntPtr hwnd, int item, int stateMask)
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETITEMSTATE, new IntPtr(item), new IntPtr(stateMask));
        } 
        // set listview item state 
        private static bool SetItemState (IntPtr hwnd, int item, int stateMask, int state)
        { 
            NativeMethods.LVITEM lvitem = new NativeMethods.LVITEM ();

            lvitem.mask = NativeMethods.LVIF_STATE;
            lvitem.state = state; 
            lvitem.stateMask = stateMask;
 
            return XSendMessage.SetItem(hwnd, item, lvitem); 
        }
 
        // Check if the point on the screen is part of the header
        private bool PtInListViewHeader (int x, int y)
        {
            // See if header exist 
            IntPtr hwndHeader = ListViewGetHeader (_hwnd);
 
            if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader)) 
            {
                if (Misc.PtInWindowRect(hwndHeader, x, y)) 
                {
                    return true;
                }
            } 

            return false; 
        } 

 
        private static void RaiseEventsOnClient(IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        {
            ProxySimple el = null;
 
            WindowsListView wlv = new WindowsListView (hwnd, null, -1);
            AutomationProperty automationProperty = idProp as AutomationProperty; 
            AutomationEvent automationEvent = idProp as AutomationEvent; 

            if (eventId == NativeMethods.EventObjectSelectionRemove && automationProperty == SelectionItemPattern.IsSelectedProperty) 
            {
                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1);
                if (el != null)
                { 
                    el.DispatchEvents(eventId, idProp, idObject, idChild);
                    return; 
                } 
            }
            else if (eventId == NativeMethods.EventObjectSelection 
            || eventId == NativeMethods.EventObjectSelectionRemove
            || eventId == NativeMethods.EventObjectSelectionAdd)
            {
                // This is the speced behavior for events and selection 
                //
                // The following rule should be used to decide when to fire Selected vs Add/Remove events: 
                // If the result of a SelectElement or an AddElementToSelection is that a single item is selected, 
                // then send a Select for that element; otherwise send Add/Removes as appropriate.
                // Note that this rule does not depend on whether the selection container is single- or multi- select, 
                // or on what method was used to change the selection. Only the result matters.
                // Overall message to clients (test automation and assistive technologies)
                //
                // 1) If you receive ElementSelectedEvent this guarantees that the element that raised the 
                //    event is the only selected element in that container.
                // 2) If you receive ElementAddedToSelectionEvent this guarantees that those items are added 
                //    to selection and the end result of the selection is more than one item. 
                // 3) If you receive ElementRemovedFromSelectionEvent this guarantees that those items are
                //    deselected and the end result is NOT one item selected. 
                //
                // For the listview adhering to the spec is not possible because of an ambiguity with the winevents that
                // are fired.  This code is trying to map the winevents received to the UIAutomaiton events that are expected.
                // These are the two cases that are ambiguous: 
                //
                // Case 1: 
                // The user clicks two different items in succession. 
                // We get an EventObjectSelectionRemove WinEvent for each item that loses
                // selection and in addition an EventObjectSelection for the new item that got selection. 
                // In this case we want to disregard the EventObjectSelectionRemove (not raise UIA
                // ElementRemovedFromSelectionEvent) because the end result of this scenario is that only one item is selected.
                //
                // Case 2: 
                // If the ListView is multi-select and there are two items selected and the user cntl clicks on one (unselects it).
                // The listview fires only one WinEvent (an EventObjectSelectionRemove) for the 
                // item that was removed.   In this case since there is only one 
                // item left selected UIA should raise ElementSelectedEvent for the remaining item.
                // 
                // If we turn the EventObjectSelectionRemove WinEvent in case 2 into a ElementSelected
                // event for the remaining element we would end up firing two events for each click in a multi
                // select list.  This is because the EventObjectSelectionRemove in case 1 is just like
                // the one case 2.  Except that in case 1 we also get a EventObjectSelection separately so we 
                // would end up firing another selected event.
                // 
                // It has been decided that it is preferred to receive extra EventObjectSelection events 
                // then receiving EventObjectSelectionRemove events at the wrong time. If two items are selected
                // in a listview and the user clicks on one of them the only event winevent we get is a remove so 
                // we have to convert removed winevent to selected event or we would miss an event.  So it better
                // to have multiple events in some case than none when there should be ([....] 9/8/2004).
                //
                if (eventId == NativeMethods.EventObjectSelectionRemove && GetSelectedItemCount(hwnd) == 1) 
                {
                    if (MultiSelected(hwnd)) 
                    { 
                        // Change the EventObjectSelectionRemove to an EventObjectSelection.
                        eventId = NativeMethods.EventObjectSelection; 
                        idProp = SelectionItemPattern.ElementSelectedEvent;

                        // Change the child id to the selected child.
                        int item = GetStartOfSelectedItems(hwnd); 
                        if (item > -1)
                        { 
                            idChild = item + 1; 
                        }
                    } 
                    else
                    {
                        // Since case 2 does not apply to single selection listviews, suppress the
                        // EventObjectSelectionRemove. 
                        return;
                    } 
                } 

                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 
            }
            // GridItem case
            else if (eventId == NativeMethods.EventObjectReorder && (automationProperty == GridItemPattern.ColumnProperty || automationProperty == GridItemPattern.RowProperty))
            { 
                // GridItem case. We need to recursively call all of the list items
                for (el = wlv.GetFirstChild(); el != null; el = wlv.GetNextSibling(el)) 
                { 
                    el.DispatchEvents(eventId, idProp, idObject, idChild);
                } 
                return;
            }
            // Map the WinEvent NameChange to ValueChange to go through the dispatch
            else if (eventId == NativeMethods.EventObjectNameChange) 
            {
                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 
                eventId = NativeMethods.EventObjectValueChange; 
            }
            // Change of state for the check box must generates a StateChange Win Events. 
            // Map it to of ObjectChange for the checkbox
            else if (eventId == NativeMethods.EventObjectStateChange && CheckBoxes(hwnd))
            {
                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 

                el = ((ProxyFragment)el).GetFirstChild(); 
                eventId = NativeMethods.EventObjectValueChange; 

                // Assert if the assumption that the first child is a check box is false 
                System.Diagnostics.Debug.Assert(el is ListViewItemCheckbox);
            }
            // Special case for logical element change for a list view item
            else if ((eventId == NativeMethods.EventObjectDestroy || eventId == NativeMethods.EventObjectCreate) && automationEvent == AutomationElement.StructureChangedEvent) 
            {
                ProxySimple parent = wlv; 
                bool fGroupView = IsGroupViewEnabled(hwnd); 

                // Allways disable the groups as one may have been created or 
                // destroyed
                if (fGroupView)
                {
                    // remove groupmanager from collection 
                    _groupsCollection.Remove(hwnd);
 
                    // If it is an object creation, create the element and picks 
                    // its parent to invalidate. The parent can be either a group
                    // or  the listview itself. 
                    if (eventId == NativeMethods.EventObjectCreate && fGroupView)
                    {
                        // Get the item with the resetted collection of groups (may be null if the group is empty)
                        ProxySimple lvi = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 
                        if (lvi != null)
                            parent = lvi.GetParent(); 
                    } 
                }
 
                // If the element destroyed is in a group invalidate the whole listview as we have no
                // idea the element was part of before
                // Since children are referenced by position in the tree, addition and removal
                // of items leads to different results when asking properties for the same element 
                // On removal, item + 1 is now item!
                // Use Children Invalidated to let the client knows that all the cached children are invalid 
                AutomationInteropProvider.RaiseStructureChangedEvent( parent, new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, parent.MakeRuntimeId() ) ); 
                return;
            } 
            else
            {
                el = wlv;
            } 

            if (el != null) 
            { 
                el.DispatchEvents(eventId, idProp, idObject, idChild);
            } 

            return;
        }
 
        #endregion Private Methods
 
        //----------------------------------------------------- 
        //
        //  Private Fields 
        //
        //------------------------------------------------------

        #region Private Fields 

        // group specific events. Used for internal tracking 
        private readonly static WinEventTracker.EvtIdProperty [] _groupEvents; 

        #endregion Private Fields 
    }

}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: Win32 ListView proxy 
//
// History: 
//        Jean-Francois Peyroux, alexsn - Created (in DotNet)
//        2003/08/08 - alexsn Updated for WCP
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections; 
using System.Text;
using System.Runtime.InteropServices; 
using System.ComponentModel;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows; 
using MS.Win32;
 
namespace MS.Internal.AutomationProxies 
{
    // 
    // ListView Logical Tree Diagram
    //
    //  ListView
    //    --Group 1 (if in "Group" mode, if not LVI gets promoted up) 
    //      -----ListViewItem0...ListViewItemN
    //           ---------Cell (if in details mode) 
    //    --Group 2(if in "Group" mode, if not LVI gets promoted up) 
    //      -----ListViewItem0...ListViewItemN
    //           ---------Cell (if in details mode) 
    //    --Header (If in detail mode)
    //      ----Header Item0...X (If in detail mode)
    //    --ScrollBar
    //       -----SmallDecrement 
    //       -----LargeDecrement
    //       -----Thumb 
    //       -----LargeIncrement 
    //       -----SmallIncrement
    // 
    //  NOTE: Hwnd-based tree  will look like this
    //
    //              ListView
    //                 | 
    //                 |
    //                SysHeader32 (if available) 
    // 
    //   UIAutomation will discover a header for us and will hook it up
    //   to our navigation chain, nothing needs to be done on our side, it is just a magic 
    //   Please do not add ANY SysHeader32 specific navigation code
    class WindowsListView: ProxyHwnd, ISelectionProvider, IScrollProvider, IGridProvider, IMultipleViewProvider, ITableProvider
    {
 
        // -----------------------------------------------------
        // 
        //  Constructors 
        //
        //----------------------------------------------------- 

        #region Constructors

        static WindowsListView () 
        {
            _groupEvents = new WinEventTracker.EvtIdProperty [3]; 
            _groupEvents [0]._evtId = NativeMethods.EventObjectReorder; 
            _groupEvents [1]._evtId = NativeMethods.EventObjectHide;
            _groupEvents [2]._evtId = NativeMethods.EventObjectDestroy; 
            _groupEvents [0]._idProp = _groupEvents [1]._idProp = _groupEvents [2]._idProp = 0;
        }

        internal WindowsListView (IntPtr hwnd, ProxyFragment parent, int item) 
            : base( hwnd, parent, item )
        { 
            // Set the strings to return properly the properties. 
            if (IsDetailMode(hwnd))
            { 
                _cControlType = ControlType.DataGrid;
            }
            else
            { 
                _cControlType = ControlType.List;
            } 
 
            // Can be focused
            _fIsKeyboardFocusable = true; 

            // support for events
            _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents);
 
            // internally track some of the lv events
            WinEventTracker.AddToNotificationList (_hwnd, new WinEventTracker.ProxyRaiseEvents (WindowsListView.GroupSpecificEvents), _groupEvents, 3); 
        } 

        #endregion Constructors 

        #region Proxy Create

        // Static Create method called by UIAutomation to create this proxy. 
        // null if unsuccessful
        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild, int idObject) 
        { 
            return Create(hwnd, idChild);
        } 

        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild)
        {
            WindowsListView lv = new WindowsListView(hwnd, null, 0); 

            if( idChild == 0 ) 
                return lv; 
            else
                return lv.CreateListViewItemCheckIfInGroup (idChild - 1); 
        }

        internal static void RaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        { 
            switch (idObject)
            { 
                case NativeMethods.OBJID_CLIENT : 
                {
                    RaiseEventsOnClient(hwnd, eventId, idProp, idObject, idChild); 
                    return;
                }

                case NativeMethods.OBJID_WINDOW : 
                {
                    // Special case for logical element change for a list view item 
                    if ((eventId == NativeMethods.EventObjectReorder) && (idProp as AutomationEvent) == AutomationElement.StructureChangedEvent) 
                    {
                        WindowsListView wlv = new WindowsListView( hwnd, null, -1 ); 

                        AutomationInteropProvider.RaiseStructureChangedEvent( wlv, new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, wlv.MakeRuntimeId() ) );
                        return;
                    } 

                    break; 
                } 

                case NativeMethods.OBJID_VSCROLL: 
                case NativeMethods.OBJID_HSCROLL:
                    // The NonClientArea proxy handles these events
                    break;
 
                default :
                { 
                    ProxySimple el = new WindowsListView( hwnd, null, -1 ); 
                    if (el != null)
                    { 
                        el.DispatchEvents( eventId, idProp, idObject, idChild );
                    }
                    break;
                } 
            }
 
        } 

        #endregion 

        //------------------------------------------------------
        //
        //  Patterns Implementation 
        //
        //----------------------------------------------------- 
 
        #region ProxySimple Interface
 
        internal override object GetPatternProvider (AutomationPattern iid)
        {
            // selection is always supported
            if (iid == SelectionPattern.Pattern) 
            {
                return this; 
            } 

            // Note that condition for grid should be true when condition for table is also true: 
            // providers that implement table should impl grid at the same time.
            if (iid == GridPattern.Pattern &&
                    (IsDetailMode (_hwnd) ||
                     IsImplementingGrid (_hwnd) && GetItemCount (_hwnd) > 0)) 
            {
                return this; 
            } 

            if (iid == MultipleViewPattern.Pattern) 
            {
                return this;
            }
 
            // table is supported only in the detail mode
            if (iid == TablePattern.Pattern && IsDetailMode (_hwnd)) 
            { 
                return this;
            } 

            return null;
        }
 
        #endregion ProxySimple Interface
 
        #region ProxyFragment Interface 

        internal override ProxySimple GetNextSibling (ProxySimple child) 
        {
            bool hasGroup = IsGroupViewEnabled (_hwnd);
            int item = child._item;
            int count = -1; 

            if (!hasGroup) 
            { 
                // Determine how many items are in the list view.
                count = GetItemCount (_hwnd); 

                // Next for an item that does not exist in the list
                if (item >= count)
                { 
                    throw new ElementNotAvailableException ();
                } 
 
                // Check if index of the next item is in range
                if (item >= 0 && (item + 1) < count) 
                {
                    // return a node to represent the requested item.
                    return CreateListViewItem (item + 1);
                } 
            }
 
            if (hasGroup) 
            {
                if (child is ListViewItem) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed));
                }
 
                WindowsListViewGroup windowsListViewGroup = child as WindowsListViewGroup;
                if (windowsListViewGroup != null) 
                { 
                    // The group might have destroyed by an event between a first an now
                    _groupsCollection.EnsureCreation (_hwnd); 

                    GroupManager manager = _groupsCollection[_hwnd];
                    int [] groupIds = manager.GetGroupIds ();
                    int groupsCount = groupIds.Length; 

                    // if there are no groups this is an empty list 
                    if (groupIds.Length == 0) 
                    {
                        return null; 
                    }

                    // Navigation: from one group to another
                    int groupID = windowsListViewGroup.ID; 
                    int location = Array.IndexOf (groupIds, groupID);
                    if (location == -1) 
                        return null; // the ListView is updating so this group ID is no longer valid? 

                    if (location + 1 < groupsCount) 
                    {
                        return CreateListViewGroup (groupIds [location + 1]);
                    }
                } 
            }
 
            return null; 
        }
 
        internal override ProxySimple GetPreviousSibling (ProxySimple child)
        {
            // go to lv group if applicable
            // When to go to the Group: 
            // A. If navigating from the scrollbar
            // B. When navigating from the another Group 
            if (IsGroupViewEnabled (_hwnd)) 
            {
                // The group might have destroyed by an event between a first an now 
                _groupsCollection.EnsureCreation (_hwnd);

                // note: navigation is done from last group to the first
                GroupManager manager = _groupsCollection [_hwnd]; 
                int [] groupIds = manager.GetGroupIds ();
                int groupsCount = groupIds.Length; 
 
                // if there are no groups this is an empty list
                if (groupIds.Length == 0) 
                {
                    return null;
                }
 
                // check if current child is a group
                WindowsListViewGroup windowsListViewGroup = child as WindowsListViewGroup; 
                if (windowsListViewGroup != null) 
                {
                    int groupID = windowsListViewGroup.ID; 
                    int location = Array.IndexOf (groupIds, groupID);
                    if (location == -1)
                        return null; // the ListView is updating so this group ID is no longer valid?
 
                    if (location > 0)
                    { 
                        return CreateListViewGroup(groupIds [location - 1]); 
                    }
                } 
                else
                {
                    // return last group
                    return CreateListViewGroup(groupIds [groupsCount - 1]); 
                }
            } 
                // in the case when lv in the Group mode 
                // the lvitems will live under the corresponding group
                //if (!hasGroup) 
                else
            {
                // LVItem can be called by:
                // A. Scrollbar 
                // B. Another listviewitem
                int item = child._item; 
                int count = GetItemCount (_hwnd); 
                if (item > 0 && item < count)
                { 
                    // navigation from one lvitem to another
                    return CreateListViewItem (item - 1);
                }
 
                // we may need to retrun either last lv item
                // or give control back to UIAutomation (e.g. From 1st lvitem to header) 
                return item < 0 && count > 0 ? CreateListViewItem (count - 1) : null; 
            }
 
            return null;
        }

        internal override ProxySimple GetFirstChild () 
        {
            // if LV is in Group mode, lvitems will live under the corresponding group 
            bool hasGroup = IsGroupViewEnabled (_hwnd); 
            int itemCount = GetItemCount(_hwnd);
 
            if (itemCount > 0)
            {
                if (!hasGroup)
                { 
                    return CreateListViewItem(0);
                } 
 
                // Navigate to the first group
                if (hasGroup) 
                {
                    _groupsCollection.EnsureCreation(_hwnd);

                    GroupManager manager = _groupsCollection[_hwnd]; 
                    int[] groupIds = manager.GetGroupIds();
 
                    // if there are no groups this is an empty list 
                    if (groupIds.Length == 0)
                    { 
                        return null;
                    }

                    // return the first group 
                    return CreateListViewGroup(groupIds[0]);
                } 
            } 

            // no content 
            return null;
        }

        internal override ProxySimple GetLastChild () 
        {
            bool hasGroup = IsGroupViewEnabled (_hwnd); 
 
            // check for the group
            if (hasGroup) 
            {
                // group view is enabled and we got here
                // now return the last group
                _groupsCollection.EnsureCreation (_hwnd); 

                GroupManager manager = _groupsCollection [_hwnd]; 
                int [] groupIds = manager.GetGroupIds (); 
                int groupsCount = groupIds.Length;
 
                // if there are no groups this is an empty list
                if (groupIds.Length == 0)
                {
                    return null; 
                }
 
                return CreateListViewGroup (groupIds[groupsCount - 1]); 
            }
                // if !group 
                else
            {
                int count = GetItemCount (_hwnd);
                return count > 0 ? CreateListViewItem (count - 1) : null; 
            }
        } 
 
        internal override ProxySimple ElementProviderFromPoint (int x, int y)
        { 
            // The header is a window on top of the client area.
            // If the point is the non client area, skip
            // Must return null if hit tested.
            if (PtInListViewHeader (x, y) || !PtInClientRect (_hwnd, x, y)) 
            {
                return null; 
            } 

            // in the case of the lv in the group mode, return a group at point(x,y) 
            if (IsGroupViewEnabled (_hwnd))
            {
                _groupsCollection.EnsureCreation (_hwnd);
 
                // Let's locate the group that contains x,y
                GroupManager manager = WindowsListView._groupsCollection [_hwnd]; 
                int length = manager.GroupCount (); 
                for (int i = 0; i < length; i++)
                { 
                    NativeMethods.Win32Rect rc = manager.GetGroupRcByIndex (i);
                    if (Misc.PtInRect(ref rc, x, y))
                    {
                        // found a group where point belongs 
                        int groupID = manager.GetGroupIdByIndex (i);
                        ProxyFragment group = new WindowsListViewGroup (_hwnd, this, groupID); 
                        return ProxyFragment.DrillDownFragment (group, x, y); 
                    }
                } 
            }

            NativeMethods.LVHITTESTINFO_INTERNAL hitTest = WindowsListView.SubitemHitTest(_hwnd, new NativeMethods.Win32Point(x, y));
            if (hitTest.iItem >= 0) 
            {
                // create the item 
                ProxyFragment item = CreateListViewItemOrStartMenuItem(this, hitTest.iItem); 
                return ProxyFragment.DrillDownFragment (item, x, y);
            } 
            else if (hitTest.flags == NativeMethods.LVHT_NOWHERE && IsDetailMode(_hwnd))
            {
                // LVHT_NOWHERE means: The position is inside the list-view control's client window, but it is not over a list item.
                // The hit was in the non-client area of the ListView box, so adjust piont to get the point on a 
                // sub-item to find which ListViewItem to create.  There is not adjustment needed in the y-coordinate
                // spaces since ListView's do not apply a non-client area to the top or the bottom of items. 
                Rect boundingRectangle = BoundingRectangle; 
                int xAdjustment = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXBORDER) + UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXFRAME);
 
                if (x - boundingRectangle.Left < xAdjustment)
                {
                    x += xAdjustment;
                } 
                else if (boundingRectangle.Left + boundingRectangle.Width - x < xAdjustment)
                { 
                    x -= xAdjustment; 
                }
 
                hitTest = WindowsListView.SubitemHitTest(_hwnd, new NativeMethods.Win32Point(x, y));
                if (hitTest.iItem >= 0)
                {
                    // Since this original point was in the non-client area of the listview, the item being looked for 
                    // is the item not one of it's subitems, so just return the ListViewItem.
                    return CreateListViewItemOrStartMenuItem(this, hitTest.iItem); 
                } 
            }
 
            // NOTE: Do not return this, since user might of specified point that belongs to the hwnd-control
            // that lives inside of the lv (e.g. header). If we return null UIAutomation will do the drilling
            return null;
        } 

        // Returns an item corresponding to the focused element (if there is one), or null otherwise. 
        internal override ProxySimple GetFocus () 
        {
            if (IsGroupViewEnabled (_hwnd)) 
            {
                _groupsCollection.EnsureCreation (_hwnd);
                return WindowsListViewGroup.GetFocusInGroup (_hwnd, this);
            } 

            // get the focused item 
            int index = GetItemNext(_hwnd, -1, NativeMethods.LVNI_FOCUSED); 

            if (index != -1) 
            {
                return CreateListViewItemCheckIfInGroup (index);
            }
 
            return this;
        } 
 
        #endregion
 
        #region ProxyHwnd Interface

        internal override void AdviseEventAdded (AutomationEvent eventId, AutomationProperty [] aidProps)
        { 
            if (aidProps != null)
            { 
                for (int i = 0, c = aidProps.Length; i < c; i++) 
                {
                    if (aidProps [i] == TablePattern.ColumnHeadersProperty) 
                    {
                        // Return array of the HeaderItems
                        IntPtr hwndHeader = ListViewGetHeader (_hwnd);
                        if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader)) 
                        {
                            WindowsSysHeader header = (WindowsSysHeader) WindowsSysHeader.Create (hwndHeader, 0); 
                            WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectCreate, TablePattern.ColumnHeadersProperty) }; 
                            WinEventTracker.AddToNotificationList(hwndHeader, header._createOnEvent, aEvents, 1);
                        } 
                    }
                }
            }
 
            if (eventId == InvokePattern.InvokedEvent)
            { 
                WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectSelection, eventId) }; 
                WinEventTracker.AddToNotificationList(_hwnd, _createOnEvent, aEvents, 1);
            } 

            base.AdviseEventAdded (eventId, aidProps);
        }
 
        internal override void AdviseEventRemoved (AutomationEvent eventId, AutomationProperty [] aidProps)
        { 
            if (aidProps != null) 
            {
                for (int i = 0, c = aidProps.Length; i < c; i++) 
                {
                    if (aidProps [i] == TablePattern.ColumnHeadersProperty)
                    {
                        // Return array of the HeaderItems 
                        IntPtr hwndHeader = ListViewGetHeader (_hwnd);
                        if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader)) 
                        { 
                            WindowsSysHeader header = (WindowsSysHeader) WindowsSysHeader.Create (hwndHeader, 0);
                            WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectCreate, TablePattern.ColumnHeadersProperty) }; 
                            WinEventTracker.RemoveToNotificationList (hwndHeader, aEvents, header._createOnEvent, 1);
                        }
                    }
                } 
            }
 
            if (eventId == InvokePattern.InvokedEvent) 
            {
                WinEventTracker.EvtIdProperty[] aEvents = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectSelection, eventId) }; 
                WinEventTracker.AddToNotificationList(_hwnd, _createOnEvent, aEvents, 1);
            }

            base.AdviseEventRemoved(eventId, aidProps); 
        }
 
        #endregion 

        #region SelectionPattern 

        // Returns an array of elements that are current selection.
        IRawElementProviderSimple[] ISelectionProvider.GetSelection()
        { 
            int count = GetItemCount (_hwnd);
            int countSelection = MultiSelected(_hwnd) ? GetSelectedItemCount(_hwnd) : 1; 
 
            if (count <= 0 || countSelection <= 0 )
            { 
                // this should be handled correctly in the framework
                return null;
            }
 
            IRawElementProviderSimple[] selection = new IRawElementProviderSimple[countSelection];
            int index = 0; 
 
            for (int itemPos = GetItemNext(_hwnd, -1, NativeMethods.LVNI_SELECTED); itemPos != -1; itemPos = GetItemNext(_hwnd, itemPos, NativeMethods.LVNI_SELECTED))
            { 
                selection[index] = CreateListViewItemCheckIfInGroup(itemPos);
                index++;
            }
 
            if (index == 0)
            { 
                return null; 
            }
 
            return selection;
        }

        // Returns whether the control supports multiple selection. 
        bool ISelectionProvider.CanSelectMultiple
        { 
            get 
            {
                // Get the style bits for the list view window. 
                return MultiSelected (_hwnd);
            }
        }
 
        // Returns whether the control requires a minimum of one selected element at all times.
        bool ISelectionProvider.IsSelectionRequired 
        { 
            get
            { 
                return false;
            }
        }
 

        #endregion SelectionPattern 
 
        #region ScrollPattern
 
        void IScrollProvider.Scroll (ScrollAmount horizontalAmount, ScrollAmount verticalAmount)
        {
            // Make sure that the control is enabled
            if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) 
            {
                throw new ElementNotEnabledException(); 
            } 

            WindowScroll.Scroll (_hwnd, horizontalAmount, verticalAmount, true); 
        }

        void IScrollProvider.SetScrollPercent (double horizontalPercent, double verticalPercent)
        { 
            // Make sure that the control is enabled
            if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) 
            { 
                throw new ElementNotEnabledException();
            } 

            // get the "full size" of the list-view
            int size = ApproximateViewRect (_hwnd);
 
            // ApproximateViewRect adds the window edge size, substract it
            int cx = NativeMethods.Util.LOWORD (size) /*- 2 * UnsafeNativeMethods.GetSystemMetrics (NativeMethods.SM_CXBORDER)*/; 
            int cy = NativeMethods.Util.HIWORD (size) /*- 2 * UnsafeNativeMethods.GetSystemMetrics (NativeMethods.SM_CYBORDER)*/; 

            // maximum width to fit all elements 
            int dx, dy;
            bool fHz = SetScrollPercent (horizontalPercent, NativeMethods.SB_HORZ, cx, out dx);
            bool fVt = SetScrollPercent (verticalPercent, NativeMethods.SB_VERT, cy, out dy);
 
            if (fHz || fVt)
            { 
                // scroll relative to the current thumb position 
                bool fScrollSuccess = true;
 
                // if there is no movement need do not call Scroll().
                if (dx != 0 || dy != 0)
                {
                    fScrollSuccess = Scroll(_hwnd, (IntPtr)dx, (IntPtr)dy); 

                    // On occasion in the listview control the new position of the scroll bar is off by 
                    // one column/row. To deal with that bug in listview, we query the value we just set. 
                    // If it differs then we try a second time to scroll the content. It is a scroll by
                    // just one column and this always succeeds. 
                    // It is done both on hz and vt as a safety measure.
                    if (fScrollSuccess && (((int)horizontalPercent != (int)ScrollPattern.NoScroll && (int)horizontalPercent != (int)WindowScroll.GetPropertyScroll(ScrollPattern.HorizontalScrollPercentProperty, _hwnd))
                    || ((int)verticalPercent != (int)ScrollPattern.NoScroll && (int)verticalPercent != (int)WindowScroll.GetPropertyScroll(ScrollPattern.VerticalScrollPercentProperty, _hwnd))))
                    { 
                        SetScrollPercent(horizontalPercent, NativeMethods.SB_HORZ, cx, out dx);
                        SetScrollPercent(verticalPercent, NativeMethods.SB_VERT, cy, out dy); 
 
                        // if there is no movement need do not call Scroll() again.
                        if (dx != 0 || dy != 0) 
                        {
                            Scroll(_hwnd, (IntPtr)dx, (IntPtr)dy);
                        }
                    } 
                }
 
                if (fHz && fVt && fScrollSuccess) 
                {
                    return; 
                }
            }

            throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); 
        }
 
        // Calc the position of the horizontal scroll bar thumb in the 0..100 % range 
        double IScrollProvider.HorizontalScrollPercent
        { 
            get
            {
                // use the common implementation for all the windows based controls
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.HorizontalScrollPercentProperty, _hwnd); 
            }
        } 
 
        // Calc the position of the Vertical scroll bar thumb in the 0..100 % range
        double IScrollProvider.VerticalScrollPercent 
        {
            get
            {
                // use the common implementation for all the windows based controls 
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.VerticalScrollPercentProperty, _hwnd);
            } 
        } 

        // Percentage of the window that is visible along the horizontal axis. 
        double IScrollProvider.HorizontalViewSize
        {
            get
            { 
                // use the common implementation for all the windows based controls
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.HorizontalViewSizeProperty, _hwnd); 
            } 
        }
 
        // Percentage of the window that is visible along the vertical axis.
        double IScrollProvider.VerticalViewSize
        {
            get 
            {
                // use the common implementation for all the windows based controls 
                return (double)WindowScroll.GetPropertyScroll(ScrollPattern.VerticalViewSizeProperty, _hwnd); 
            }
        } 

        // Can the element be horizontaly scrolled
        bool IScrollProvider.HorizontallyScrollable
        { 
            get
            { 
                // use the common implementation for all the windows based controls 
                return (bool) WindowScroll.GetPropertyScroll (ScrollPattern.HorizontallyScrollableProperty, _hwnd);
            } 
        }

        // Can the element be verticaly scrolled
        bool IScrollProvider.VerticallyScrollable 
        {
            get 
            { 
                return (bool) WindowScroll.GetPropertyScroll (ScrollPattern.VerticallyScrollableProperty, _hwnd);
            } 
        }

        #endregion ScrollPattern
 
        #region Grid Pattern
 
        // Obtain the AutomationElement at an zero based absolute position in the grid. 
        // Where 0,0 is top left
        IRawElementProviderSimple IGridProvider.GetItem(int row, int column) 
        {
            int maxRow = GetRowCount (_hwnd);
            int maxColumn = GetColumnCount (_hwnd);
 
            if (row < 0 || row >= maxRow)
            { 
                throw new ArgumentOutOfRangeException("row", row, SR.Get(SRID.GridRowOutOfRange)); 
            }
 
            if (column < 0 || column >= maxColumn)
            {
                throw new ArgumentOutOfRangeException("column", column, SR.Get(SRID.GridColumnOutOfRange));
            } 

            // GetCell 
            if (IsDetailMode (_hwnd)) 
            {
                return GetCellInDetailMode (row, column); 
            }

            return GetCellInOtherModes (row, column, maxColumn, maxRow);
        } 

        int IGridProvider.RowCount 
        { 
            get
            { 
                return GetRowCount (_hwnd);
            }
        }
 
        int IGridProvider.ColumnCount
        { 
            get 
            {
                return GetColumnCount (_hwnd); 
            }
        }

        #endregion #region Grid Pattern 

        #region Table Pattern 
 
        // Collection of all Row Headers associated with the Table. Order is consistent with the table
        IRawElementProviderSimple [] ITableProvider.GetRowHeaders () 
        {
            return null;
        }
 
        // Collection of all Column Headers associated with the Table. Order is consistent with the table
        IRawElementProviderSimple [] ITableProvider.GetColumnHeaders () 
        { 
            // Return array of the HeaderItems
            IntPtr hwndHeader = ListViewGetHeader (_hwnd); 
            if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader))
            {
                WindowsSysHeader header = (WindowsSysHeader) WindowsSysHeader.Create (hwndHeader, 0);
                int size = HeaderItemCount (hwndHeader); 
                if (size > 0)
                { 
                    IRawElementProviderSimple [] columns = new IRawElementProviderSimple [size]; 
                    for (ProxySimple headerItem = header.GetFirstChild (); headerItem != null; headerItem = header.GetNextSibling (headerItem))
                    { 
                        columns [headerItem._item] = headerItem;
                    }
                    return columns;
                } 
            }
            return null; 
        } 

        // Describe the best way to present the information within this table. 
        RowOrColumnMajor ITableProvider.RowOrColumnMajor
        {
            get
            { 
                return RowOrColumnMajor.RowMajor;
            } 
        } 

 
        #endregion Table Pattern

        #region MultipleViewPattern
 
        //
        string IMultipleViewProvider.GetViewName (int viewID) 
        { 
            if ( viewID < 0 || viewID > ListViewViews.Length )
            { 
                throw new ArgumentException( SR.Get( SRID.InvalidParameter ) );
            }
            return ListViewViews [viewID];
        } 

        void IMultipleViewProvider.SetCurrentView (int viewID) 
        { 
            if ( viewID < 0 || viewID > ListViewViews.Length )
            { 
                throw new ArgumentException( SR.Get( SRID.InvalidParameter ) );
            }
            // alexsn
 

 
            // If requested view is in array, than do a Set 
            // {
            //     return ListViewSetView(_hwnd, viewID); 
            // }

            // currently do nothing
        } 

        int [] IMultipleViewProvider.GetSupportedViews () 
        { 
            // alexsn
 


            return new int [] { ListViewGetView (_hwnd) };
        } 

        int IMultipleViewProvider.CurrentView 
        { 
            get
            { 
                return ListViewGetView (_hwnd);
            }
        }
 

        #endregion MultipleViewPattern 
 
        //------------------------------------------------------
        // 
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        // set focus to the specified item 
        static internal bool SetItemFocused (IntPtr hwnd, int item)
        { 
            return SetItemState(hwnd, item, NativeMethods.LVIS_FOCUSED, NativeMethods.LVIS_FOCUSED);
        }

        // set focus to the specified item 
        static internal bool IsItemFocused (IntPtr hwnd, int item)
        { 
            int state = GetItemState(hwnd, item, NativeMethods.LVIS_FOCUSED); 

            return (Misc.IsBitSet(state, NativeMethods.LVIS_FOCUSED)); 
        }

        // detect if listview is in detail mode
        static internal bool IsDetailMode (IntPtr hwnd) 
        {
            int view = ListViewGetView (hwnd); 
 
            // Current LH builds (4059) display the header even in tile view,
            // which makes us think we are in details view. 
            // Explicitly detect tile view and return false.
            if (view == NativeMethods.LV_VIEW_TILE)
            {
                return false; 
            }
 
            if (InReportView(hwnd) || (view == NativeMethods.LV_VIEW_DETAILS)) 
            {
                return true; 
            }

            if (Environment.OSVersion.Version.Major < 6)
            { 
                // handle lv that not LVS_REPORT but act like one
                // e.g. Window explorer 
                // NOTE: in XP version the NativeMethods.LV_VIEW_DETAILS check will cover Windows 
                // Explorer, but in order to work for before XP lv we need the code below.
                IntPtr hwndHeader = ListViewGetHeader(hwnd); 

                return SafeNativeMethods.IsWindowVisible(hwndHeader);
            }
            else 
            {
                // No need to examine the listview header on Vista, since 
                // ListViewGetView() suffices. 
                return false;
            } 

         }

        // detect if listview is in list mode 
        static internal bool IsListMode (IntPtr hwnd)
        { 
            if (ListViewList(hwnd) || (NativeMethods.LV_VIEW_LIST == ListViewGetView(hwnd))) 
            {
                return true; 
            }

            return false;
        } 

        // detect if given listview should support Grid pattern 
        static internal bool IsImplementingGrid (IntPtr hwnd) 
        {
            // in the case when Group is enabled Group will support 
            // Grid pattern rather than ListView
            if (IsGroupViewEnabled (hwnd))
            {
                return false; 
            }
 
            // Rules for supporting grid pattern: 
            // 1. ListView is in detail mode
            // 2. ListView is in the list mode 
            // 3. Any other modes and LVS_AUTOARRANGE is set
            if (IsDetailMode (hwnd) || IsListMode (hwnd) || ListViewAutoArrange (hwnd))
            {
                return true; 
            }
 
            return false; 
        }
 
        // retrieve count of columns in the listview
        static internal int GetColumnCount (IntPtr hwnd)
        {
            if (IsDetailMode (hwnd)) 
            {
                int column = ListViewItem.GetSubItemCount (hwnd); 
 
                return (column <= -1) ? 0 : column;
            } 

            return GetColumnCountOtherModes (hwnd);
        }
 
        // retrieve count of rows in the listview
        static internal int GetRowCount (IntPtr hwnd) 
        { 
            if (IsDetailMode (hwnd))
            { 
                int row = GetItemCount (hwnd);

                return (row <= -1) ? 0 : row;
            } 

            return GetRowCountOtherModes (hwnd); 
        } 

        // get count of column in the non-detail lv 
        static internal int GetColumnCountOtherModes (IntPtr hwnd)
        {
            // Check for empty list
            if (GetItemCount(hwnd) <= 0) 
            {
                return 0; 
            } 

            // Algorithm for non-list mode of ListView with at least one item: 
            // Starting from the first item, count the items to the right of it.
            //
            int columnCount = 0;
            int curItem = 0; 
            while (true)
            { 
                columnCount++; 
                int nextItem = GetItemNext(hwnd, curItem, NativeMethods.LVNI_TORIGHT);
                // Expect -1 when no more items to right of current item 
                if (nextItem < 0)
                    break;
                // Guard against infinite loop (getting back the same item LH
                if (nextItem == curItem) 
                    break;
 
                // Assumption: As long as nextItem is changing everything is OK 
                // Note: Docs imply it may be possible for nextItem < curItem at this
                // point so don't assume nextItem is always increasing. 
                curItem = nextItem;
            }

            return columnCount; 
        }
 
        // get count of row for the listview when it is in the list mode 
        static internal int GetRowCountListMode (IntPtr hwnd, int itemCount)
        { 
            // NOTE: ListView in the List mode is tricky
            // In the List mode during the navigation columns getting
            // wrapped hence the number of rows we'll get by simply doing
            // LVNI_BELOW will be same as maximum number of elements 
            // Algorithm: get the item position  while doing GetItemNext(,,LVNI_BELOW)
            // as long as pt.x is the same we on the same column 
            // as soon as pt.x is changed we know we jump to the different column and hence we know the number of rows 
            // This is true except:
            // If user had Groups shown, and than changed to the List mode (List mode does not have groups) 
            // the List will not be snaking anymore (Windows Explorer LV bug on XP), hence after we come to the end of the first column
            // the GetItemNext(,,LVNI_BELOW) will return -1. all other case list will snake
            // Lucky for us at this point rowCount will contain the number of rows
            int columnCount = GetColumnCountOtherModes (hwnd); 

            if (columnCount == 1) 
            { 
                // list does not snake, number of elements == number or rows
                return itemCount; 
            }

            // We know that list has at least itemCount/columnCount rows
            int rowCount = (int) System.Math.Ceiling (((double) itemCount) / columnCount); 

            NativeMethods.Win32Point pt = new NativeMethods.Win32Point (0, 0); 
 
            // items are 0-based
            int current = rowCount - 1; 
            if (!GetItemPosition(hwnd, current, out pt))
            {
                return 0;
            } 

            int pos = pt.x; 
            while (true) 
            {
                int next = GetItemNext(hwnd, current, NativeMethods.LVNI_BELOW); 
                // Expect -1 when no more items below
                if (next == -1)
                    return rowCount;
 
                // Guard against infinite loop (LH
                if (next == current) 
                    return rowCount; 

                // Get this next item's top-left coordinate 
                if (!GetItemPosition(hwnd, next, out pt))
                    return rowCount;

                // If we're not on the same left-most x-axis we've got the row count 
                if (pos != pt.x)
                    return rowCount; 
 
                ++rowCount;
                current = next; 
            }
        }

        // detect if group view is enabled 
        internal static bool IsGroupViewEnabled (IntPtr hwnd)
        { 
            return (ListViewIsGroupViewEnabled(hwnd) && NativeMethods.LV_VIEW_LIST != ListViewGetView(hwnd)); 
        }
 
        // Events produced by the LV, that will help us working with the Group
        internal static void GroupSpecificEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        {
            // NOTE: Whenever LV goes from Group mode to non-Group mode we will be getting an event 
            //       Whenver LV goes from one Group mode to another (arrange By: Name - Size) we will be getting an event
            // BUT 
            //      Whenver LV switched between Views : Icond - Detail, events are not always getting send -> This one is ok since 
            //      it does not affect Grouping (e.g. Groups do not change), except when switching from any View (with Group on) to
            //                                   List mode (List mode does not have group) 
            //
            //      Whenver LV goes from non group mode to Group mode, events pretty much never getting generated
            //
            //      We will detect, in the code, when user got a stale logical tree and tries to use element from this tree. We will throw an exception 
            //      indicating invalid operation (e.g. InvalidOperationException("Operation cannot be performed");)
            //      If user does listens to the events, and still got the stale tree (due to the LV control not raising needed events) 
            //      the exception will be thrown in any case. The events will be raised on LH as soon as LV event-related bugs are fixed. On XP 
            //      user can catch the exception and do the needed thing, or use the verification method (should be done in M7) on LE to validate it.
            switch (eventId) 
            {
                case NativeMethods.EventObjectReorder :
                    {
                        // First check if we are in the Groupmode 
                        if (IsGroupViewEnabled (hwnd))
                        { 
                            // reorder event may  mean: 
                            // 1. What lv is grouped by changed.... (Need an update and an event to be sent)
                            // 2. View changed (e.g.icone -> tiles)     (No action needed group did no change) 
                            // 3. something as simple as resize     (No action needed)
                            // To detect case 1 we need to make sure that our ids are still valid
                            if (_groupsCollection.Contains (hwnd))
                            { 
                                GroupManager manager = _groupsCollection [hwnd];
 
                                if (!manager.AreGroupsValid ()) 
                                {
                                    // went from one arrangement to another... (e.g. Name->Size) 
                                    RemoveGroupAndRaiseLogicalChangedEvent (hwnd);
                                }
                            }
                            else 
                            {
                                // New GroupManager showed in the LV 
                                RaiseLogicalChangedEvent (hwnd); 
                            }
                        } 
                        else
                        {
                            // This can mean that Group mode got disable
                            // we need to remove the corresponding GroupManager from collection 
                            // and raise an event
                            // NOTE: Unfortunately LV will not produce event on this consistently (e.g. Any mode->List) 
                            if (_groupsCollection.Contains (hwnd)) 
                            {
                                // groups were removed 
                                RemoveGroupAndRaiseLogicalChangedEvent (hwnd);
                            }
                        }
                    } 
                    break;
 
                case NativeMethods.EventObjectDestroy : 
                    {
                        // lv is being destroyed... 
                        if (_groupsCollection.Contains (hwnd))
                        {
                            _groupsCollection.Remove (hwnd);
                            WinEventTracker.RemoveToNotificationList (hwnd, _groupEvents, null, 3); 
                        }
                    } 
                    break; 

                case NativeMethods.EventObjectHide : 
                    {
                        // alexsn : Explorer LV does not send DESTROY event during destruction, instead EVENT_OBJECT_HIDE is getting send.
                        // OBJECT_HIDE can also be raised by Explorer during Arrange Icons By (e.g. Any->Type), we do not want to "remove" groupmanager
                        // and event notification if this is the case, hence we're are checking window's visible and enable state... 
                        // There is however some timing issue: The window handle sometimes still will be visible and enabled even though OBJECT_HIDE
                        // event was raised in the response to LV going away: 
                        // Hence our "remove" code may not be executing all the time 
                        if (_groupsCollection.Contains (hwnd) && !SafeNativeMethods.IsWindowVisible (hwnd) && !SafeNativeMethods.IsWindowEnabled (hwnd))
                        { 
                            _groupsCollection.Remove (hwnd);
                            WinEventTracker.RemoveToNotificationList (hwnd, _groupEvents, null, 3);
                        }
                    } 
                    break;
            } 
        } 

        // detect if the listview is in LVS_REPORT mode 
        static internal bool InReportView (IntPtr hwnd)
        {
            return ((Misc.GetWindowStyle(hwnd) & NativeMethods.LVS_TYPEMASK) == NativeMethods.LVS_REPORT);
        } 

        // Removes group from collection 
        // and notifies client about LV tree structure change 
        static internal void RemoveGroupAndRaiseLogicalChangedEvent (IntPtr hwnd)
        { 
            // Raise logical structure changed event
            RaiseLogicalChangedEvent (hwnd);
        }
 
        // Invalidate LV tree structure
        static internal void RaiseLogicalChangedEvent (IntPtr hwnd) 
        { 
            // remove groupmanager from collection
            _groupsCollection.Remove (hwnd); 

            // Raise logical structure changed event
            IRawElementProviderFragment wlv = (IRawElementProviderFragment) new WindowsListView (hwnd, null, -1);
 
            // Note we're using MakeRuntimeId() vs IRawElementProviderFragment.GetRuntimeId().  GetRuntimeId
            // only returns the part of the RuntimeId for the subtree this provider is handling.  When returning 
            // RuntimeId for an event the entire RuntimeId is required so use MakeRuntimeId(). 
            StructureChangedEventArgs change = new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, ( (WindowsListView)wlv ).MakeRuntimeId() );
            AutomationInteropProvider.RaiseStructureChangedEvent(wlv, change); 
        }

        // get listview item count
        static internal int GetItemCount (IntPtr hwnd) 
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero); 
        } 

        // get listview item count os selected items 
        static internal int GetSelectedItemCount (IntPtr hwnd)
        {
            if (GetItemCount (hwnd) <= 0)
                return 0; 

            int count = 0; 
            for (int index = GetItemNext(hwnd, -1, NativeMethods.LVNI_SELECTED); index != -1; index = GetItemNext(hwnd, index, NativeMethods.LVNI_SELECTED)) 
            {
                count++; 
            }

            return count;
        } 

        static internal int GetStartOfSelectedItems (IntPtr hwnd) 
        { 
            return GetItemNext(hwnd, -1, NativeMethods.LVNI_SELECTED);
        } 

        // Search for the next listview item based on the passed in properties
        // pass -1 for item in order to find the first item that matches condition
        // specified by flags 
        static internal int GetItemNext (IntPtr hwnd, int item, int flags)
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETNEXTITEM, new IntPtr(item), new IntPtr(flags)); 
        }
 
        static internal bool IsIconView(IntPtr hwnd)
        {
            return ListViewGetView(hwnd) == NativeMethods.LV_VIEW_ICON;
        } 

        // Retrieves the current view of the listview control 
        static internal int ListViewGetView (IntPtr hwnd) 
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETVIEW, IntPtr.Zero, IntPtr.Zero); 
        }

        // simple version of ApproxiamateViewRect
        static internal int ApproximateViewRect (IntPtr hwnd) 
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_APPROXIMATEVIEWRECT, new IntPtr(-1), NativeMethods.Util.MAKELPARAM(-1, -1)); 
        } 

        // Scroll the content of the listview control 
        static internal bool Scroll (IntPtr hwnd, IntPtr dx, IntPtr dy)
        {
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_SCROLL, dx, dy) != 0;
        } 

        // get listview rectangle 
        static internal unsafe bool GetItemRect (IntPtr hwnd, int item, int lvir, out NativeMethods.Win32Rect itemRectangle) 
        {
            itemRectangle = NativeMethods.Win32Rect.Empty; 
            itemRectangle.left = lvir;

            fixed (int * location = &(itemRectangle.left))
            { 
                if (XSendMessage.XSend(hwnd, NativeMethods.LVM_GETITEMRECT, new IntPtr(item), new IntPtr(location), Marshal.SizeOf(itemRectangle.GetType())))
                { 
                    return Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref itemRectangle, 2); 
                }
 
                return false;
            }
        }
 
        // check if lv has a group view enabled
        internal static bool ListViewIsGroupViewEnabled (IntPtr hwnd) 
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_ISGROUPVIEWENABLED, IntPtr.Zero, IntPtr.Zero) != 0;
        } 

        // unselect all items in the listview
        static internal bool UnselectAll (IntPtr hwnd)
        { 
            return SetItemState(hwnd, -1, NativeMethods.LVIS_SELECTED, 0);
        } 
 
        // select specified listview item
        static internal bool SelectItem (IntPtr hwnd, int item) 
        {
            return SetItemState(hwnd, item, NativeMethods.LVIS_SELECTED, NativeMethods.LVIS_SELECTED);
        }
 
        // un-select specified listview item
        static internal bool UnSelectItem (IntPtr hwnd, int item) 
        { 
            return SetItemState(hwnd, item, NativeMethods.LVIS_SELECTED, 0);
        } 

        // detect if listview item selected
        static internal bool IsItemSelected (IntPtr hwnd, int listItem)
        { 
            return Misc.IsBitSet(GetItemState(hwnd, listItem, NativeMethods.LVIS_SELECTED), NativeMethods.LVIS_SELECTED);
        } 
 
        // detect if listviewitem has label that can be edited
        static internal bool ListViewEditable (IntPtr hwnd) 
        {
            return Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_EDITLABELS);
        }
 
        // detect if listviewitem can be invoked
        static internal bool ListViewInvokable(IntPtr hwnd) 
        { 
            int style = GetExtendedListViewStyle(hwnd);
 
            // Listview documentation suggests LVS_EX_ONECLICKACTIVATE or LVS_EX_TWOCLICKACTIVATE
            // indicate an item that can be activated, but Explorer listview items do not provide
            // either of these flags.  They do however contain the values LVS_EX_UNDERLINEHOT and
            // LVS_EX_UNDERLINECOLD.  Documentation for these flags indicate they're specifically 
            // for items that can be activated, and that they have no impact if one of the other
            // two values is not set: 
            // 
            // LVS_EX_UNDERLINEHOT
            // Causes those hot items that may be activated to be displayed with underlined text. 
            //
            // LV_EX_UNDERLINE_COLD
            // Causes those non-hot items that may be activated to be displayed with underlined text.
            // 
            // This code tests for both sets of styles since the presence of the HOT or COLD bits
            // implies items that can be activated and supports our observations of Explorer. 
 
            int flags = NativeMethods.LVS_EX_ONECLICKACTIVATE
                | NativeMethods.LVS_EX_TWOCLICKACTIVATE 
                | NativeMethods.LVS_EX_UNDERLINEHOT
                | NativeMethods.LVS_EX_UNDERLINECOLD;

            return ((style & flags) != 0); 
        }
 
        static internal IntPtr ListViewEditLabel(IntPtr hwnd, int item) 
        {
            return Misc.ProxySendMessage(hwnd, NativeMethods.LVM_EDITLABEL, new IntPtr(item), IntPtr.Zero); 
        }

        // detect if listview enables item activation with one click
        static internal bool ListViewSingleClickActivate (IntPtr hwnd) 
        {
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_ONECLICKACTIVATE); 
        } 

        // detect if listview supports multiple selection 
        static internal bool MultiSelected (IntPtr hwnd)
        {
            return !Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_SINGLESEL);
        } 

        // detect if listview contains or potential may contain scrollbar 
        static internal bool Scrollable (IntPtr hwnd) 
        {
            return !Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_NOSCROLL); 
        }

        // ensure listview item visibility
        static internal bool EnsureVisible (IntPtr hwnd, int item, bool partialOK) 
        {
            IntPtr partialVisible = (partialOK) ? IntPtr.Zero : new IntPtr (1); 
 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_ENSUREVISIBLE, new IntPtr(item), partialVisible) != 0;
        } 

        // return listview header
        static internal IntPtr ListViewGetHeader (IntPtr hwnd)
        { 
            return Misc.ProxySendMessage(hwnd, NativeMethods.LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
        } 
 
        // retrieve listview item text
        static internal string GetItemText (IntPtr hwnd, NativeMethods.LVITEM item) 
        {
            item.cchTextMax = Misc.MaxLengthNameProperty;

            return XSendMessage.GetItemText(hwnd, item); 
        }
 
        // perform a hit test on the specific point 
        // POINT is in screen coordinates
        static internal NativeMethods.LVHITTESTINFO_INTERNAL SubitemHitTest (IntPtr hwnd, NativeMethods.Win32Point pt) 
        {
            return SubitemHitTest (hwnd, 0, pt);
        }
 
        // perform a hit test on the specific point
        // POINT is in screen coordinates 
        static internal NativeMethods.LVHITTESTINFO_INTERNAL SubitemHitTest (IntPtr hwnd, int item, NativeMethods.Win32Point pt) 
        {
            // Allocate a local LVHITTESTINFO struct. 
            NativeMethods.LVHITTESTINFO_INTERNAL hitTest = new NativeMethods.LVHITTESTINFO_INTERNAL ();

            // Set the point of interest.
            hitTest.pt = pt; 
            hitTest.iItem = item;
 
            int result = -1; 

            // convert to client 
            if (Misc.MapWindowPoints(IntPtr.Zero, hwnd, ref hitTest.pt, 1))
            {
                unsafe
                { 
                    // Send the LVM_SUBITEMHITTEST message to the list view owner process.
                    // This is ok to do even for non LVS_REPORT listview, since in that case this 
                    // message will behaive like LVM_HITTEST 
                    if (Misc.IsComctrlV6OnOsVerV6orHigher(hwnd))
                    { 
                        NativeMethods.LVHITTESTINFO_V6 hitTestNative = new NativeMethods.LVHITTESTINFO_V6(hitTest);
                        result = XSendMessage.XSendGetIndex(hwnd, NativeMethods.LVM_SUBITEMHITTEST, IntPtr.Zero, new IntPtr(&hitTestNative), Marshal.SizeOf(hitTestNative.GetType()));
                        hitTest.flags = hitTestNative.flags;
                        hitTest.iItem = hitTestNative.iItem; 
                        hitTest.iGroup = hitTestNative.iGroup;
                    } 
                    else 
                    {
                        NativeMethods.LVHITTESTINFO hitTestNative = new NativeMethods.LVHITTESTINFO(hitTest); 
                        result = XSendMessage.XSendGetIndex(hwnd, NativeMethods.LVM_SUBITEMHITTEST, IntPtr.Zero, new IntPtr(&hitTestNative), Marshal.SizeOf(hitTestNative.GetType()));
                        hitTest.flags = hitTestNative.flags;
                        hitTest.iItem = hitTestNative.iItem;
                    } 
                }
            } 
 
            if (result == -1)
            { 
                hitTest.iSubItem = hitTest.iItem = -1;
            }

            return hitTest; 
        }
 
        // retrieve count of header items 
        static internal int HeaderItemCount (IntPtr hwnd)
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.HDM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
        }

        // detect if the listview support checkboxes 
        static internal bool CheckBoxes (IntPtr hwnd)
        { 
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_CHECKBOXES); 
        }
 
        // get listview item check state
        static internal int GetCheckedState (IntPtr hwnd, int item)
        {
            int state = GetItemState(hwnd, item, NativeMethods.LVIS_STATEIMAGEMASK); 

            return ((state >> 12) - 1); 
        } 

        // detect if listview is auto-arranged 
        static internal bool ListViewAutoArrange (IntPtr hwnd)
        {
            return Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.LVS_AUTOARRANGE);
        } 

        // detect if listview supports full row selection 
        static public bool FullRowSelect (IntPtr hwnd) 
        {
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_FULLROWSELECT); 
        }

        // detects if icons are lined up in columns that use up the whole view area
        static public bool HasJustifyColumnsExStyle(IntPtr hwnd) 
        {
            return Misc.IsBitSet(GetExtendedListViewStyle(hwnd), NativeMethods.LVS_EX_JUSTIFYCOLUMNS); 
        } 

        // gets rectangle of the subitem. 
        // This method is inteded to be used with the LVS_REPORT lv
        static public unsafe bool GetSubItemRect (IntPtr hwnd, int item, int subItem, int lvir, out NativeMethods.Win32Rect itemRectangle)
        {
            itemRectangle = NativeMethods.Win32Rect.Empty; 
            itemRectangle.left = lvir;
            itemRectangle.top = subItem; 
 
            fixed (int * location = &(itemRectangle.left))
            { 
                if (XSendMessage.XSend(hwnd, NativeMethods.LVM_GETSUBITEMRECT, new IntPtr(item), new IntPtr(location), Marshal.SizeOf(itemRectangle.GetType())))
                {
                    return Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref itemRectangle, 2);
                } 

                return false; 
            } 
        }
 
        static internal string GetItemToolTipText(IntPtr hwnd)
        {
            IntPtr hwndToolTip = Misc.ProxySendMessage(hwnd, NativeMethods.LVM_GETTOOLTIPS, IntPtr.Zero, IntPtr.Zero);
 
            return Misc.GetItemToolTipText(hwnd, hwndToolTip, 0);
        } 
 
        #endregion Internal Methods
 
        //-----------------------------------------------------
        //
        //  Internal Fields
        // 
        //------------------------------------------------------
 
        #region Internal Fields 

        internal readonly static GroupManagerCollection _groupsCollection = new GroupManagerCollection(); 
        // alexsn Used for MultipleView Pattern, until official table
        // will not be finalyzed
        //
 
        internal static string [] ListViewViews = new string [] {
                "Icons", "Details", "Smallicon", "List", "Tiles" 
            }; 

        #endregion Internal Fields 

        //-----------------------------------------------------
        //
        //  Protected Methods 
        //
        //----------------------------------------------------- 
 
        #region Protected Methods
 
        // Picks a WinEvent to track for a UIA property
        protected override int [] PropertyToWinEvent (AutomationProperty idProp)
        {
            if (idProp == ValuePattern.ValueProperty) 
            {
                return CheckBoxes (_hwnd) ? new int [] { NativeMethods.EventObjectNameChange, NativeMethods.EventObjectStateChange } : new int [] { NativeMethods.EventObjectNameChange }; 
            } 
            else if (idProp == GridPattern.ColumnCountProperty || idProp == GridPattern.RowCountProperty || idProp == GridItemPattern.ColumnProperty || idProp == GridItemPattern.RowProperty)
            { 
                return new int [] { NativeMethods.EventObjectReorder };
            }
            return base.PropertyToWinEvent (idProp);
        } 

        #endregion Protected Methods 
 
        //-----------------------------------------------------
        // 
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        // Create a Listview item proxy 
        private ProxyFragment CreateListViewItem(int index)
        { 
            return CreateListViewItemOrStartMenuItem(this, index);
        }

        // Create a group for the listview 
        private ProxyFragment CreateListViewGroup (int groupID)
        { 
            return new WindowsListViewGroup (_hwnd, this, groupID); 
        }
 
        // Create a Listview item proxy, check if the item is enclosed in a group.
        // If it is the case, then create also the ListViewGroup and do the parenting
        // properly.
        private ProxySimple CreateListViewItemCheckIfInGroup (int item) 
        {
            // if LV is in Group mode, lvitems will live under the corresponding group 
            bool hasGroup = IsGroupViewEnabled (_hwnd); 

            if ((!hasGroup) && GetItemCount(_hwnd) > 0) 
            {
                return CreateListViewItemOrStartMenuItem(this, item);
            }
 
            // Navigate to the first group
            if (hasGroup) 
            { 
                _groupsCollection.EnsureCreation(_hwnd);
 
                GroupManager manager = _groupsCollection[_hwnd];
                int[] groupIds = manager.GetGroupIds();

                // if there are no groups this is an empty list 
                if (groupIds.Length == 0)
                { 
                    return null; 
                }
 
                // Loop through all the groups to figure out in which group this item belongs
                foreach (int groupId in groupIds)
                {
                    GroupManager.GroupInfo gi = manager.GetGroupInfo (groupId); 

                    if (gi.IndexOf (item) != -1) 
                    { 
                        return CreateListViewItemOrStartMenuItem(new WindowsListViewGroup(_hwnd, this, groupId), item);
                    } 
                }

                // Could not find an item, must be a groupId.
                return new WindowsListViewGroup (_hwnd, this, item); 
            }
 
            // no content go for the scrollbars 
            return null;
        } 

        private ProxyFragment CreateListViewItemOrStartMenuItem(ProxyFragment parent, int item)
        {
            // ListView items on the Start Menu are special.  The IAccessible is needed to get 
            // information from these special ListView items.  If a valid IAccessible can not be
            // obtained default to the normal ListView item. 
            if (InStartMenu() && AccessibleObject != null) 
            {
                ProxyFragment proxyFragment = new ListViewItemStartMenu(_hwnd, parent, item, AccessibleObject); 
                if (proxyFragment != null)
                {
                    proxyFragment.AccessibleObject = AccessibleObject;
                } 
                return proxyFragment;
            } 
            else 
            {
                return new ListViewItem(_hwnd, parent, item); 
            }
        }

        // This method maybe OS depended.  (Most likely will change in Longhorn.  In Longhorn we may not need this method.) 
        private bool InStartMenu()
        { 
            string className = Misc.GetClassName(Misc.GetParent(_hwnd)); 
            return string.Compare(className, "DesktopSFTBarHost", StringComparison.OrdinalIgnoreCase) == 0;
        } 

        private bool SetScrollPercent(double fScrollPos, int sbFlag, int cPelsAll, out int delta)
        {
            // in case of early exit no move 
            delta = 0;
 
            // Check param 
            if ((int)fScrollPos == (int)ScrollPattern.NoScroll)
            { 
                return true;
            }

            if (fScrollPos < 0 || fScrollPos > 100) 
            {
                throw new ArgumentOutOfRangeException(sbFlag == NativeMethods.SB_HORZ ? "horizontalPercent" : "verticalPercent", SR.Get(SRID.ScrollBarOutOfRange)); 
            } 

            int scrollBar = sbFlag == NativeMethods.SB_HORZ ? NativeMethods.OBJID_HSCROLL : NativeMethods.OBJID_VSCROLL; 

            NativeMethods.ScrollBarInfo scrollBarInfo = new NativeMethods.ScrollBarInfo();
            scrollBarInfo.cbSize = Marshal.SizeOf(scrollBarInfo.GetType());
            if (!Misc.GetScrollBarInfo(_hwnd, scrollBar, ref scrollBarInfo) || 
                (scrollBarInfo.scrollBarInfo & NativeMethods.STATE_SYSTEM_INVISIBLE) != 0 ||
                (scrollBarInfo.scrollBarInfo & NativeMethods.STATE_SYSTEM_UNAVAILABLE) != 0) 
            { 
                return false;
            } 

            // Get scroll range
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo (); // this is used all over
 
            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_ALL; 
 
            // if no scroll bar return false
            // on Win 6.0 success is false 
            // on other system check through the scroll info is a scroll bar is there
            if (!Misc.GetScrollInfo(_hwnd, sbFlag, ref si) ||
                !((si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1)))
            { 
                return false;
            } 
 
            // calculate user-requested thumb position
            int deltaPage = (si.nPage > 0) ? si.nPage - 1 : 0; 
            int future = (int) Math.Round (((si.nMax - deltaPage) - si.nMin) * fScrollPos / 100.0 + si.nMin);

            // delta between current and user-requested position in pixels
            // since the cPelsAll contains the dimension in pels for all items + the 2 pels of the border 
            // the operation below does a trunc on purpose
            delta = (future - si.nPos) * (cPelsAll / (si.nMax + 1 - si.nMin)); 
 
            return true;
        } 

        // Grid.GetCell implementation for detail mode
        private IRawElementProviderSimple GetCellInDetailMode (int row, int column)
        { 
            // NOTE: In Detail mode the is no empty cells
            ProxyFragment lvItem = CreateListViewItem (row); 
 
            return new ListViewSubItem (_hwnd, lvItem, column, row);
        } 
        // Grid.GetCell implementation for lv that is not in detail mode
        private IRawElementProviderSimple GetCellInOtherModes (int row, int column, int maxColumn, int maxRow)
        {
            // Assumption: passed in arguments were already verified 
            // NOTE: cells at the end might be empty
            int itemCount = GetItemCount (_hwnd); 
            int itemIndex = 0; 

            if (IsListMode (_hwnd)) 
            {
                // calculate item's index
                itemIndex = column * maxRow + row;
            } 
            else
            { 
                // SIcon, LIcon, Title, Thumbnails 
                itemIndex = row * maxColumn + column;
            } 

            // verify the cell exists
            if (itemIndex >= itemCount)
            { 
                // Return an empty cell
                return new EmptyGridItem (row, column, this); 
            } 

            // return cell 
            return CreateListViewItem (itemIndex);
        }

        // get count of rows in the non-detail lv 
        static private int GetRowCountOtherModes (IntPtr hwnd)
        { 
            // Assumption: items are autoarranged 
            int count = GetItemCount(hwnd);
 
            // Check for empty list
            if (count <= 0)
            {
                return 0; 
            }
 
            if (IsListMode(hwnd)) 
            {
                return GetRowCountListMode(hwnd, count); 
            }

            // Algorithm for non-list mode of ListView with at least one item:
            // Starting from the first item, count the items below it. 
            int rowCount = 0;
            int curItem = 0; 
            while (true) 
            {
                rowCount++; 
                int nextItem = GetItemNext(hwnd, curItem, NativeMethods.LVNI_BELOW);
                // Expect -1 when no more items below current item
                if (nextItem < 0)
                    break; 
                // Guard against infinite loop (getting back the same item LH
                if (nextItem == curItem) 
                    break; 

                // Assumption: As long as nextItem is changing everything is OK 
                // Note: Docs imply it may be possible for nextItem < curItem at this
                // point so don't assume nextItem is always increasing.
                curItem = nextItem;
            } 

            return rowCount; 
        } 

        // detect if the listview has a list style 
        static private bool ListViewList (IntPtr hwnd)
        {
            return ((Misc.GetWindowStyle(hwnd) & NativeMethods.LVS_TYPEMASK) == NativeMethods.LVS_LIST);
        } 

        // get top-left point of the listview item 
        static private unsafe bool GetItemPosition (IntPtr hwnd, int item, out NativeMethods.Win32Point pt) 
        {
            pt.x = 0; 
            pt.y = 0;

            fixed (int * location = &(pt.x))
            { 
                if (XSendMessage.XSend(hwnd, NativeMethods.LVM_GETITEMPOSITION, new IntPtr(item), new IntPtr(location), Marshal.SizeOf(pt.GetType())))
                { 
                    return Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref pt, 1); 
                }
 
                return false;
            }
        }
 
        // Get listview extended styles
        static private int GetExtendedListViewStyle (IntPtr hwnd) 
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETEXTENDEDLISTVIEWSTYLE, IntPtr.Zero, IntPtr.Zero);
        } 

        // retrieve specific "state" of the listview item
        private static int GetItemState (IntPtr hwnd, int item, int stateMask)
        { 
            return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_GETITEMSTATE, new IntPtr(item), new IntPtr(stateMask));
        } 
        // set listview item state 
        private static bool SetItemState (IntPtr hwnd, int item, int stateMask, int state)
        { 
            NativeMethods.LVITEM lvitem = new NativeMethods.LVITEM ();

            lvitem.mask = NativeMethods.LVIF_STATE;
            lvitem.state = state; 
            lvitem.stateMask = stateMask;
 
            return XSendMessage.SetItem(hwnd, item, lvitem); 
        }
 
        // Check if the point on the screen is part of the header
        private bool PtInListViewHeader (int x, int y)
        {
            // See if header exist 
            IntPtr hwndHeader = ListViewGetHeader (_hwnd);
 
            if (hwndHeader != IntPtr.Zero && SafeNativeMethods.IsWindowVisible (hwndHeader)) 
            {
                if (Misc.PtInWindowRect(hwndHeader, x, y)) 
                {
                    return true;
                }
            } 

            return false; 
        } 

 
        private static void RaiseEventsOnClient(IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        {
            ProxySimple el = null;
 
            WindowsListView wlv = new WindowsListView (hwnd, null, -1);
            AutomationProperty automationProperty = idProp as AutomationProperty; 
            AutomationEvent automationEvent = idProp as AutomationEvent; 

            if (eventId == NativeMethods.EventObjectSelectionRemove && automationProperty == SelectionItemPattern.IsSelectedProperty) 
            {
                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1);
                if (el != null)
                { 
                    el.DispatchEvents(eventId, idProp, idObject, idChild);
                    return; 
                } 
            }
            else if (eventId == NativeMethods.EventObjectSelection 
            || eventId == NativeMethods.EventObjectSelectionRemove
            || eventId == NativeMethods.EventObjectSelectionAdd)
            {
                // This is the speced behavior for events and selection 
                //
                // The following rule should be used to decide when to fire Selected vs Add/Remove events: 
                // If the result of a SelectElement or an AddElementToSelection is that a single item is selected, 
                // then send a Select for that element; otherwise send Add/Removes as appropriate.
                // Note that this rule does not depend on whether the selection container is single- or multi- select, 
                // or on what method was used to change the selection. Only the result matters.
                // Overall message to clients (test automation and assistive technologies)
                //
                // 1) If you receive ElementSelectedEvent this guarantees that the element that raised the 
                //    event is the only selected element in that container.
                // 2) If you receive ElementAddedToSelectionEvent this guarantees that those items are added 
                //    to selection and the end result of the selection is more than one item. 
                // 3) If you receive ElementRemovedFromSelectionEvent this guarantees that those items are
                //    deselected and the end result is NOT one item selected. 
                //
                // For the listview adhering to the spec is not possible because of an ambiguity with the winevents that
                // are fired.  This code is trying to map the winevents received to the UIAutomaiton events that are expected.
                // These are the two cases that are ambiguous: 
                //
                // Case 1: 
                // The user clicks two different items in succession. 
                // We get an EventObjectSelectionRemove WinEvent for each item that loses
                // selection and in addition an EventObjectSelection for the new item that got selection. 
                // In this case we want to disregard the EventObjectSelectionRemove (not raise UIA
                // ElementRemovedFromSelectionEvent) because the end result of this scenario is that only one item is selected.
                //
                // Case 2: 
                // If the ListView is multi-select and there are two items selected and the user cntl clicks on one (unselects it).
                // The listview fires only one WinEvent (an EventObjectSelectionRemove) for the 
                // item that was removed.   In this case since there is only one 
                // item left selected UIA should raise ElementSelectedEvent for the remaining item.
                // 
                // If we turn the EventObjectSelectionRemove WinEvent in case 2 into a ElementSelected
                // event for the remaining element we would end up firing two events for each click in a multi
                // select list.  This is because the EventObjectSelectionRemove in case 1 is just like
                // the one case 2.  Except that in case 1 we also get a EventObjectSelection separately so we 
                // would end up firing another selected event.
                // 
                // It has been decided that it is preferred to receive extra EventObjectSelection events 
                // then receiving EventObjectSelectionRemove events at the wrong time. If two items are selected
                // in a listview and the user clicks on one of them the only event winevent we get is a remove so 
                // we have to convert removed winevent to selected event or we would miss an event.  So it better
                // to have multiple events in some case than none when there should be ([....] 9/8/2004).
                //
                if (eventId == NativeMethods.EventObjectSelectionRemove && GetSelectedItemCount(hwnd) == 1) 
                {
                    if (MultiSelected(hwnd)) 
                    { 
                        // Change the EventObjectSelectionRemove to an EventObjectSelection.
                        eventId = NativeMethods.EventObjectSelection; 
                        idProp = SelectionItemPattern.ElementSelectedEvent;

                        // Change the child id to the selected child.
                        int item = GetStartOfSelectedItems(hwnd); 
                        if (item > -1)
                        { 
                            idChild = item + 1; 
                        }
                    } 
                    else
                    {
                        // Since case 2 does not apply to single selection listviews, suppress the
                        // EventObjectSelectionRemove. 
                        return;
                    } 
                } 

                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 
            }
            // GridItem case
            else if (eventId == NativeMethods.EventObjectReorder && (automationProperty == GridItemPattern.ColumnProperty || automationProperty == GridItemPattern.RowProperty))
            { 
                // GridItem case. We need to recursively call all of the list items
                for (el = wlv.GetFirstChild(); el != null; el = wlv.GetNextSibling(el)) 
                { 
                    el.DispatchEvents(eventId, idProp, idObject, idChild);
                } 
                return;
            }
            // Map the WinEvent NameChange to ValueChange to go through the dispatch
            else if (eventId == NativeMethods.EventObjectNameChange) 
            {
                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 
                eventId = NativeMethods.EventObjectValueChange; 
            }
            // Change of state for the check box must generates a StateChange Win Events. 
            // Map it to of ObjectChange for the checkbox
            else if (eventId == NativeMethods.EventObjectStateChange && CheckBoxes(hwnd))
            {
                el = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 

                el = ((ProxyFragment)el).GetFirstChild(); 
                eventId = NativeMethods.EventObjectValueChange; 

                // Assert if the assumption that the first child is a check box is false 
                System.Diagnostics.Debug.Assert(el is ListViewItemCheckbox);
            }
            // Special case for logical element change for a list view item
            else if ((eventId == NativeMethods.EventObjectDestroy || eventId == NativeMethods.EventObjectCreate) && automationEvent == AutomationElement.StructureChangedEvent) 
            {
                ProxySimple parent = wlv; 
                bool fGroupView = IsGroupViewEnabled(hwnd); 

                // Allways disable the groups as one may have been created or 
                // destroyed
                if (fGroupView)
                {
                    // remove groupmanager from collection 
                    _groupsCollection.Remove(hwnd);
 
                    // If it is an object creation, create the element and picks 
                    // its parent to invalidate. The parent can be either a group
                    // or  the listview itself. 
                    if (eventId == NativeMethods.EventObjectCreate && fGroupView)
                    {
                        // Get the item with the resetted collection of groups (may be null if the group is empty)
                        ProxySimple lvi = wlv.CreateListViewItemCheckIfInGroup(idChild - 1); 
                        if (lvi != null)
                            parent = lvi.GetParent(); 
                    } 
                }
 
                // If the element destroyed is in a group invalidate the whole listview as we have no
                // idea the element was part of before
                // Since children are referenced by position in the tree, addition and removal
                // of items leads to different results when asking properties for the same element 
                // On removal, item + 1 is now item!
                // Use Children Invalidated to let the client knows that all the cached children are invalid 
                AutomationInteropProvider.RaiseStructureChangedEvent( parent, new StructureChangedEventArgs( StructureChangeType.ChildrenInvalidated, parent.MakeRuntimeId() ) ); 
                return;
            } 
            else
            {
                el = wlv;
            } 

            if (el != null) 
            { 
                el.DispatchEvents(eventId, idProp, idObject, idChild);
            } 

            return;
        }
 
        #endregion Private Methods
 
        //----------------------------------------------------- 
        //
        //  Private Fields 
        //
        //------------------------------------------------------

        #region Private Fields 

        // group specific events. Used for internal tracking 
        private readonly static WinEventTracker.EvtIdProperty [] _groupEvents; 

        #endregion Private Fields 
    }

}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK