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();
        }
Exemple #2
0
            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);
            }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
        }
Exemple #6
0
        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);
            }
        }
Exemple #7
0
        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);
            }
        }
Exemple #8
0
            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;
                }
            }