public unsafe void ListView_HeaderAlignment_GetGroupInfo_Success(string headerParam, HorizontalAlignment valueParam, int expectedAlignParam) { // Run this from another thread as we call Application.EnableVisualStyles. RemoteExecutor.Invoke((header, valueString, expectedAlignString) => { HorizontalAlignment value = (HorizontalAlignment)Enum.Parse(typeof(HorizontalAlignment), valueString); int expectedAlign = int.Parse(expectedAlignString); Application.EnableVisualStyles(); using var listView = new ListView(); var group1 = new ListViewGroup { Header = header }; listView.Groups.Add(group1); Assert.NotEqual(IntPtr.Zero, listView.Handle); group1.HeaderAlignment = value; Assert.Equal((IntPtr)1, User32.SendMessageW(listView.Handle, (User32.WM)LVM.GETGROUPCOUNT, IntPtr.Zero, IntPtr.Zero)); char *buffer = stackalloc char[256]; var lvgroup = new LVGROUPW { cbSize = (uint)sizeof(LVGROUPW), mask = LVGF.HEADER | LVGF.GROUPID | LVGF.ALIGN, pszHeader = buffer, cchHeader = 256 }; Assert.Equal((IntPtr)1, User32.SendMessageW(listView.Handle, (User32.WM)LVM.GETGROUPINFOBYINDEX, (IntPtr)0, ref lvgroup)); Assert.Equal(header, new string(lvgroup.pszHeader)); Assert.True(lvgroup.iGroupId >= 0); Assert.Equal(expectedAlign, (int)lvgroup.uAlign); }, headerParam, valueParam.ToString(), expectedAlignParam.ToString()).Dispose(); }
private unsafe int GetNativeGroupId() { LVGROUPW lvgroup = new LVGROUPW { cbSize = (uint)sizeof(LVGROUPW), mask = LVGF.GROUPID, }; return(User32.SendMessageW(_owningListView, (User32.WM)LVM.GETGROUPINFOBYINDEX, (IntPtr)CurrentIndex, ref lvgroup) == IntPtr.Zero ? -1 : lvgroup.iGroupId); }
public unsafe void ListViewGroup_Collapse_GetGroupInfo_Success() { // Run this from another thread as we call Application.EnableVisualStyles. using RemoteInvokeHandle invokerHandle = RemoteExecutor.Invoke(() => { foreach (object[] data in CollapsedState_TestData()) { Application.EnableVisualStyles(); using var listView = new ListView(); var group = new ListViewGroup(); listView.Groups.Add(group); Assert.NotEqual(IntPtr.Zero, listView.Handle); group.CollapsedState = (ListViewGroupCollapsedState)data[0]; ListViewGroupCollapsedState expectedCollapsedState = (ListViewGroupCollapsedState)data[1]; Assert.Equal((IntPtr)1, User32.SendMessageW(listView.Handle, (User32.WM)LVM.GETGROUPCOUNT, IntPtr.Zero, IntPtr.Zero)); var lvgroup = new LVGROUPW { cbSize = (uint)sizeof(LVGROUPW), mask = LVGF.STATE | LVGF.GROUPID, stateMask = LVGS.COLLAPSIBLE | LVGS.COLLAPSED }; Assert.Equal((IntPtr)1, User32.SendMessageW(listView.Handle, (User32.WM)LVM.GETGROUPINFOBYINDEX, (IntPtr)0, ref lvgroup)); Assert.True(lvgroup.iGroupId >= 0); Assert.Equal(expectedCollapsedState, group.CollapsedState); if (expectedCollapsedState == ListViewGroupCollapsedState.Default) { Assert.Equal(LVGS.NORMAL, lvgroup.state); } else if (expectedCollapsedState == ListViewGroupCollapsedState.Expanded) { Assert.Equal(LVGS.COLLAPSIBLE, lvgroup.state); } else { Assert.Equal(LVGS.COLLAPSIBLE | LVGS.COLLAPSED, lvgroup.state); } } }); // verify the remote process succeeded Assert.Equal(0, invokerHandle.ExitCode); }
public unsafe void SetGroupState(ListViewGroup group, LVGS state) { int groupId = GetGroupId(group); if (groupId < 0) { groupId = Groups.IndexOf(group) - _minGroupInsertionIndex; } var lvgroup = new LVGROUPW(); lvgroup.cbSize = (uint)sizeof(LVGROUPW); lvgroup.state = state; lvgroup.mask = LVGF.STATE; lvgroup.iGroupId = groupId; NativeMethods.SendMessageW(Handle, LVM_SETGROUPINFO, (IntPtr)groupId, ref lvgroup); }
public unsafe void ListViewGroup_Header_GetGroupInfo_Success() { // Run this from another thread as we call Application.EnableVisualStyles. using RemoteInvokeHandle invokerHandle = RemoteExecutor.Invoke(() => { foreach (object[] data in Header_GetGroupInfo_TestData()) { string value = (string)data[0]; string expected = (string)data[1]; Application.EnableVisualStyles(); using var listView = new ListView(); var group = new ListViewGroup(); listView.Groups.Add(group); Assert.NotEqual(IntPtr.Zero, listView.Handle); group.Header = value; Assert.Equal((IntPtr)1, User32.SendMessageW(listView.Handle, (User32.WM)LVM.GETGROUPCOUNT, IntPtr.Zero, IntPtr.Zero)); char *buffer = stackalloc char[256]; var lvgroup = new LVGROUPW { cbSize = (uint)sizeof(LVGROUPW), mask = LVGF.HEADER | LVGF.GROUPID | LVGF.ALIGN, pszHeader = buffer, cchHeader = 256 }; Assert.Equal((IntPtr)1, User32.SendMessageW(listView.Handle, (User32.WM)LVM.GETGROUPINFOBYINDEX, (IntPtr)0, ref lvgroup)); Assert.Equal(expected, new string(lvgroup.pszHeader)); Assert.True(lvgroup.iGroupId >= 0); Assert.Equal(LVGA.HEADER_LEFT | LVGA.FOOTER_LEFT, lvgroup.uAlign); } }); // verify the remote process succeeded Assert.Equal(0, invokerHandle.ExitCode); }
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 = 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]; SetGrpState(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); } unsafe void SetGrpState(ListViewGroup grp, LVGS state) { int groupId = GetGroupId(grp); if (groupId < 0) { groupId = Groups.IndexOf(grp) - _minGroupInsertionIndex; } var lvgroup = new LVGROUPW(); lvgroup.cbSize = (uint)sizeof(LVGROUPW); lvgroup.state = state; lvgroup.mask = LVGF.STATE; lvgroup.iGroupId = groupId; NativeMethods.SendMessageW(Handle, 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); } }
private unsafe void ReloadListView() { ListViewGroup group = listView1.Groups[0]; int id = (int)typeof(ListViewGroup).GetProperty("ID", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(group); IntPtr lParam = (IntPtr)id; LVGF mask = 0; mask |= LVGF.HEADER; mask |= LVGF.SUBTITLE; mask |= LVGF.FOOTER; mask |= LVGF.ALIGN; mask |= LVGF.DESCRIPTIONBOTTOM; mask |= LVGF.DESCRIPTIONTOP; mask |= LVGF.STATE; mask |= LVGF.TASK; mask |= LVGF.TITLEIMAGE; mask |= LVGF.EXTENDEDIMAGE; LVGS stateMask = LVGS.COLLAPSED | LVGS.COLLAPSIBLE | LVGS.NOHEADER | LVGS.SUBSETED | 0; LVGS state = 0; if (collapsibleCheckBox.Checked) { state |= LVGS.COLLAPSIBLE; } if (collapsedCheckBox.Checked) { state |= LVGS.COLLAPSED; } if (!showHeaderCheckBox.Checked) { state |= LVGS.NOHEADER; } if (subsetTitleCheckBox.Checked) { state |= LVGS.SUBSETED; } var lvgroup = new LVGROUPW { cbSize = (uint)sizeof(LVGROUPW), mask = mask, iGroupId = id, stateMask = stateMask, state = state, iTitleImage = (int)titleImageIndexNumericUpDown.Value, iExtendedImage = (int)extendedImageIndexNumericUpDown.Value, }; switch (headerAlignmentComboBox.SelectedIndex) { case 0: lvgroup.uAlign |= LVGA.HEADER_LEFT; break; case 1: lvgroup.uAlign |= LVGA.HEADER_CENTER; break; case 2: lvgroup.uAlign |= LVGA.HEADER_RIGHT; break; } switch (footerAlignmentComboBox.SelectedIndex) { case 0: lvgroup.uAlign |= LVGA.FOOTER_LEFT; break; case 1: lvgroup.uAlign |= LVGA.FOOTER_CENTER; break; case 2: lvgroup.uAlign |= LVGA.FOOTER_RIGHT; break; } fixed(char *pHeader = headerTextBox.Text) fixed(char *pFooter = footerTextBox.Text) fixed(char *pDescriptionBottom = descriptionBottomTextBox.Text) fixed(char *pDescriptionTop = descriptionTopTextBox.Text) fixed(char *pSubsetTitle = subsetTitleTextBox.Text) fixed(char *pSubtitle = subtitleTextBox.Text) fixed(char *pTask = taskTextBox.Text) { if (footerCheckBox.Checked && footerTextBox.Text.Length > 0) { lvgroup.cchFooter = footerTextBox.Text.Length; lvgroup.pszFooter = pFooter; } if (descriptionBottomCheckBox.Checked) { lvgroup.cchDescriptionBottom = (uint)descriptionBottomTextBox.Text.Length; lvgroup.pszDescriptionBottom = pDescriptionBottom; } if (descriptionTopCheckBox.Checked) { lvgroup.cchDescriptionTop = (uint)descriptionTopTextBox.Text.Length; lvgroup.pszDescriptionTop = pDescriptionTop; } if (subsetTitleCheckBox.Checked) { lvgroup.cchSubsetTitle = (uint)subsetTitleTextBox.Text.Length; lvgroup.pszSubsetTitle = pSubsetTitle; } if (subtitleCheckBox.Checked) { lvgroup.cchSubsetTitle = (uint)subtitleTextBox.Text.Length; lvgroup.pszSubtitle = pSubtitle; } if (taskCheckBox.Checked && taskTextBox.Text.Length > 0) { lvgroup.cchTask = (uint)taskTextBox.Text.Length; lvgroup.pszTask = pTask; } lvgroup.pszHeader = pHeader; IntPtr result = User32.SendMessageW(listView1.Handle, (User32.WindowMessage)LVM.SETGROUPINFO, lParam, ref lvgroup); listView1.Invalidate(true); } }
protected unsafe override void WndProc(ref Message m) { switch (m.Msg) { case (int)LVM.HITTEST: case (int)LVM.GETITEM: case (int)LVM.GETITEMRECT: case (int)LVM.GETITEMCOUNT: case (int)LVM.GETITEMSTATE: case (int)LVM.GETNEXTITEM: case (int)LVM.FINDITEM: case (int)LVM.UPDATE: case (int)LVM.SETTEXTBKCOLOR: case (int)LVM.SETTEXTCOLOR: case (int)LVM.SETBKCOLOR: case (int)LVM.ENABLEGROUPVIEW: case (int)LVM.SETIMAGELIST: case (int)LVM.SETITEMSTATE: case (int)LVM.SETVIEW: case (int)User32.WindowMessage.WM_NCCREATE: case (int)User32.WindowMessage.WM_NCCALCSIZE: case (int)User32.WindowMessage.WM_CREATE: case (int)User32.WindowMessage.WM_MOUSEHOVER: case (int)User32.WindowMessage.WM_MOUSELEAVE: case (int)User32.WindowMessage.WM_MOUSEMOVE: case (int)User32.WindowMessage.WM_NCHITTEST: case (int)User32.WindowMessage.WM_SETCURSOR: case (int)User32.WindowMessage.WM_SETREDRAW: case (int)User32.WindowMessage.WM_KILLFOCUS: case (int)User32.WindowMessage.WM_PAINT: case (int)User32.WindowMessage.WM_ERASEBKGND: case (int)User32.WindowMessage.WM_CAPTURECHANGED: case (int)User32.WindowMessage.WM_SIZE: case (int)User32.WindowMessage.WM_MOVE: case (int)User32.WindowMessage.WM_IME_SETCONTEXT: case (int)User32.WindowMessage.WM_IME_NOTIFY: case (int)User32.WindowMessage.WM_SETFOCUS: base.WndProc(ref m); break; case (int)User32.WindowMessage.WM_LBUTTONUP: base.DefWndProc(ref m); break; case (int)User32.WindowMessage.WM_REFLECT_NOTIFY: User32.NMHDR *nmhdr = (User32.NMHDR *)m.LParam; switch (nmhdr->code) { case (int)LVN.LINKCLICK: NMLVLINK *link = (NMLVLINK *)m.LParam; string task = "Clicked"; var group = new LVGROUPW { cbSize = (uint)sizeof(LVGROUPW), mask = LVGF.TASK, cchTask = (uint)task.Length }; fixed(char *pTask = "Clicked") { group.pszTask = pTask; User32.SendMessageW(Handle, (User32.WindowMessage)LVM.SETGROUPINFO, (IntPtr)link->iSubItem, ref group); MessageBox.Show("Link Clicked"); } break; case (int)LVN.GETDISPINFOW: case (int)LVN.ITEMCHANGING: case (int)LVN.ITEMCHANGED: case (int)LVN.INSERTITEM: case (int)LVN.MARQUEEBEGIN: case (int)LVN.HOTTRACK: case (int)NM.SETFOCUS: case (int)NM.KILLFOCUS: case (int)NM.CUSTOMDRAW: case (int)NM.RELEASEDCAPTURE: case (int)NM.CLICK: default: break; } base.WndProc(ref m); break; default: //Debug.WriteLine((User32.WindowMessage)m.Msg); base.WndProc(ref m); break; } }