Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / WindowsListViewGroupHelper.cs / 1305600 / WindowsListViewGroupHelper.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Windows ListView Group helper classes // // History: // alexsn - Created (in DotNet) // 2003/08/08 - alexsn Updated for WCP // //--------------------------------------------------------------------------- using System; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows; using System.Collections; using System.Runtime.InteropServices; using System.ComponentModel; using MS.Win32; namespace MS.Internal.AutomationProxies { // Class representing collection of ListView GroupManagers class GroupManagerCollection { //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods // Ensures GroupManager creation for the specified listview // This method will be called only from certain methods on the LV // Called from LV: FirstChild, LastChild, ElementFromPoint, GetFocus internal void EnsureCreation(IntPtr hwnd) { if (!Contains(hwnd)) { _groupManagers[hwnd] = GroupManager.CreateGroupManager(hwnd); } } internal void Remove(IntPtr hwnd) { _groupManagers.Remove(hwnd); } // O(1) internal bool Contains(IntPtr hwnd) { return _groupManagers.ContainsKey(hwnd); } internal GroupManager this[IntPtr hwnd] { get { if (!WindowsListView.IsGroupViewEnabled(hwnd)) { // Group was disabled but we did not get the needed event // since for some things events are not being sent // (e.g: Going from some LV modes to - List mode, List mode does not have Groups) // alexsn @ throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // The group may have been discarded by the reorder winevent. EnsureCreation (hwnd); GroupManager manager = _groupManagers[hwnd] as GroupManager; if (manager == null) { // E.G. Going from the List mode to the something that has Group will cause this // alexsn @ throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } return manager; } } #endregion Internal Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields static private Hashtable _groupManagers = new Hashtable(10); #endregion Private Fields } // Class responsible for managing listview groups class GroupManager { // ------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructor private GroupManager(int groups, IntPtr hwnd, bool isComctrlV6OnOsVerV6orHigher) { _groups = new ArrayList(groups); _hwnd = hwnd; _isComctrlV6OnOsVerV6orHigher = isComctrlV6OnOsVerV6orHigher; } #endregion Constructor //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods internal NativeMethods.Win32Rect GetGroupRc(int id) { Group group = GetGroup(id); if (group == null) { return NativeMethods.Win32Rect.Empty; } return GetGroupRcInternal(group); } internal NativeMethods.Win32Rect GetGroupRcByIndex(int index) { if (index >= _groups.Count) { return NativeMethods.Win32Rect.Empty; } return GetGroupRcInternal((Group)_groups[index]); } internal int[] GetGroupIds() { int count = _groups.Count; int[] groupIds = new int[count]; for (int i = 0; i < count; i++) { groupIds[i] = ((Group)_groups[i])._groupID; } return groupIds; } internal bool IsGroupIdValid(int groupID) { int count = _groups.Count; for (int i = 0; i < count; i++) { Group group = (Group)_groups[i]; if (group._groupID == groupID) { return true; } } return false; } internal int GetGroupIdByIndex(int index) { if (index >= _groups.Count) { return -1; } return ((Group)_groups[index])._groupID; } internal int GroupCount() { return _groups.Count; } internal bool AreGroupsValid() { int count = _groups.Count; for (int i = 0; i < count; i++) { Group group = (Group)_groups[i]; if (!ListViewHasGroup(_hwnd, group._groupID)) { return false; } } // Make sure that no new group have been added, try to match all the GroupId to an // existing one. int itemCount = WindowsListView.GetItemCount (_hwnd); NativeMethods.LVITEM_V6 item = new NativeMethods.LVITEM_V6 (); item.mask = NativeMethods.LVIF_GROUPID; for (item.iItem = 0; item.iItem < itemCount; item.iItem++) { if (!XSendMessage.GetItem(_hwnd, ref item) || GetGroup(item.iGroupID) == null) { return false; } } return true; } internal GroupInfo GetGroupInfo(int groupID) { Group group = GetGroup(groupID); if (group != null) { return new GroupInfo(group.Items, group.Count); } // empty group info return GroupInfo.Null; } internal static GroupManager CreateGroupManager(IntPtr hwnd) { return InitializeManager(hwnd); } // detect whether the lv has a specified group internal static bool ListViewHasGroup(IntPtr hwnd, int groupID) { return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_HASGROUP, new IntPtr(groupID), IntPtr.Zero) != 0; } #endregion Internal Methods //----------------------------------------------------- // // Internal Fields // //----------------------------------------------------- #region Internal Fields // structure containing group information internal struct GroupInfo { //----------------------------------------------------- // // Constructor // //------------------------------------------------------ #region Constructor internal GroupInfo(int[] items, int count) { _items = items; // OK to do an assignment here, instead of deep copy _count = count; } #endregion Constructor //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region Public Methods static public bool operator true(GroupInfo info) { return info._items != null; } static public bool operator false(GroupInfo info) { return info._items == null; } static public bool operator !(GroupInfo info) { if (info) { return false; } return true; } #endregion Public Methods //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods internal int IndexOf(int item) { int index = 0; while (index < _count) { if (_items[index] == item) { return index; } index++; } return -1; } #endregion Internal Methods //------------------------------------------------------ // // Internal Fields // //----------------------------------------------------- #region Internal Fields internal int[] _items; internal int _count; static readonly internal GroupInfo Null = new GroupInfo(null, -1); #endregion Internal Fields } // collection of groups internal ArrayList _groups; // list hwnd internal IntPtr _hwnd; #endregion Internal Fields //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- #region Private Methods private Group GetGroup(int id) { int count = _groups.Count; Group current = null; for (int i = 0; i < count; i++) { current = (Group)(_groups[i]); if (current._groupID == id) { return current; } } return null; } private static unsafe GroupManager InitializeManager(IntPtr hwnd) { bool isComctrlV6OnOsVerV6orHigher = Misc.IsComctrlV6OnOsVerV6orHigher(hwnd); int itemCount = WindowsListView.GetItemCount(hwnd); NativeMethods.LVITEM_V6 item = new NativeMethods.LVITEM_V6(); item.mask = NativeMethods.LVIF_GROUPID; // The only place where the GroupManager gets constructed GroupManager manager = new GroupManager(itemCount, hwnd, isComctrlV6OnOsVerV6orHigher); if (isComctrlV6OnOsVerV6orHigher) { NativeMethods.LVITEMINDEX ii = new NativeMethods.LVITEMINDEX(-1, -1); int flags = NativeMethods.LVNI_VISIBLEONLY | NativeMethods.LVNI_VISIBLEORDER; // When a listview is being "grouped by" an item may be in more than one group. The itemCount // is the number of unique items. This loop may iterate for more than the unique items in the group. // We are taking advantage of that fact the the array list will expand if there are alot of duplicates. while (XSendMessage.XSend (hwnd, NativeMethods.LVM_GETNEXTITEMINDEX, new IntPtr(&ii), flags, Marshal.SizeOf(ii.GetType()))) { // need to convert the item id to a group id NativeMethods.LVGROUP_V6 groupInfo = new NativeMethods.LVGROUP_V6(); groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6))); groupInfo.mask = NativeMethods.LVGF_GROUPID; bool lresult = XSendMessage.XSend(hwnd, NativeMethods.LVM_GETGROUPINFOBYINDEX, new IntPtr(ii.iGroup), new IntPtr(&groupInfo), Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6))); if (!lresult) { if (groupInfo.iGroupID == -1) { // A -1 here means that there are no duplicates in this grouped listview so // we have to get the group the old way. This is done for performance reasons. break; } // no group for this item should never happen. // If it ever does the other items might ok so just keep going. continue; } if (!manager.Add(groupInfo.iGroupID, ii.iItem)) { // we had problem adding item to the needed group at this point it makes no // sense to continue System.Diagnostics.Debug.Assert(false, "Cannot add item to the needed group"); return null; } } } bool sortNeeded = false; // If the code above did not yield anything try this way. This will work for // listviews pre Vista and grouped listviews in vista that don't have duplicate items. if (manager.GroupCount() == 0) { // if we get the groups this way they need to be sorted. The code above brings them in sorted. sortNeeded = true; int current = 0; while (current < itemCount) { item.iItem = current; if (XSendMessage.GetItem(hwnd, ref item) && manager.Add(item.iGroupID, item.iItem)) { current++; } else { // we had problem adding item to the needed group at this point it makes no // sense to continue System.Diagnostics.Debug.Assert(false, "Cannot add item to the needed group"); return null; } } } // Sort items within the group int groupsCount = manager.GroupCount(); for (int i = 0; i < groupsCount; i++) { Group group = (Group)manager._groups[i]; Array.Sort(group.Items, 0, group.Count, new SortGroupItems(hwnd)); } // Depending on how we got the group info we may need to sort it. // In vista the the listview can put the list items in the correct order. // Pre vista or old ui (v5) will always need to be sorted. if (sortNeeded) { // Sort groups manager._groups.Sort(new SortGroups(hwnd)); } return manager; } private unsafe int GetGroupHeaderHeight() { NativeMethods.LVGROUPMETRICS metric = new NativeMethods.LVGROUPMETRICS (sizeof(NativeMethods.LVGROUPMETRICS), NativeMethods.LVGMF_BORDERSIZE); XSendMessage.XSend(_hwnd, NativeMethods.LVM_GETGROUPMETRICS, IntPtr.Zero, new IntPtr(&(metric.cbSize)), metric.cbSize, XSendMessage.ErrorValue.NoCheck); return metric.Top + padding; } private bool Add(int id, int item) { Group group = GetGroup(id); if (group == null) { group = new Group(id, _hwnd, _isComctrlV6OnOsVerV6orHigher); _groups.Add(group); } // group already exist, simply add an item to it return group.Add(item); } // Retrieve the rect of the group. private NativeMethods.Win32Rect GetGroupRcInternal(Group group) { NativeMethods.Win32Rect rcGroup = group.GetGroupRect(); if (rcGroup.IsEmpty) { // LVM_GETGROUPRECT failed. rcGroup = group.CalculateRectNoHeader(); // add the header's height hence changing the rcGroup.top coordinate // increase top by subtracting header from it rcGroup.top -= GetGroupHeaderHeight(); } return rcGroup; } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // Group header padding private const int padding = 12; private bool _isComctrlV6OnOsVerV6orHigher; #endregion Private Fields //------------------------------------------------------ // // Nested classes // //------------------------------------------------------ #region Nested classes // Implementation of IComparer used to // sort groups. // Note: we want to have groups sorted in the way they displayed to the user // Hence we cannot sort by group id, since if group has the lower id it does not mean // that it displayed before the group with the higher id. // Hence we will sort groups by the rect of the first item within the group private class SortGroups : IComparer { //----------------------------------------------------- // // Constructors // //------------------------------------------------------ #region Constructors internal SortGroups(IntPtr hwnd) { _hwnd = hwnd; } #endregion Constructor //----------------------------------------------------- // // IComparer // //----------------------------------------------------- #region IComparer int IComparer.Compare(object x, object y) { Group g1 = (Group)x; Group g2 = (Group)y; // piggy back on the SortGroupItem.Compare SortGroupItems helper = new SortGroupItems(_hwnd); return ((IComparer)helper).Compare(g1.Items[0], g2.Items[0]); } #endregion IComparer //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ #region Private Fields private IntPtr _hwnd; #endregion Private Fields } // Implementation of IComparer used to // sort items within group based on their rectangle // NOTE: After the sort the listviewitems DIRECTION in array will be the following (except when in Detail mode) // 0 1 2 // 3 4 5 // 6 7 private class SortGroupItems : IComparer { //----------------------------------------------------- // // Constructors // //------------------------------------------------------ #region Constructors internal SortGroupItems(IntPtr hwnd) { _hwnd = hwnd; } #endregion Constructor //------------------------------------------------------ // // IComparer // //----------------------------------------------------- #region IComparer int IComparer.Compare(object x, object y) { int item1 = (int)x; int item2 = (int)y; // get the rect of 2 items NativeMethods.Win32Rect rc1; WindowsListView.GetItemRect(_hwnd, item1, NativeMethods.LVIR_BOUNDS, out rc1); NativeMethods.Win32Rect rc2; WindowsListView.GetItemRect(_hwnd, item2, NativeMethods.LVIR_BOUNDS, out rc2); // compare rectangles if (rc1.left < rc2.left || rc1.top < rc2.top) { return -1; } else if (rc1.left != rc2.left || rc1.top != rc2.top) { return 1; } return 0; } #endregion IComparer //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private IntPtr _hwnd; #endregion Private Fields } // Class describing single ListView Group private class Group { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors internal Group(int id, IntPtr hwnd, bool isComctrlV6OnOsVerV6orHigher) { _items = new int[_size]; _groupID = id; _hwnd = hwnd; _index = -1; _isComctrlV6OnOsVerV6orHigher = isComctrlV6OnOsVerV6orHigher; } #endregion Constructor //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods internal int Count { get { return _index + 1; } } internal int[] Items { get { return _items; } } internal unsafe NativeMethods.Win32Rect GetGroupRect() { NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); bool isCollapsed = WindowsListViewGroup.IsCollapsed(_hwnd, _groupID); rect.top = isCollapsed ? NativeMethods.LVGGR_HEADER : NativeMethods.LVGGR_GROUP; XSendMessage.XSend(_hwnd, NativeMethods.LVM_GETGROUPRECT, new IntPtr(_groupID), new IntPtr(&rect), Marshal.SizeOf(rect.GetType())); Misc.MapWindowPoints(_hwnd, IntPtr.Zero, ref rect, 2); return rect; } internal NativeMethods.Win32Rect CalculateRectNoHeader() { NativeMethods.Win32Rect rcLv = NativeMethods.Win32Rect.Empty; if (!Misc.GetWindowRect(_hwnd, ref rcLv)) { return NativeMethods.Win32Rect.Empty; } // set top to the top coordinate of the first item NativeMethods.Win32Rect item; WindowsListView.GetItemRect(_hwnd, _items[0], NativeMethods.LVIR_BOUNDS, out item); NativeMethods.Win32Rect groupRc; groupRc.top = item.top; // left coordinate defined by the left coordinate of the listview groupRc.left = rcLv.left; int count = Count; // bottom defined by the bottom coordinate of the last item if (count > 1) { // get the rect of the last item in the group WindowsListView.GetItemRect(_hwnd, _items[count - 1], NativeMethods.LVIR_BOUNDS, out item); } groupRc.bottom = item.bottom; // right coordinate defined by lv.right groupRc.right = rcLv.right; // when vertical scrollbar is present take it into account if (WindowScroll.Scrollable(_hwnd, NativeMethods.SB_VERT)) { NativeMethods.Win32Rect rc = GetScrollbarRect(); int width = rc.right - rc.left; if (Misc.IsControlRTL(_hwnd)) { // Right to left mirroring style groupRc.left += width; } else { groupRc.right -= width; } } return groupRc; } // Add lvitem to the collection internal bool Add(int item) { // Check if we have an empty place in our array _index++; EnsureCapacity(_index); _items[_index] = item; return true; } #endregion Internal Methods //------------------------------------------------------ // // Internal Fields // //------------------------------------------------------ #region Internal Fields // id of the group internal int _groupID; #endregion Internal Fields //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods // grow the size of _items if needed private void EnsureCapacity(int min) { System.Diagnostics.Debug.Assert(min <= _items.Length, "EnsureCapacity: min is > _items.Length"); if (min == _items.Length) { // grow _items by factor of 2 int[] temp = _items; _items = new int[temp.Length * 2]; Array.Copy(temp, _items, temp.Length); } } // get rect of the v-scrollbar private NativeMethods.Win32Rect GetScrollbarRect() { NativeMethods.ScrollBarInfo sbi = new NativeMethods.ScrollBarInfo (); sbi.cbSize = Marshal.SizeOf(sbi.GetType()); if (Misc.GetScrollBarInfo(_hwnd, NativeMethods.OBJID_VSCROLL, ref sbi)) { return new NativeMethods.Win32Rect(sbi.rcScrollBar.left, sbi.rcScrollBar.top, sbi.rcScrollBar.right, sbi.rcScrollBar.bottom); } return NativeMethods.Win32Rect.Empty; } #endregion Private Methods //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields // Collection of items in the group // Store lvitem indexies. Use array to prevent boxing/unboxing // alexsn @ private int[] _items; private int _index; // current location in the array of items private IntPtr _hwnd; // lv hwnd private const int _size = 16; private bool _isComctrlV6OnOsVerV6orHigher; #endregion Private Fields } #endregion Nested classes } } // 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: Windows ListView Group helper classes // // History: // alexsn - Created (in DotNet) // 2003/08/08 - alexsn Updated for WCP // //--------------------------------------------------------------------------- using System; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows; using System.Collections; using System.Runtime.InteropServices; using System.ComponentModel; using MS.Win32; namespace MS.Internal.AutomationProxies { // Class representing collection of ListView GroupManagers class GroupManagerCollection { //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods // Ensures GroupManager creation for the specified listview // This method will be called only from certain methods on the LV // Called from LV: FirstChild, LastChild, ElementFromPoint, GetFocus internal void EnsureCreation(IntPtr hwnd) { if (!Contains(hwnd)) { _groupManagers[hwnd] = GroupManager.CreateGroupManager(hwnd); } } internal void Remove(IntPtr hwnd) { _groupManagers.Remove(hwnd); } // O(1) internal bool Contains(IntPtr hwnd) { return _groupManagers.ContainsKey(hwnd); } internal GroupManager this[IntPtr hwnd] { get { if (!WindowsListView.IsGroupViewEnabled(hwnd)) { // Group was disabled but we did not get the needed event // since for some things events are not being sent // (e.g: Going from some LV modes to - List mode, List mode does not have Groups) // alexsn @ throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // The group may have been discarded by the reorder winevent. EnsureCreation (hwnd); GroupManager manager = _groupManagers[hwnd] as GroupManager; if (manager == null) { // E.G. Going from the List mode to the something that has Group will cause this // alexsn @ throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } return manager; } } #endregion Internal Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields static private Hashtable _groupManagers = new Hashtable(10); #endregion Private Fields } // Class responsible for managing listview groups class GroupManager { // ------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructor private GroupManager(int groups, IntPtr hwnd, bool isComctrlV6OnOsVerV6orHigher) { _groups = new ArrayList(groups); _hwnd = hwnd; _isComctrlV6OnOsVerV6orHigher = isComctrlV6OnOsVerV6orHigher; } #endregion Constructor //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods internal NativeMethods.Win32Rect GetGroupRc(int id) { Group group = GetGroup(id); if (group == null) { return NativeMethods.Win32Rect.Empty; } return GetGroupRcInternal(group); } internal NativeMethods.Win32Rect GetGroupRcByIndex(int index) { if (index >= _groups.Count) { return NativeMethods.Win32Rect.Empty; } return GetGroupRcInternal((Group)_groups[index]); } internal int[] GetGroupIds() { int count = _groups.Count; int[] groupIds = new int[count]; for (int i = 0; i < count; i++) { groupIds[i] = ((Group)_groups[i])._groupID; } return groupIds; } internal bool IsGroupIdValid(int groupID) { int count = _groups.Count; for (int i = 0; i < count; i++) { Group group = (Group)_groups[i]; if (group._groupID == groupID) { return true; } } return false; } internal int GetGroupIdByIndex(int index) { if (index >= _groups.Count) { return -1; } return ((Group)_groups[index])._groupID; } internal int GroupCount() { return _groups.Count; } internal bool AreGroupsValid() { int count = _groups.Count; for (int i = 0; i < count; i++) { Group group = (Group)_groups[i]; if (!ListViewHasGroup(_hwnd, group._groupID)) { return false; } } // Make sure that no new group have been added, try to match all the GroupId to an // existing one. int itemCount = WindowsListView.GetItemCount (_hwnd); NativeMethods.LVITEM_V6 item = new NativeMethods.LVITEM_V6 (); item.mask = NativeMethods.LVIF_GROUPID; for (item.iItem = 0; item.iItem < itemCount; item.iItem++) { if (!XSendMessage.GetItem(_hwnd, ref item) || GetGroup(item.iGroupID) == null) { return false; } } return true; } internal GroupInfo GetGroupInfo(int groupID) { Group group = GetGroup(groupID); if (group != null) { return new GroupInfo(group.Items, group.Count); } // empty group info return GroupInfo.Null; } internal static GroupManager CreateGroupManager(IntPtr hwnd) { return InitializeManager(hwnd); } // detect whether the lv has a specified group internal static bool ListViewHasGroup(IntPtr hwnd, int groupID) { return Misc.ProxySendMessageInt(hwnd, NativeMethods.LVM_HASGROUP, new IntPtr(groupID), IntPtr.Zero) != 0; } #endregion Internal Methods //----------------------------------------------------- // // Internal Fields // //----------------------------------------------------- #region Internal Fields // structure containing group information internal struct GroupInfo { //----------------------------------------------------- // // Constructor // //------------------------------------------------------ #region Constructor internal GroupInfo(int[] items, int count) { _items = items; // OK to do an assignment here, instead of deep copy _count = count; } #endregion Constructor //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ #region Public Methods static public bool operator true(GroupInfo info) { return info._items != null; } static public bool operator false(GroupInfo info) { return info._items == null; } static public bool operator !(GroupInfo info) { if (info) { return false; } return true; } #endregion Public Methods //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods internal int IndexOf(int item) { int index = 0; while (index < _count) { if (_items[index] == item) { return index; } index++; } return -1; } #endregion Internal Methods //------------------------------------------------------ // // Internal Fields // //----------------------------------------------------- #region Internal Fields internal int[] _items; internal int _count; static readonly internal GroupInfo Null = new GroupInfo(null, -1); #endregion Internal Fields } // collection of groups internal ArrayList _groups; // list hwnd internal IntPtr _hwnd; #endregion Internal Fields //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- #region Private Methods private Group GetGroup(int id) { int count = _groups.Count; Group current = null; for (int i = 0; i < count; i++) { current = (Group)(_groups[i]); if (current._groupID == id) { return current; } } return null; } private static unsafe GroupManager InitializeManager(IntPtr hwnd) { bool isComctrlV6OnOsVerV6orHigher = Misc.IsComctrlV6OnOsVerV6orHigher(hwnd); int itemCount = WindowsListView.GetItemCount(hwnd); NativeMethods.LVITEM_V6 item = new NativeMethods.LVITEM_V6(); item.mask = NativeMethods.LVIF_GROUPID; // The only place where the GroupManager gets constructed GroupManager manager = new GroupManager(itemCount, hwnd, isComctrlV6OnOsVerV6orHigher); if (isComctrlV6OnOsVerV6orHigher) { NativeMethods.LVITEMINDEX ii = new NativeMethods.LVITEMINDEX(-1, -1); int flags = NativeMethods.LVNI_VISIBLEONLY | NativeMethods.LVNI_VISIBLEORDER; // When a listview is being "grouped by" an item may be in more than one group. The itemCount // is the number of unique items. This loop may iterate for more than the unique items in the group. // We are taking advantage of that fact the the array list will expand if there are alot of duplicates. while (XSendMessage.XSend (hwnd, NativeMethods.LVM_GETNEXTITEMINDEX, new IntPtr(&ii), flags, Marshal.SizeOf(ii.GetType()))) { // need to convert the item id to a group id NativeMethods.LVGROUP_V6 groupInfo = new NativeMethods.LVGROUP_V6(); groupInfo.Init(Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6))); groupInfo.mask = NativeMethods.LVGF_GROUPID; bool lresult = XSendMessage.XSend(hwnd, NativeMethods.LVM_GETGROUPINFOBYINDEX, new IntPtr(ii.iGroup), new IntPtr(&groupInfo), Marshal.SizeOf(typeof(NativeMethods.LVGROUP_V6))); if (!lresult) { if (groupInfo.iGroupID == -1) { // A -1 here means that there are no duplicates in this grouped listview so // we have to get the group the old way. This is done for performance reasons. break; } // no group for this item should never happen. // If it ever does the other items might ok so just keep going. continue; } if (!manager.Add(groupInfo.iGroupID, ii.iItem)) { // we had problem adding item to the needed group at this point it makes no // sense to continue System.Diagnostics.Debug.Assert(false, "Cannot add item to the needed group"); return null; } } } bool sortNeeded = false; // If the code above did not yield anything try this way. This will work for // listviews pre Vista and grouped listviews in vista that don't have duplicate items. if (manager.GroupCount() == 0) { // if we get the groups this way they need to be sorted. The code above brings them in sorted. sortNeeded = true; int current = 0; while (current < itemCount) { item.iItem = current; if (XSendMessage.GetItem(hwnd, ref item) && manager.Add(item.iGroupID, item.iItem)) { current++; } else { // we had problem adding item to the needed group at this point it makes no // sense to continue System.Diagnostics.Debug.Assert(false, "Cannot add item to the needed group"); return null; } } } // Sort items within the group int groupsCount = manager.GroupCount(); for (int i = 0; i < groupsCount; i++) { Group group = (Group)manager._groups[i]; Array.Sort(group.Items, 0, group.Count, new SortGroupItems(hwnd)); } // Depending on how we got the group info we may need to sort it. // In vista the the listview can put the list items in the correct order. // Pre vista or old ui (v5) will always need to be sorted. if (sortNeeded) { // Sort groups manager._groups.Sort(new SortGroups(hwnd)); } return manager; } private unsafe int GetGroupHeaderHeight() { NativeMethods.LVGROUPMETRICS metric = new NativeMethods.LVGROUPMETRICS (sizeof(NativeMethods.LVGROUPMETRICS), NativeMethods.LVGMF_BORDERSIZE); XSendMessage.XSend(_hwnd, NativeMethods.LVM_GETGROUPMETRICS, IntPtr.Zero, new IntPtr(&(metric.cbSize)), metric.cbSize, XSendMessage.ErrorValue.NoCheck); return metric.Top + padding; } private bool Add(int id, int item) { Group group = GetGroup(id); if (group == null) { group = new Group(id, _hwnd, _isComctrlV6OnOsVerV6orHigher); _groups.Add(group); } // group already exist, simply add an item to it return group.Add(item); } // Retrieve the rect of the group. private NativeMethods.Win32Rect GetGroupRcInternal(Group group) { NativeMethods.Win32Rect rcGroup = group.GetGroupRect(); if (rcGroup.IsEmpty) { // LVM_GETGROUPRECT failed. rcGroup = group.CalculateRectNoHeader(); // add the header's height hence changing the rcGroup.top coordinate // increase top by subtracting header from it rcGroup.top -= GetGroupHeaderHeight(); } return rcGroup; } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // Group header padding private const int padding = 12; private bool _isComctrlV6OnOsVerV6orHigher; #endregion Private Fields //------------------------------------------------------ // // Nested classes // //------------------------------------------------------ #region Nested classes // Implementation of IComparer used to // sort groups. // Note: we want to have groups sorted in the way they displayed to the user // Hence we cannot sort by group id, since if group has the lower id it does not mean // that it displayed before the group with the higher id. // Hence we will sort groups by the rect of the first item within the group private class SortGroups : IComparer { //----------------------------------------------------- // // Constructors // //------------------------------------------------------ #region Constructors internal SortGroups(IntPtr hwnd) { _hwnd = hwnd; } #endregion Constructor //----------------------------------------------------- // // IComparer // //----------------------------------------------------- #region IComparer int IComparer.Compare(object x, object y) { Group g1 = (Group)x; Group g2 = (Group)y; // piggy back on the SortGroupItem.Compare SortGroupItems helper = new SortGroupItems(_hwnd); return ((IComparer)helper).Compare(g1.Items[0], g2.Items[0]); } #endregion IComparer //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ #region Private Fields private IntPtr _hwnd; #endregion Private Fields } // Implementation of IComparer used to // sort items within group based on their rectangle // NOTE: After the sort the listviewitems DIRECTION in array will be the following (except when in Detail mode) // 0 1 2 // 3 4 5 // 6 7 private class SortGroupItems : IComparer { //----------------------------------------------------- // // Constructors // //------------------------------------------------------ #region Constructors internal SortGroupItems(IntPtr hwnd) { _hwnd = hwnd; } #endregion Constructor //------------------------------------------------------ // // IComparer // //----------------------------------------------------- #region IComparer int IComparer.Compare(object x, object y) { int item1 = (int)x; int item2 = (int)y; // get the rect of 2 items NativeMethods.Win32Rect rc1; WindowsListView.GetItemRect(_hwnd, item1, NativeMethods.LVIR_BOUNDS, out rc1); NativeMethods.Win32Rect rc2; WindowsListView.GetItemRect(_hwnd, item2, NativeMethods.LVIR_BOUNDS, out rc2); // compare rectangles if (rc1.left < rc2.left || rc1.top < rc2.top) { return -1; } else if (rc1.left != rc2.left || rc1.top != rc2.top) { return 1; } return 0; } #endregion IComparer //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields private IntPtr _hwnd; #endregion Private Fields } // Class describing single ListView Group private class Group { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors internal Group(int id, IntPtr hwnd, bool isComctrlV6OnOsVerV6orHigher) { _items = new int[_size]; _groupID = id; _hwnd = hwnd; _index = -1; _isComctrlV6OnOsVerV6orHigher = isComctrlV6OnOsVerV6orHigher; } #endregion Constructor //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- #region Internal Methods internal int Count { get { return _index + 1; } } internal int[] Items { get { return _items; } } internal unsafe NativeMethods.Win32Rect GetGroupRect() { NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); bool isCollapsed = WindowsListViewGroup.IsCollapsed(_hwnd, _groupID); rect.top = isCollapsed ? NativeMethods.LVGGR_HEADER : NativeMethods.LVGGR_GROUP; XSendMessage.XSend(_hwnd, NativeMethods.LVM_GETGROUPRECT, new IntPtr(_groupID), new IntPtr(&rect), Marshal.SizeOf(rect.GetType())); Misc.MapWindowPoints(_hwnd, IntPtr.Zero, ref rect, 2); return rect; } internal NativeMethods.Win32Rect CalculateRectNoHeader() { NativeMethods.Win32Rect rcLv = NativeMethods.Win32Rect.Empty; if (!Misc.GetWindowRect(_hwnd, ref rcLv)) { return NativeMethods.Win32Rect.Empty; } // set top to the top coordinate of the first item NativeMethods.Win32Rect item; WindowsListView.GetItemRect(_hwnd, _items[0], NativeMethods.LVIR_BOUNDS, out item); NativeMethods.Win32Rect groupRc; groupRc.top = item.top; // left coordinate defined by the left coordinate of the listview groupRc.left = rcLv.left; int count = Count; // bottom defined by the bottom coordinate of the last item if (count > 1) { // get the rect of the last item in the group WindowsListView.GetItemRect(_hwnd, _items[count - 1], NativeMethods.LVIR_BOUNDS, out item); } groupRc.bottom = item.bottom; // right coordinate defined by lv.right groupRc.right = rcLv.right; // when vertical scrollbar is present take it into account if (WindowScroll.Scrollable(_hwnd, NativeMethods.SB_VERT)) { NativeMethods.Win32Rect rc = GetScrollbarRect(); int width = rc.right - rc.left; if (Misc.IsControlRTL(_hwnd)) { // Right to left mirroring style groupRc.left += width; } else { groupRc.right -= width; } } return groupRc; } // Add lvitem to the collection internal bool Add(int item) { // Check if we have an empty place in our array _index++; EnsureCapacity(_index); _items[_index] = item; return true; } #endregion Internal Methods //------------------------------------------------------ // // Internal Fields // //------------------------------------------------------ #region Internal Fields // id of the group internal int _groupID; #endregion Internal Fields //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods // grow the size of _items if needed private void EnsureCapacity(int min) { System.Diagnostics.Debug.Assert(min <= _items.Length, "EnsureCapacity: min is > _items.Length"); if (min == _items.Length) { // grow _items by factor of 2 int[] temp = _items; _items = new int[temp.Length * 2]; Array.Copy(temp, _items, temp.Length); } } // get rect of the v-scrollbar private NativeMethods.Win32Rect GetScrollbarRect() { NativeMethods.ScrollBarInfo sbi = new NativeMethods.ScrollBarInfo (); sbi.cbSize = Marshal.SizeOf(sbi.GetType()); if (Misc.GetScrollBarInfo(_hwnd, NativeMethods.OBJID_VSCROLL, ref sbi)) { return new NativeMethods.Win32Rect(sbi.rcScrollBar.left, sbi.rcScrollBar.top, sbi.rcScrollBar.right, sbi.rcScrollBar.bottom); } return NativeMethods.Win32Rect.Empty; } #endregion Private Methods //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields // Collection of items in the group // Store lvitem indexies. Use array to prevent boxing/unboxing // alexsn @ private int[] _items; private int _index; // current location in the array of items private IntPtr _hwnd; // lv hwnd private const int _size = 16; private bool _isComctrlV6OnOsVerV6orHigher; #endregion Private Fields } #endregion Nested classes } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- OperatingSystem.cs
- HtmlInputHidden.cs
- ErrorProvider.cs
- Convert.cs
- FontUnitConverter.cs
- PingOptions.cs
- DbDataSourceEnumerator.cs
- SqlLiftIndependentRowExpressions.cs
- FocusManager.cs
- AmbientProperties.cs
- RootBrowserWindowProxy.cs
- Translator.cs
- EventPropertyMap.cs
- XPathAxisIterator.cs
- RadioButton.cs
- SecurityAttributeGenerationHelper.cs
- DataServiceHostWrapper.cs
- EndPoint.cs
- CompiledRegexRunnerFactory.cs
- SocketAddress.cs
- Rotation3DAnimationBase.cs
- EditingMode.cs
- WorkflowQueuingService.cs
- ErrorEventArgs.cs
- MultiTrigger.cs
- UrlPath.cs
- TempFiles.cs
- WriteFileContext.cs
- ErrorEventArgs.cs
- WindowsListBox.cs
- SingleBodyParameterMessageFormatter.cs
- TableItemStyle.cs
- Shared.cs
- BindingElement.cs
- ShaderEffect.cs
- ViewSimplifier.cs
- MailFileEditor.cs
- QuaternionRotation3D.cs
- DataGridViewCellErrorTextNeededEventArgs.cs
- ZoneIdentityPermission.cs
- DebugView.cs
- HighlightVisual.cs
- RedirectionProxy.cs
- PhonemeConverter.cs
- PrintDialog.cs
- QilTargetType.cs
- DataObject.cs
- DateTimeAutomationPeer.cs
- ValidationSummary.cs
- HttpAsyncResult.cs
- ListenerElementsCollection.cs
- VisualProxy.cs
- BufferBuilder.cs
- JsonReader.cs
- SqlFlattener.cs
- __Filters.cs
- WindowsStartMenu.cs
- HttpContext.cs
- TextEditorCharacters.cs
- X509SecurityTokenProvider.cs
- ReplyChannelAcceptor.cs
- GridViewRowEventArgs.cs
- CacheVirtualItemsEvent.cs
- GeneralTransform3D.cs
- ClientSettingsStore.cs
- CorrelationActionMessageFilter.cs
- ChannelTokenTypeConverter.cs
- FixUpCollection.cs
- MetadataArtifactLoaderFile.cs
- XmlAtomErrorReader.cs
- CodeTypeReferenceSerializer.cs
- VScrollBar.cs
- MimeParameterWriter.cs
- QueryPrefixOp.cs
- ColumnCollection.cs
- DocumentNUp.cs
- LinqDataSourceDisposeEventArgs.cs
- MimeObjectFactory.cs
- TraceContextEventArgs.cs
- ContainerParaClient.cs
- WebPartChrome.cs
- StringInfo.cs
- PerformanceCounterLib.cs
- XmlNodeReader.cs
- UserInitiatedNavigationPermission.cs
- SqlServer2KCompatibilityCheck.cs
- TextTreeNode.cs
- ContextMenu.cs
- HotCommands.cs
- DataGridView.cs
- MetaModel.cs
- GridViewPageEventArgs.cs
- LiteralTextParser.cs
- DataRecordInternal.cs
- ControllableStoryboardAction.cs
- ServiceCredentials.cs
- DBSchemaRow.cs
- DecoratedNameAttribute.cs
- OutputCacheProviderCollection.cs
- SystemIPInterfaceStatistics.cs