protected override void WndProc(ref Message m) { var message = m; var groupHitInfo = new Lazy <ListViewGroupHitInfo>(() => GetGroupHitInfo(message)); switch (m.Msg) { case NativeMethods.WM_LBUTTONUP when groupHitInfo.Value?.IsCollapseButton == true: DefWndProc(ref m); // collapse / expand by clicking button in group header break; case NativeMethods.WM_PAINT: _isInWmPaintMsg = true; base.WndProc(ref m); _isInWmPaintMsg = false; break; case NativeMethods.WM_REFLECT_NOTIFY when IsCustomDraw(m) && !_isInWmPaintMsg: case NativeMethods.WM_RBUTTONUP when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Right, isDown: false) : case NativeMethods.WM_RBUTTONDOWN when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Right, isDown: true) : case NativeMethods.WM_LBUTTONUP when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Left, isDown: false) : case NativeMethods.WM_LBUTTONDOWN when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Left, isDown: true) : break; default: base.WndProc(ref m); break; } bool IsGroupMouseEventHandled(ListViewGroupHitInfo hitInfo, MouseButtons button, bool isDown) { if (hitInfo == null) { return(false); } var eventArgs = new ListViewGroupMouseEventArgs(button, hitInfo, 1, 0); if (isDown) { GroupMouseDown?.Invoke(this, eventArgs); } else { GroupMouseUp?.Invoke(this, eventArgs); } return(eventArgs.Handled); } bool IsCustomDraw(Message msg) { var nmhdr = (NativeMethods.NMHDR)msg.GetLParam(typeof(NativeMethods.NMHDR)); return(nmhdr.code == NativeMethods.NM_CUSTOMDRAW); } }
protected override void WndProc(ref Message m) { var message = m; switch (m.Msg) { case WM_LBUTTONUP when GetGroupHitInfo(message.LParam.ToPoint())?.IsCollapseButton == true: DefWndProc(ref m); // collapse / expand by clicking button in group header break; case LVM_INSERTGROUP: base.WndProc(ref m); HandleAddedGroup(m); break; case WM_RBUTTONUP when IsGroupMouseEventHandled(MouseButtons.Right, isDown: false) : case WM_RBUTTONDOWN when IsGroupMouseEventHandled(MouseButtons.Right, isDown: true) : case WM_LBUTTONUP when IsGroupMouseEventHandled(MouseButtons.Left, isDown: false) : case WM_LBUTTONDOWN when IsGroupMouseEventHandled(MouseButtons.Left, isDown: true) : break; default: HandleScroll(m); base.WndProc(ref m); break; } void HandleScroll(Message msg) { ScrollEventType type; int? newValue = null; switch (msg.Msg) { case WM_VSCROLL: type = (ScrollEventType)LowWord(msg.WParam.ToInt64()); newValue = HighWord(msg.WParam.ToInt64()); break; case WM_MOUSEWHEEL: type = HighWord(msg.WParam.ToInt64()) > 0 ? ScrollEventType.SmallDecrement : ScrollEventType.SmallIncrement; break; case WM_KEYDOWN: switch ((Keys)msg.WParam.ToInt32()) { case Keys.Up: type = ScrollEventType.SmallDecrement; break; case Keys.Down: type = ScrollEventType.SmallIncrement; break; case Keys.PageUp: type = ScrollEventType.LargeDecrement; break; case Keys.PageDown: type = ScrollEventType.LargeIncrement; break; case Keys.Home: type = ScrollEventType.First; break; case Keys.End: type = ScrollEventType.Last; break; default: return; } break; default: return; } newValue ??= GetScrollPos(Handle, SB.VERT); Scroll?.Invoke(this, new ScrollEventArgs(type, newValue.Value)); short LowWord(long number) => unchecked ((short)(number & 0x0000ffff)); short HighWord(long number) => unchecked ((short)(number >> 16)); } void HandleAddedGroup(Message msg) { if (!IsGroupStateSupported || !AllowCollapseGroups) { return; } int groupIndex = GetGroupIndex(); if (groupIndex < _minGroupInsertionIndex) { return; } var listViewGroup = Groups[groupIndex]; SetGroupState(listViewGroup, LVGS.Collapsible); Invalidate(); int GetGroupIndex() { // https://docs.microsoft.com/en-us/windows/desktop/controls/lvm-insertgroup int index = msg.WParam.ToInt32(); if (index == -1) { // -1 because addition already happened return(Groups.Count - 1); } return(index - 1 + _minGroupInsertionIndex); } } bool IsGroupMouseEventHandled(MouseButtons button, bool isDown) { var hitInfo = GetGroupHitInfo(message.LParam.ToPoint()); if (hitInfo is null) { return(false); } var eventArgs = new ListViewGroupMouseEventArgs(button, hitInfo, 1, 0); if (isDown) { GroupMouseDown?.Invoke(this, eventArgs); } else { GroupMouseUp?.Invoke(this, eventArgs); } return(eventArgs.Handled); } }
protected override void WndProc(ref Message m) { var message = m; switch (m.Msg) { case NativeMethods.WM_LBUTTONUP when GetGroupHitInfo(message.LParam.ToPoint())?.IsCollapseButton == true: DefWndProc(ref m); // collapse / expand by clicking button in group header break; case NativeMethods.LVM_INSERTGROUP: base.WndProc(ref m); HandleAddedGroup(m); break; case NativeMethods.WM_PAINT: _isInWmPaintMsg = true; base.WndProc(ref m); _isInWmPaintMsg = false; break; case NativeMethods.WM_REFLECT_NOTIFY when IsCustomDraw(m) && !_isInWmPaintMsg: case NativeMethods.WM_RBUTTONUP when IsGroupMouseEventHandled(MouseButtons.Right, isDown: false) : case NativeMethods.WM_RBUTTONDOWN when IsGroupMouseEventHandled(MouseButtons.Right, isDown: true) : case NativeMethods.WM_LBUTTONUP when IsGroupMouseEventHandled(MouseButtons.Left, isDown: false) : case NativeMethods.WM_LBUTTONDOWN when IsGroupMouseEventHandled(MouseButtons.Left, isDown: true) : break; default: base.WndProc(ref m); break; } HandleScroll(m); void HandleScroll(Message msg) { ScrollEventType type; int? newValue = null; switch (msg.Msg) { case NativeMethods.WM_VSCROLL: type = (ScrollEventType)(msg.WParam.ToInt32() & 0xffff); newValue = msg.WParam.ToInt32() & 0x0000ffff; break; case NativeMethods.WM_MOUSEWHEEL: type = ScrollEventType.EndScroll; break; case NativeMethods.WM_KEYDOWN: switch ((Keys)msg.WParam.ToInt32()) { case Keys.Up: type = ScrollEventType.SmallDecrement; break; case Keys.Down: type = ScrollEventType.SmallIncrement; break; case Keys.PageUp: type = ScrollEventType.LargeDecrement; break; case Keys.PageDown: type = ScrollEventType.LargeIncrement; break; case Keys.Home: type = ScrollEventType.First; break; case Keys.End: type = ScrollEventType.Last; break; default: return; } break; default: return; } newValue = newValue ?? NativeMethods.GetScrollPos(Handle, NativeMethods.SB_VERT); Scroll?.Invoke(this, new ScrollEventArgs(type, newValue.Value)); } void HandleAddedGroup(Message msg) { if (!IsGroupStateSupported || !AllowCollapseGroups) { return; } int groupIndex = GetGroupIndex(); if (groupIndex < _minGroupInsertionIndex) { return; } var listViewGroup = Groups[groupIndex]; SetGrpState(listViewGroup, ListViewGroupState.Collapsible); Invalidate(); int GetGroupIndex() { // https://docs.microsoft.com/en-us/windows/desktop/controls/lvm-insertgroup int index = msg.WParam.ToInt32(); if (index == -1) { // -1 because addition already happened return(Groups.Count - 1); } return(index - 1 + _minGroupInsertionIndex); } void SetGrpState(ListViewGroup grp, ListViewGroupState state) { int groupId = GetGroupId(grp); if (groupId < 0) { groupId = Groups.IndexOf(grp) - _minGroupInsertionIndex; } var lvgroup = new NativeMethods.LVGROUP(); lvgroup.CbSize = Marshal.SizeOf(lvgroup); lvgroup.State = state; lvgroup.Mask = NativeMethods.ListViewGroupMask.State; lvgroup.IGroupId = groupId; var handleRef = new HandleRef(this, Handle); NativeMethods.SendMessage(handleRef, NativeMethods.LVM_SETGROUPINFO, (IntPtr)groupId, ref lvgroup); } } bool IsGroupMouseEventHandled(MouseButtons button, bool isDown) { var hitInfo = GetGroupHitInfo(message.LParam.ToPoint()); if (hitInfo == null) { return(false); } var eventArgs = new ListViewGroupMouseEventArgs(button, hitInfo, 1, 0); if (isDown) { GroupMouseDown?.Invoke(this, eventArgs); } else { GroupMouseUp?.Invoke(this, eventArgs); } return(eventArgs.Handled); } bool IsCustomDraw(Message msg) { var nmhdr = (NativeMethods.NMHDR)msg.GetLParam(typeof(NativeMethods.NMHDR)); return(nmhdr.code == NativeMethods.NM_CUSTOMDRAW); } }
protected override void WndProc(ref Message m) { var message = m; var groupHitInfo = new Lazy <ListViewGroupHitInfo>(() => GetGroupHitInfo(message)); switch (m.Msg) { case NativeMethods.WM_LBUTTONUP when groupHitInfo.Value?.IsCollapseButton == true: DefWndProc(ref m); // collapse / expand by clicking button in group header break; case NativeMethods.LVM_INSERTGROUP: base.WndProc(ref m); HandleAddedGroup(m); break; case NativeMethods.WM_PAINT: _isInWmPaintMsg = true; base.WndProc(ref m); _isInWmPaintMsg = false; break; case NativeMethods.WM_REFLECT_NOTIFY when IsCustomDraw(m) && !_isInWmPaintMsg: case NativeMethods.WM_RBUTTONUP when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Right, isDown: false) : case NativeMethods.WM_RBUTTONDOWN when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Right, isDown: true) : case NativeMethods.WM_LBUTTONUP when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Left, isDown: false) : case NativeMethods.WM_LBUTTONDOWN when IsGroupMouseEventHandled(groupHitInfo.Value, MouseButtons.Left, isDown: true) : break; default: base.WndProc(ref m); break; } void HandleAddedGroup(Message msg) { if (!IsGroupStateSupported || !AllowCollapseGroups) { return; } int groupIndex = GetGroupIndex(); if (groupIndex < _minGroupInsertionIndex) { return; } var listViewGroup = Groups[groupIndex]; SetGrpState(listViewGroup, ListViewGroupState.Collapsible); Invalidate(); int GetGroupIndex() { // https://docs.microsoft.com/en-us/windows/desktop/controls/lvm-insertgroup int index = msg.WParam.ToInt32(); if (index == -1) { // -1 because addition already happened return(Groups.Count - 1); } return(index - 1 + _minGroupInsertionIndex); } void SetGrpState(ListViewGroup grp, ListViewGroupState state) { int groupId = GetGroupId(grp); if (groupId < 0) { groupId = Groups.IndexOf(grp) - _minGroupInsertionIndex; } var lvgroup = new NativeMethods.LVGROUP(); lvgroup.CbSize = Marshal.SizeOf(lvgroup); lvgroup.State = state; lvgroup.Mask = NativeMethods.ListViewGroupMask.State; lvgroup.IGroupId = groupId; var handleRef = new HandleRef(this, Handle); NativeMethods.SendMessage(handleRef, NativeMethods.LVM_SETGROUPINFO, (IntPtr)groupId, ref lvgroup); } } bool IsGroupMouseEventHandled(ListViewGroupHitInfo hitInfo, MouseButtons button, bool isDown) { if (hitInfo == null) { return(false); } var eventArgs = new ListViewGroupMouseEventArgs(button, hitInfo, 1, 0); if (isDown) { GroupMouseDown?.Invoke(this, eventArgs); } else { GroupMouseUp?.Invoke(this, eventArgs); } return(eventArgs.Handled); } bool IsCustomDraw(Message msg) { var nmhdr = (NativeMethods.NMHDR)msg.GetLParam(typeof(NativeMethods.NMHDR)); return(nmhdr.code == NativeMethods.NM_CUSTOMDRAW); } }