/// <summary> /// Gets the column at the specified position /// </summary> /// <param name="p">Point in client coordinates</param> /// <returns>The nul zero based index of the column (-1 if failed)</returns> public int GetColumnAt(Point p) { APIsStructs.LVHITTESTINFO hittest = new APIsStructs.LVHITTESTINFO(); hittest.pt = new APIsStructs.POINTAPI(PointToClient(MousePosition)); APIsUser32.SendMessage( Handle, (Int32) APIsEnums.ListViewMessages.SUBITEMHITTEST, 0, ref hittest); return(hittest.iSubItem); }
/// <summary> /// WndProc /// </summary> /// <param name="m"></param> protected override void WndProc(ref System.Windows.Forms.Message m) { #region View messages if(false) { string val; val = Enum.GetName(typeof(APIsEnums.ListViewMessages), (APIsEnums.ListViewMessages) m.Msg); if(val != "" && val != null) Debug.WriteLine(val); else { val = Enum.GetName(typeof(APIsEnums.WindowMessages), (APIsEnums.WindowMessages) m.Msg); if(val != "" && val != null) Debug.WriteLine(val); } if(val != "" && val != null) Debug.WriteLine(m.Msg.ToString()); if(val == "LBUTTONDOWN") { int a= 0; a++; } } #endregion TreeListViewItem item = null; Rectangle rec; switch((APIsEnums.WindowMessages) m.Msg) { #region NOTIFY case APIsEnums.WindowMessages.NOTIFY: case (APIsEnums.WindowMessages) APIsEnums.ReflectedMessages.NOTIFY: APIsStructs.NMHDR nmhdr = (APIsStructs.NMHDR) m.GetLParam(typeof(APIsStructs.NMHDR)); APIsStructs.NMHEADER nmheader =(APIsStructs.NMHEADER) m.GetLParam(typeof(APIsStructs.NMHEADER)); switch((APIsEnums.ListViewNotifications) nmhdr.code) { #region APIsEnums.ListViewNotifications.MARQUEEBEGIN case APIsEnums.ListViewNotifications.MARQUEEBEGIN: if((MouseButtons & MouseButtons.Left) != MouseButtons.Left) m.Result = (IntPtr)1; else _hasMarquee = true; break; #endregion #region APIsEnums.ListViewNotifications.ITEMCHANGING case APIsEnums.ListViewNotifications.ITEMCHANGING: APIsStructs.NMLISTVIEW nmlistview = (APIsStructs.NMLISTVIEW) m.GetLParam(typeof(APIsStructs.NMLISTVIEW)); if(nmlistview.iItem < 0) break; if((item = GetTreeListViewItemFromIndex(nmlistview.iItem)) == null) break; bool cancel = false; if(nmlistview.Select) { if(_selectionMark == null) _selectionMark = item; else if(!_selectionMark.Visible) _selectionMark = item; if(HasMarquee) item.Focused = true; } else if(nmlistview.UnSelect && HasMarquee) { if(item.NextVisibleItem != null) if(item.NextVisibleItem.Selected) item.NextVisibleItem.Focused = true; if(item.PrevVisibleItem != null) if(item.PrevVisibleItem.Selected) item.PrevVisibleItem.Focused = true; } #region Select after dbl click // Disable the selection after a double click (normaly, if the control scrolls after // a collapse, the new item under the cursor is automatically selected...) if(_dblclicktime.AddMilliseconds(500).CompareTo(DateTime.Now) > 0 && (nmlistview.Select || nmlistview.Focus) && FocusedItem != item) cancel = true; #endregion #region Wrong Level Select if(((APIsEnums.ListViewItemStates)nmlistview.uNewState & APIsEnums.ListViewItemStates.SELECTED) == APIsEnums.ListViewItemStates.SELECTED && MultiSelect) if(SelectedIndices.Count > 0) if(GetTreeListViewItemFromIndex(nmlistview.iItem).Parent != SelectedItems[0].Parent) cancel = true; #endregion #region Check during selection // Disable check boxes check when : // - the Marquee selection tool is being used // - the Ctrl or Shift keys are down bool state = (nmlistview.uChanged & (uint)APIsEnums.ListViewItemFlags.STATE) == (uint)APIsEnums.ListViewItemFlags.STATE; bool ctrlKeyDown = (ModifierKeys & Keys.Control) == Keys.Control; bool shiftKeyDown = (ModifierKeys & Keys.Shift) == Keys.Shift; if((nmlistview.Check || nmlistview.UnCheck) && (HasMarquee || ctrlKeyDown || shiftKeyDown)) { // MessageBox.Show(this, // "uChanged = " + nmlistview->uChanged.ToString() + "\n\n" + // "uOld = " + nmlistview->uOldState.ToString() + "\n" + // "uNew = " + nmlistview->uChanged.ToString() + "\n\n" + // "OldCheck : " + (oldCheck ? "true" : "false") + "\n" + // "NewCheck : " + (newCheck ? "true" : "false")); cancel = true; } #endregion if(cancel) { m.Result = (IntPtr)1; return; } break; #endregion #region APIsEnums.ListViewNotifications.BEGINLABELEDIT case APIsEnums.ListViewNotifications.BEGINLABELEDIT: // Cancel label edit if the message is sent just after a double click if(_lastdoubleclick.AddMilliseconds(450) > DateTime.Now) { Message canceledit = Message.Create(Handle, (int) APIsEnums.ListViewMessages.CANCELEDITLABEL, IntPtr.Zero, IntPtr.Zero); WndProc(ref canceledit); m.Result = (IntPtr) 1; return; } item = _lastitemclicked.Item; item.EnsureVisible(); // Add subitems if needed while(item.SubItems.Count-1 < _lastitemclicked.ColumnIndex) item.SubItems.Add(""); TreeListViewBeforeLabelEditEventArgs beforeed = new TreeListViewBeforeLabelEditEventArgs( FocusedItem, _lastitemclicked.ColumnIndex, item.SubItems[_lastitemclicked.ColumnIndex].Text); OnBeforeLabelEdit(beforeed); if(beforeed.Cancel) { Message canceledit = Message.Create(Handle, (int) APIsEnums.ListViewMessages.CANCELEDITLABEL, IntPtr.Zero, IntPtr.Zero); WndProc(ref canceledit); m.Result = (IntPtr) 1; return; } _inedit = true; // Get edit handle Message mess = Message.Create(Handle, (int)APIsEnums.ListViewMessages.GETEDITCONTROL, IntPtr.Zero, IntPtr.Zero); WndProc(ref mess); IntPtr edithandle = mess.Result; _customedit = new CustomEdit(edithandle, this, beforeed.Editor); _editeditem = new EditItemInformations( FocusedItem, beforeed.ColumnIndex, FocusedItem.SubItems[beforeed.ColumnIndex].Text); m.Result = IntPtr.Zero; return; #endregion #region APIsEnums.ListViewNotifications.ENDLABELEDIT case APIsEnums.ListViewNotifications.ENDLABELEDIT: if(_customedit != null) _customedit.HideEditControl(); _customedit = null; _inedit = false; _editeditem = new EditItemInformations(); m.Result = IntPtr.Zero; return; #endregion #region CUSTOMDRAW case (APIsEnums.ListViewNotifications) APIsEnums.NotificationMessages.CUSTOMDRAW: base.WndProc(ref m); CustomDraw(ref m); return; #endregion #region BEGINSCROLL case APIsEnums.ListViewNotifications.BEGINSCROLL: _updating = true; break; #endregion #region ENDSCROLL case APIsEnums.ListViewNotifications.ENDSCROLL: _updating = false; // Disable display bug with vertical lines (slow...) // if(ShowPlusMinus) // { // DrawPlusMinusItemsLines(); // DrawPlusMinusItems(); // } break; #endregion #region APIsEnums.HeaderControlNotifications.BEGINDRAG case (APIsEnums.ListViewNotifications) APIsEnums.HeaderControlNotifications.BEGINDRAG: nmheader =(APIsStructs.NMHEADER) m.GetLParam(typeof(APIsStructs.NMHEADER)); if(nmheader.iItem == 0) { m.Result = (IntPtr)1; return; } break; #endregion #region APIsEnums.HeaderControlNotifications.ENDDRAG case (APIsEnums.ListViewNotifications) APIsEnums.HeaderControlNotifications.ENDDRAG: nmheader =(APIsStructs.NMHEADER) m.GetLParam(typeof(APIsStructs.NMHEADER)); // Get mouse position in header coordinates IntPtr headerHandle = (IntPtr) APIsUser32.SendMessage(Handle, (int) APIsEnums.ListViewMessages.GETHEADER, IntPtr.Zero, IntPtr.Zero); APIsStructs.POINTAPI pointapi = new APIsStructs.POINTAPI(MousePosition); APIsUser32.ScreenToClient(headerHandle, ref pointapi); // HeaderItem Rect APIsStructs.RECT headerItemRect = new APIsStructs.RECT(); APIsUser32.SendMessage(headerHandle, (int)APIsEnums.HeaderControlMessages.GETITEMRECT, 0, ref headerItemRect); int headerItemWidth = headerItemRect.right - headerItemRect.left; // Cancel the drag operation if the first column is moved // or destination is the first column if(pointapi.x <= headerItemRect.left + headerItemWidth / 2 || nmheader.iItem == 0) { m.Result = (IntPtr)1; return; } break; #endregion #region APIsEnums.HeaderControlNotifications.TRACK / ENDTRACK // case (APIsEnums.ListViewNotifications)APIsEnums.HeaderControlNotifications.TRACK: case (APIsEnums.ListViewNotifications)APIsEnums.HeaderControlNotifications.ENDTRACK: Invalidate(); break; #endregion } break; #endregion #region LBUTTONDOWN // Cancel the click on checkboxes if the item is not "checkable" case APIsEnums.WindowMessages.LBUTTONDOWN: if(Columns.Count == 0) break; // Set the clickeditem and column int colclicked = GetColumnAt(MousePosition); if(colclicked == -1) colclicked = 0; item = GetItemAtFullRow(PointToClient(MousePosition)); _lastitemclicked = new EditItemInformations(item, colclicked, ""); if(_selectionMark == null || !_selectionMark.Visible) _selectionMark = item; if(((APIsEnums.KeyStatesMasks)(int)m.WParam & APIsEnums.KeyStatesMasks.SHIFT) != APIsEnums.KeyStatesMasks.SHIFT && !(((APIsEnums.KeyStatesMasks)(int)m.WParam & APIsEnums.KeyStatesMasks.CONTROL) == APIsEnums.KeyStatesMasks.CONTROL && item.Parent != _selectionMark.Parent)) _selectionMark = item; // Get where the mouse has clicked APIsStructs.LVHITTESTINFO lvhittest = new APIsStructs.LVHITTESTINFO(); lvhittest.pt = new APIsStructs.POINTAPI(PointToClient(MousePosition)); APIsUser32.SendMessage(Handle, (Int32) APIsEnums.ListViewMessages.HITTEST, 0, ref lvhittest); if(item == null) break; // Plus / Minus click if(item.GetBounds(TreeListViewItemBoundsPortion.PlusMinus).Contains(PointToClient(MousePosition)) && ShowPlusMinus && item.Items.Count > 0 && Columns[0].Width > (item.Level+1)*SystemInformation.SmallIconSize.Width) { Focus(); if(item.IsExpanded) item.Collapse(); else item.Expand(); OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, PointToClient(MousePosition).X, PointToClient(MousePosition).Y, 0)); return; } // Cancel mouse click if multiselection on a wrong item if(SelectedIndices.Count > 0 && (((APIsEnums.KeyStatesMasks)(int)m.WParam & APIsEnums.KeyStatesMasks.SHIFT) == APIsEnums.KeyStatesMasks.SHIFT || ((APIsEnums.KeyStatesMasks)(int)m.WParam & APIsEnums.KeyStatesMasks.CONTROL) == APIsEnums.KeyStatesMasks.CONTROL) && MultiSelect) { if(_selectionMark.Parent == item.Parent && ((APIsEnums.KeyStatesMasks)(int)m.WParam & APIsEnums.KeyStatesMasks.SHIFT) == APIsEnums.KeyStatesMasks.SHIFT) { _updating = true; SetSelectedItemsRange(item, _selectionMark); // Prevent all item at the wrong level of being selected m.WParam = (IntPtr) APIsEnums.KeyStatesMasks.CONTROL; base.WndProc(ref m); item.Selected = true; _updating = false; DrawSelectedItemsFocusCues(); return; } } break; #endregion #region LBUTTONDBLCLK // Disable this notification to remove the auto-check when // the user double-click on an item and append the expand / collapse function case APIsEnums.WindowMessages.LBUTTONDBLCLK: _lastdoubleclick = DateTime.Now; if(FocusedItem != null) { item = FocusedItem; bool doExpColl = false; switch(ExpandMethod) { case TreeListViewExpandMethod.IconDbleClick: rec = item.GetBounds(ItemBoundsPortion.Icon); if(rec.Contains(PointToClient(MousePosition))) doExpColl = true; break; case TreeListViewExpandMethod.ItemOnlyDbleClick: rec = item.GetBounds(ItemBoundsPortion.ItemOnly); if(rec.Contains(PointToClient(MousePosition))) doExpColl = true; break; case TreeListViewExpandMethod.EntireItemDbleClick: rec = item.GetBounds(ItemBoundsPortion.Entire); if(rec.Contains(PointToClient(MousePosition))) doExpColl = true; break; default: break; } if(doExpColl) { _dblclicktime = DateTime.Now; Cursor = Cursors.WaitCursor; BeginUpdate(); if(item.IsExpanded) item.Collapse(); else item.Expand(); EndUpdate(); Cursor = Cursors.Default; } } OnDoubleClick(new EventArgs()); return; #endregion #region MOUSEMOVE case APIsEnums.WindowMessages.MOUSEMOVE: if((MouseButtons & MouseButtons.Left) != MouseButtons.Left && HasMarquee) _hasMarquee = false; break; #endregion #region UNICHAR, CHAR, KEYDOWN case APIsEnums.WindowMessages.UNICHAR: case APIsEnums.WindowMessages.CHAR: CharPressed((char) m.WParam); return; case APIsEnums.WindowMessages.KEYDOWN: OnKeyDown(new KeyEventArgs((Keys)(int) m.WParam)); return; #endregion #region PAINT case APIsEnums.WindowMessages.PAINT: if(InEdit && EditedItem.Item != null) { APIsStructs.RECT rect = new APIsStructs.RECT( EditedItem.Item.GetBounds(ItemBoundsPortion.Entire)); APIsUser32.ValidateRect(Handle, ref rect); } base.WndProc(ref m); DrawIntermediateStateItems(); DrawSelectedItemsFocusCues(); return; #endregion #region VSCROLL, HSCROLL, ENSUREVISIBLE case APIsEnums.WindowMessages.VSCROLL: case APIsEnums.WindowMessages.HSCROLL: case (APIsEnums.WindowMessages)APIsEnums.ListViewMessages.ENSUREVISIBLE: if(!Scrollable) { m.Result = (IntPtr)0; return; } break; #endregion } base.WndProc(ref m); }