ListView组标题单击 - 如何向ListView组标题添加上下文菜单?

时间:2017-01-23 21:51:07

标签: c# .net winforms listview contextmenu

我在WinForms应用程序中使用了ListView,该应用程序包含许多值以及一些组。组标题仅显示组的名称,因此我想在组标题中添加一个上下文菜单,其中包含“显示描述”项以显示该组的长摘要。

在谷歌搜索一段时间后,我只找到了具有此功能的第三方控件。

如何在不使用第三方软件的情况下将ContextMenu添加到群组标头?

2 个答案:

答案 0 :(得分:3)

您可以向services发送LVM_HITTEST条消息。当您将ListView传递给-1时,如果返回值大于wParam并且在结果中设置了-1,则LVHT_EX_GROUP_HEADER方法的返回值将点击组索引。

<强>实施

在下面的实现中,我已将SendMessage事件添加到GroupHeaderClick类。您可以通过这种方式简单地处理事件:

MyListView

以下是private void myListView1_GroupHeaderClick(object sender, int e) { //Show ContextMenuStrip here. Or just for example: MessageBox.Show(myListView1.Groups[e].Header); } 实施:

MyListView

答案 1 :(得分:1)

只有将组添加到项目之前,Reza Aghaei的解决方案才有效。如果在已经添加项目之后添加了一个组,则该组将失败,因为SendMessage函数返回错误的索引。它实际上返回ID而不是索引。这是ListView组件中的一个已知问题。想法是将返回的值与组ID进行比较。

public class WListView : ListView
{
    #region [ PInvoke ]

    private const int LVM_HITTEST = 0x1000 + 18;
    private const int LVM_SUBITEMHITTEST = 0x1000 + 57;
    private const int LVHT_EX_GROUP_HEADER = 0x10000000;

    [StructLayout(LayoutKind.Sequential)]
    private struct LVHITTESTINFO
    {
        public int pt_x;
        public int pt_y;
        public int flags;
        public int iItem;
        public int iSubItem;
        public int iGroup;
    }

    [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref LVHITTESTINFO ht);

    #endregion

    /// <summary>
    /// Occurs when a group is clicked.
    /// </summary>
    [Category("Behavior")]
    [Description("Occurs when a group header is clicked.")]
    public event EventHandler<ListViewGroupClickEventArgs> GroupClick;

    /// <summary>
    /// Raises the GroupClick event.
    /// </summary>
    /// <param name="e">Event arguments.</param>
    protected virtual void OnGroupHeaderClick(ListViewGroupClickEventArgs e)
    {
        GroupClick?.Invoke(this, e);
    }

    /// <summary>
    /// Raises the Control.MouseDoubleClick event.
    /// </summary>
    /// <param name="e">Event arguments.</param>
    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);

        var group = TestGroupHit(e);
        if (group == null)
        {
            return;
        }

        switch (e.Clicks)
        {
            case 1:
                OnGroupHeaderClick(new ListViewGroupClickEventArgs(group));
                break;
        }
    }

    private ListViewGroup TestGroupHit(MouseEventArgs e)
    {
        var ht = new LVHITTESTINFO { pt_x = e.X, pt_y = e.Y };
        var msg = View == System.Windows.Forms.View.Details ? LVM_SUBITEMHITTEST : LVM_HITTEST;
        var value = SendMessage(Handle, msg, -1, ref ht);

        if (value != -1 && (ht.flags & LVHT_EX_GROUP_HEADER) != 0)
        {
            return FindGroupByID(value);
        }

        return null;
    }

    private ListViewGroup FindGroupByID(int id)
    {
        foreach (ListViewGroup group in Groups)
        {
            if (group.ExtractID() == id)
            {
                return group;
            }
        }

        return null;
    }
}

属性Group.ID是非公共的。这是提取它的扩展名。

public static int ExtractID(this ListViewGroup group)
    {
        try
        {
            return (int) group
                .GetType()
                .GetProperty("ID", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(group, new object[0]);
        }
        catch 
        {
            return -1;
        }
    }

请注意,根据具体情况,反射可能会很耗时。