// Removes this element from the selection void ISelectionItemProvider.RemoveFromSelection() { // Make sure that the control is enabled if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) { throw new ElementNotEnabledException(); } // If not selected, done if (!((ISelectionItemProvider)this).IsSelected) { return; } // If multiple selections allowed, unselect element if (WindowsTab.SupportMultipleSelection(_hwnd) == true) { NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (GetClickablePoint(out pt, true)) { Input.SendKeyboardInput(Key.LeftCtrl, true); Misc.MouseClick(pt.x, pt.y); Input.SendKeyboardInput(Key.LeftCtrl, false); return; } throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // else if button style and single select, send deselectall message else if (Misc.IsBitSet(WindowStyle, NativeMethods.TCS_BUTTONS)) { Misc.ProxySendMessage(_hwnd, NativeMethods.TCM_DESELECTALL, IntPtr.Zero, IntPtr.Zero); return; } throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); }
// Press a tab private void Select() { if (Misc.IsBitSet(WindowStyle, (NativeMethods.TCS_BUTTONS | NativeMethods.TCS_FOCUSNEVER))) { // The TCM_SETCURFOCUS message cannot be used with TCS_FOCUSNEVER // use a convulated way faking a mouse action NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (GetClickablePoint(out pt, true)) { // Convert screen coordinates to client coordinates. if (Misc.MapWindowPoints(IntPtr.Zero, _hwnd, ref pt, 1)) { Misc.PostMessage(_hwnd, NativeMethods.WM_LBUTTONDOWN, (IntPtr)NativeMethods.MK_LBUTTON, NativeMethods.Util.MAKELPARAM(pt.x, pt.y)); Misc.PostMessage(_hwnd, NativeMethods.WM_LBUTTONUP, (IntPtr)NativeMethods.MK_LBUTTON, NativeMethods.Util.MAKELPARAM(pt.x, pt.y)); } } } else { Misc.ProxySendMessage(_hwnd, NativeMethods.TCM_SETCURFOCUS, new IntPtr(_item), IntPtr.Zero); } }
// Same as clicking on an hyperlink void IInvokeProvider.Invoke() { // Check that button can be clicked. // // This state could change anytime. // // Make sure that the control is enabled if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) { throw new ElementNotEnabledException(); } if (!SafeNativeMethods.IsWindowVisible(_hwnd)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } Misc.SetFocus(_hwnd); NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (GetClickablePoint(out pt, false)) { Misc.MouseClick(pt.x, pt.y); } }
// Adds this element to the selection void ISelectionItemProvider.AddToSelection() { // Make sure that the control is enabled if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) { throw new ElementNotEnabledException(); } // If not selectable, can't add to selection if (!IsSelectable()) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // If already selected, done if (((ISelectionItemProvider)this).IsSelected) { return; } // If multiple selections allowed, add requested selection if (WindowsTab.SupportMultipleSelection(_hwnd) == true) { // Press ctrl and mouse click tab NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (GetClickablePoint(out pt, true)) { Input.SendKeyboardInput(Key.LeftCtrl, true); Misc.MouseClick(pt.x, pt.y); Input.SendKeyboardInput(Key.LeftCtrl, false); return; } throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // else only single selection allowed else { throw new InvalidOperationException(SR.Get(SRID.DoesNotSupportMultipleSelection)); } }
private void Toggle() { // Make sure that the control is enabled if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) { throw new ElementNotEnabledException(); } Misc.SetFocus(_hwnd); NativeMethods.Win32Rect rc = ListViewCheckBoxRect(_hwnd, _listviewItem); NativeMethods.Win32Point pt = new NativeMethods.Win32Point((rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2); if (Misc.MapWindowPoints(IntPtr.Zero, _hwnd, ref pt, 1)) { // "click" on the checkbox Misc.ProxySendMessage(_hwnd, NativeMethods.WM_LBUTTONDOWN, (IntPtr)NativeMethods.MK_LBUTTON, NativeMethods.Util.MAKELPARAM(pt.x, pt.y)); Misc.ProxySendMessage(_hwnd, NativeMethods.WM_LBUTTONUP, IntPtr.Zero, NativeMethods.Util.MAKELPARAM(pt.x, pt.y)); } }
// Obtain clickable point on the listviewitem // in the case when one doesnot exist return false private bool GetListviewitemClickablePoint (out NativeMethods.Win32Point clickPoint) { // When this method is called, lv was already scrolled vertically // hence item is visible veritcally clickPoint.x = clickPoint.y = 0; NativeMethods.Win32Rect itemRectangle; // Obtain rectangle if (!WindowsListView.GetItemRect(_hwnd, _item, NativeMethods.LVIR_LABEL, out itemRectangle)) { return false; } if (WindowsListView.IsDetailMode (_hwnd) && !WindowsListView.FullRowSelect (_hwnd)) { // LVS_REPORT - possible that we may need to scroll horizontaly // NativeMethods.Win32Point pt = new NativeMethods.Win32Point (itemRectangle.left, 0); if (!Misc.MapWindowPoints(IntPtr.Zero, _hwnd, ref pt, 1)) { return false; } // In client coordinates, hence negative indicates that item is to the left of lv client area if (pt.x < 0) { ((IScrollItemProvider)this).ScrollIntoView(); if (!WindowsListView.GetItemRect(_hwnd, _item, NativeMethods.LVIR_LABEL, out itemRectangle)) { return false; } } } clickPoint.x = Math.Min ((itemRectangle.left + 5), (itemRectangle.left + itemRectangle.right) / 2); clickPoint.y = (itemRectangle.top + itemRectangle.bottom) / 2; return true; }
// 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; } }
// Move the mouse to the x, y location and perfoms either // a single of double clik depending on the fDoubleClick parameter // The mouse is then brough back to the original location. internal static void MouseClick(int x, int y, bool fDoubleClick) { NativeMethods.Win32Point ptPrevious = new NativeMethods.Win32Point(); bool fSetOldCursorPos = GetCursorPos(ref ptPrevious); bool mouseSwapped = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_SWAPBUTTON) != 0; Input.SendMouseInput(x, y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute); Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightDown : SendMouseInputFlags.LeftDown); Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightUp : SendMouseInputFlags.LeftUp); if (fDoubleClick) { Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightDown : SendMouseInputFlags.LeftDown); Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightUp : SendMouseInputFlags.LeftUp); } // toolbar items don't have time to proccess the mouse click if we move it back too soon // so wait a small amount of time to give them a chance. A value of 10 made this work // on a 2gig dual proc machine so 50 should cover a slower machine. System.Threading.Thread.Sleep(50); // Set back the mouse position where it was if (fSetOldCursorPos) { Input.SendMouseInput(ptPrevious.x, ptPrevious.y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute); } }
// For Vista getting the part of the titlebar that a tooltip belongs to is more // reliable across themes private string GetTitleBarToolTipTextForDWMEnabled() { // The mouse is over the titlebar item so get that point on the screen NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (!Misc.GetCursorPos(ref pt)) { return ""; } // Find the titlebar hwnd IntPtr hwnd = UnsafeNativeMethods.WindowFromPhysicalPoint(pt.x, pt.y); if (hwnd == IntPtr.Zero) { return ""; } // Get the rects for each titlbar part Rect[] rects = Misc.GetTitlebarRects(hwnd); // Look from back to front - front is entire titlebar rect int scan; for (scan = rects.Length - 1; scan >= 0; scan--) { // Not using Misc.PtInRect because including the bounding pixels all the way around gives // better results; tooltips may appear when the mouse is one or two pixels outside of the // bounding rect so even this technique may miss. if (pt.x >= rects[scan].Left && pt.x <= rects[scan].Right && pt.y >= rects[scan].Top && pt.y <= rects[scan].Bottom) { break; } } switch (scan) { case NativeMethods.INDEX_TITLEBAR_MINBUTTON: if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_MINIMIZE)) return ST.Get(STID.LocalizedNameWindowsTitleBarButtonRestore); else return ST.Get(STID.LocalizedNameWindowsTitleBarButtonMinimize); case NativeMethods.INDEX_TITLEBAR_HELPBUTTON: return ST.Get(STID.LocalizedNameWindowsTitleBarButtonContextHelp); case NativeMethods.INDEX_TITLEBAR_MAXBUTTON: if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_MAXIMIZE)) return ST.Get(STID.LocalizedNameWindowsTitleBarButtonRestore); else return ST.Get(STID.LocalizedNameWindowsTitleBarButtonMaximize); case NativeMethods.INDEX_TITLEBAR_CLOSEBUTTON: return ST.Get(STID.LocalizedNameWindowsTitleBarButtonClose); case NativeMethods.INDEX_TITLEBAR_SELF: return Misc.ProxyGetText(hwnd); default: return ""; } }
private string GetTitleBarToolTipTextHitTest() { NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (!Misc.GetCursorPos(ref pt)) { return ""; } IntPtr hwnd = UnsafeNativeMethods.WindowFromPhysicalPoint(pt.x, pt.y); if (hwnd == IntPtr.Zero) { return ""; } int hit = Misc.ProxySendMessageInt(hwnd, NativeMethods.WM_NCHITTEST, IntPtr.Zero, NativeMethods.Util.MAKELPARAM(pt.x, pt.y)); switch (hit) { case NativeMethods.HTMINBUTTON: if (Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.WS_MINIMIZE)) return ST.Get(STID.LocalizedNameWindowsTitleBarButtonRestore); else return ST.Get(STID.LocalizedNameWindowsTitleBarButtonMinimize); case NativeMethods.HTMAXBUTTON: if (Misc.IsBitSet(Misc.GetWindowStyle(hwnd), NativeMethods.WS_MAXIMIZE)) return ST.Get(STID.LocalizedNameWindowsTitleBarButtonRestore); else return ST.Get(STID.LocalizedNameWindowsTitleBarButtonMaximize); case NativeMethods.HTCLOSE: case NativeMethods.HTMDICLOSE: return ST.Get(STID.LocalizedNameWindowsTitleBarButtonClose); case NativeMethods.HTHELP: return ST.Get(STID.LocalizedNameWindowsTitleBarButtonContextHelp); case NativeMethods.HTMDIMINBUTTON: return ST.Get(STID.LocalizedNameWindowsTitleBarButtonMinimize); case NativeMethods.HTMDIMAXBUTTON: return ST.Get(STID.LocalizedNameWindowsTitleBarButtonMaximize); case NativeMethods.HTCAPTION: return Misc.ProxyGetText(hwnd); default: break; } return ""; }
// Returns a Proxy element corresponding to the specified screen coordinates. internal override ProxySimple ElementProviderFromPoint(int x, int y) { ProxySimple proxyElement = null; // Convert screen to client coords. NativeMethods.Win32Point pt = new NativeMethods.Win32Point(x, y); if (Misc.MapWindowPoints(System.IntPtr.Zero, _hwnd, ref pt, 1)) { // GetClientRect NativeMethods.Win32Rect clientRect = new NativeMethods.Win32Rect(); if(Misc.GetClientRect(_hwnd, ref clientRect)) { if (Misc.PtInRect(ref clientRect, pt.x, pt.y)) { int column = (pt.x - _altTabInfo.ptStart.x) / _altTabInfo.cxItem; int row = (pt.y - _altTabInfo.ptStart.y) / _altTabInfo.cyItem; if (column >= 0 && column < Columns && row >= 0 && row < Rows) { proxyElement = CreateAltTabItem(ItemIndex(row, column)); } } } } if (proxyElement == null) { proxyElement = base.ElementProviderFromPoint(x, y); } return proxyElement; }
internal static unsafe IntPtr HitTestTreeView(IntPtr hwnd, int x, int y) { ProcessorTypes localBitness; ProcessorTypes remoteBitness; GetProcessTypes(hwnd, out localBitness, out remoteBitness); IntPtr hitTestItem = IntPtr.Zero; // Convert the coordinates for the point of interest from // screen coordinates to window-relative coordinates. NativeMethods.Win32Point clientPoint = new NativeMethods.Win32Point(x, y); if (Misc.MapWindowPoints(IntPtr.Zero, hwnd, ref clientPoint, 1)) { if (localBitness == remoteBitness) { NativeMethods.TVHITTESTINFO hitTestInfo = new NativeMethods.TVHITTESTINFO(clientPoint.x, clientPoint.y, 0); if (XSend(hwnd, NativeMethods.TVM_HITTEST, IntPtr.Zero, new IntPtr(&hitTestInfo), Marshal.SizeOf(hitTestInfo.GetType()), XSendMessage.ErrorValue.Zero)) { hitTestItem = hitTestInfo.hItem; } } else if (remoteBitness == ProcessorTypes.Processor32Bit) { TVHITTESTINFO_32 hitTestInfo32 = new TVHITTESTINFO_32(clientPoint.x, clientPoint.y, 0); if (XSend(hwnd, NativeMethods.TVM_HITTEST, IntPtr.Zero, new IntPtr(&hitTestInfo32), Marshal.SizeOf(hitTestInfo32.GetType()), XSendMessage.ErrorValue.Zero)) { hitTestItem = new IntPtr(hitTestInfo32.hItem); } } else if (remoteBitness == ProcessorTypes.Processor64Bit) { TVHITTESTINFO_64 hitTestInfo64 = new TVHITTESTINFO_64(clientPoint.x, clientPoint.y, 0); if (XSend(hwnd, NativeMethods.TVM_HITTEST, IntPtr.Zero, new IntPtr(&hitTestInfo64), Marshal.SizeOf(hitTestInfo64.GetType()), XSendMessage.ErrorValue.Zero)) { hitTestItem = new IntPtr(hitTestInfo64.hItem); } } } return hitTestItem; }
// Process all the Element Properties internal virtual object GetElementProperty(AutomationProperty idProp) { // we can handle some properties locally if (idProp == AutomationElement.LocalizedControlTypeProperty) { return _sType; } else if(idProp == AutomationElement.ControlTypeProperty) { return _cControlType != null ? (object)_cControlType.Id : null; } else if (idProp == AutomationElement.IsContentElementProperty) { return _item >= 0 && _fIsContent; } else if (idProp == AutomationElement.NameProperty) { return LocalizedName; } else if (idProp == AutomationElement.AccessKeyProperty) { return GetAccessKey(); } else if (idProp == AutomationElement.IsEnabledProperty) { return Misc.IsEnabled(_hwnd); } else if (idProp == AutomationElement.IsKeyboardFocusableProperty) { return IsKeyboardFocusable(); } else if (idProp == AutomationElement.ProcessIdProperty) { // Get the pid of the process that the HWND lives in, not the // pid that this proxy lives in uint pid; Misc.GetWindowThreadProcessId(_hwnd, out pid); return (int)pid; } else if (idProp == AutomationElement.ClickablePointProperty) { NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (GetClickablePoint(out pt, !IsHwndElement())) { // Due to P/Invoke marshalling issues, the reurn value is in the // form of a {x,y} array instead of using the Point datatype return new double[] { pt.x, pt.y }; } return AutomationElement.NotSupported; } else if (idProp == AutomationElement.HasKeyboardFocusProperty) { // Check first if the hwnd has the Focus // Punt if not the case, drill down otherwise // If already focused, leave as-is. Calling SetForegroundWindow // on an already focused HWND will remove focus! return Misc.GetFocusedWindow() == _hwnd ? IsFocused() : false; } else if (idProp == AutomationElement.AutomationIdProperty) { // PerSharp/PreFast will flag this as a warning 6507/56507: Prefer 'string.IsNullOrEmpty(_sAutomationId)' over checks for null and/or emptiness. // _sAutomationId being null is invalid, while being empty is a valid state. // The use of IsNullOrEmpty while hide this. #pragma warning suppress 6507 System.Diagnostics.Debug.Assert(_sAutomationId != null, "_sAutomationId is null!"); #pragma warning suppress 6507 return _sAutomationId.Length > 0 ? _sAutomationId : null; } else if (idProp == AutomationElement.IsOffscreenProperty) { return IsOffscreen(); } else if (idProp == AutomationElement.HelpTextProperty) { return HelpText; } else if (idProp == AutomationElement.FrameworkIdProperty) { return WindowsFormsHelper.IsWindowsFormsControl(_hwnd) ? "WinForm" : "Win32"; } return null; }
// Returns a Proxy element corresponding to the specified screen coordinates. internal override ProxySimple ElementProviderFromPoint (int x, int y) { int x1 = x; int y1 = y; NativeMethods.Win32Rect rebarRect = new NativeMethods.Win32Rect (); if (!Misc.GetWindowRect(_hwnd, ref rebarRect)) { return null; } if (x >= rebarRect.left && x <= rebarRect.right && y >= rebarRect.top && y <= rebarRect.bottom) { x = x - rebarRect.left; y = y - rebarRect.top; NativeMethods.Win32Point pt = new NativeMethods.Win32Point (x, y); int BandID = getRebarBandIDFromPoint (pt); if (-1 != BandID) { return CreateRebarItem (BandID).ElementProviderFromPoint (x1, y1); } } return null; }
private void Toggle() { // Convoluted way fake a mouse action NativeMethods.Win32Point pt = new NativeMethods.Win32Point(); if (GetClickablePoint(out pt, false)) { // Mouse method is used here because following methods fail: // -BM_CLICK message doesn't work with all buttons (e.g. Start) // -WM_MOUSEACTIVATE + WM_LBUTTONDOWN + WM_LBUTTONUP messages don't work with all buttons // -WM_KEYDOWN + WM_KEYUP messages for space bar // -SendKeyboardInput for space bar // See prior versions of this file for alternative code. // Misc.MouseClick(pt.x, pt.y); } }
internal static bool GetClientRectInScreenCoordinates(IntPtr hwnd, ref NativeMethods.Win32Rect rc) { rc = NativeMethods.Win32Rect.Empty; if (!GetClientRect(hwnd, ref rc)) { return false; } NativeMethods.Win32Point leftTop = new NativeMethods.Win32Point(rc.left, rc.top); if (!MapWindowPoints(hwnd, IntPtr.Zero, ref leftTop, 1)) { return false; } NativeMethods.Win32Point rightBottom = new NativeMethods.Win32Point(rc.right, rc.bottom); if (!MapWindowPoints(hwnd, IntPtr.Zero, ref rightBottom, 1)) { return false; } rc = new NativeMethods.Win32Rect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y); return true; }
internal void GetVisibleRangePoints(out int start, out int end) { start = 0; end = 0; NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect(); if (Misc.GetClientRect(_hwnd, ref rect) && !rect.IsEmpty) { NativeMethods.SIZE size; string s = new string('E', 1); GetTextExtentPoint32(s, out size); NativeMethods.Win32Point ptStart = new NativeMethods.Win32Point((int)(rect.left + size.cx / 4), (int)(rect.top + size.cy / 4)); NativeMethods.Win32Point ptEnd = new NativeMethods.Win32Point((int)(rect.right - size.cx / 8), (int)(rect.bottom - size.cy / 4)); start = CharFromPosEx(ptStart); end = CharFromPosEx(ptEnd); if (start > 0) { Point pt = PosFromChar(start); if (pt.X < rect.left) { start++; } } } else { // multi-line edit controls are handled differently than single-line edit controls. if (IsMultiline) { // get the line number of the first visible line and start the range at // the beginning of that line. int firstLine = GetFirstVisibleLine(); start = LineIndex(firstLine); // calculate the line number of the first line scrolled off the bottom and // end the range at the beginning of that line. end = LineIndex(firstLine + LinesPerPage()); } else { // single-line edit control // the problem is that using a variable-width font the number of characters visible // depends on the text that is in the edit control. so we can't just divide the // width of the edit control by the width of a character. // so instead we do a binary search of the characters from the first visible character // to the end of the text to find the visibility boundary. Rect r = GetRect(); int limit = GetTextLength(); start = GetFirstVisibleChar(); int lo = start; // known visible int hi = limit; // known non-visible while (lo + 1 < hi) { int mid = (lo + hi) / 2; Point pt = PosFromChar(mid); if (pt.X >= r.Left && pt.X < r.Right) { lo = mid; } else { hi = mid; } } // trim off one character unless the range is empty or reaches the end. end = hi > start && hi < limit ? hi - 1 : hi; } } }
private void Invoke() { // get item rect NativeMethods.Win32Rect rectItem = WindowsTreeView.GetItemRect(_hwnd, _hItem, true); if (rectItem.IsEmpty) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // get control coordinates at which we will "click" NativeMethods.Win32Point pt = new NativeMethods.Win32Point(((rectItem.left + rectItem.right) / 2), ((rectItem.top + rectItem.bottom) / 2)); // convert back to client if (Misc.MapWindowPoints(IntPtr.Zero, _hwnd, ref pt, 1)) { // click SimulateClick(pt); } }
// Returns a Proxy element corresponding to the specified screen coordinates. internal override ProxySimple ElementProviderFromPoint (int x, int y) { NativeMethods.Win32Point pt = new NativeMethods.Win32Point (x, y); NativeMethods.LVHITTESTINFO_INTERNAL hitTest = WindowsListView.SubitemHitTest (_hwnd, pt); if ((hitTest.flags & NativeMethods.LVHT_EX_GROUP_HEADER) != 0) { return this; } if ((hitTest.flags & NativeMethods.LVHT_ONITEM) != 0 && hitTest.iItem >= 0) { // create the item return new ListViewItem (_hwnd, this, hitTest.iItem); } // If we did not land on an item we may be at a subset link these only exist // in v6 comctrl and vista or later. if (_isComctrlV6OnOsVerV6orHigher) { // Allocate a local LVHITTESTINFO struct. NativeMethods.LVHITTESTINFO_V6 hitTestNative = new NativeMethods.LVHITTESTINFO_V6(hitTest); unsafe { XSendMessage.XSendGetIndex(_hwnd, NativeMethods.LVM_HITTEST, new IntPtr(-1), new IntPtr(&hitTestNative), Marshal.SizeOf(hitTestNative.GetType())); } if ((hitTestNative.flags & NativeMethods.LVHT_EX_GROUP_SUBSETLINK) != 0) { GroupManager.GroupInfo groupInfo = GetGroupInfo (_hwnd, ID); int [] items = groupInfo._items; if (groupInfo._count <= 0 || groupInfo._count > items.Length) { return null; } int index = items [groupInfo._count - 1]; return CreateGroupSubsetLink(index + 1); } } return this; }