列表视图列标题的上下文菜单。

4
我右键单击 ListView 列标题时会显示不同的 ContextMenuStrip,而在 ListView 内部则会显示另一个。
class ListViewExx : ListView
{
    public ContextMenuStrip HeaderContextMenu { get; set; }
    int contextMenuSet = 0;
    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        base.WndProc(ref m);
        switch(m.Msg)
        {
            case 0x210: //WM_PARENTNOTIFY
                contextMenuSet = 1;
                break;
            case 0x21:  //WM_MOUSEACTIVATE
                contextMenuSet++;
                break;
            case 0x7b:  //WM_CONTEXTMENU
                if(contextMenuSet == 2 && HeaderContextMenu != null)
                    HeaderContextMenu.Show(Control.MousePosition);
                break;
        }
    }
}

这个很好用。问题是,我第一次在列表视图中右键单击时,标题的上下文菜单会显示。


我认为还有另一种简单的方法。我不太理解你代码中的WM_PARENTNOTIFY和其他消息,看起来WM_PARENTNOTIFY被发送到了你的ListView,然后是WM_MOUSEACTIVATE,这使得你的contextMenuSet=2。你应该确保这些消息在你想要的时候被发送,我认为这种解决方案不可靠,除非你彻底理解WM_PARENTNOTIFYWM_MOUSEACTIVATE在哪些情况下会被发送到你的ListView - King King
我对这段代码的理解不是很好,但我在codeproject(评论中)找到它,并且它能正常运行。http://www.codeproject.com/Articles/23330/Handling-Right-Click-Events-in-ListView-Column-Hea?msg=4435185#xx4435185xx - albert
2个回答

6

依赖于激活状态太过生硬。更简单的方法是,WM_CONTEXTMENU消息会传递生成该消息的窗口句柄。因此,您只需将其与列表视图的句柄进行比较即可。如果不匹配,则知道它是标题控件:

protected override void WndProc(ref System.Windows.Forms.Message m)
{
    base.WndProc(ref m);
    if (m.Msg == 0x7b) {  //WM_CONTEXTMENU
        if (m.WParam != this.Handle) HeaderContextMenu.Show(Control.MousePosition);
    }
}

从技术上讲,您应该使用LVM_GETHEADER,但这样做也应该可以正常工作。


1
OP希望在右键单击ListView的Column Headers时仅显示“HeaderContextMenu”,但似乎当用户在ListView的任何点上右键单击时都会显示它? - King King
现在无论我在哪里点击,都会显示标题菜单。 - albert
糟糕,应该是WParam而不是LParam。 - Hans Passant
对于这个很棒的答案点赞,它似乎比在codeproject上作为文章写的解决方案要简单得多? - King King

1
我尝试找到一种简洁的方法来获取ListView的列标题矩形,以检查用户右键单击的点是否在列标题中。然而,我发现ListView的Column Header Rectangle似乎只在DrawColumnHeader事件处理程序中显示。这是我能想到的帮助您的解决方案。
public class CustomListView : ListView
{
    //This contains the Column Index and its corresponding Rectangle in screen coordinates.
    Dictionary<int, Rectangle> columns = new Dictionary<int, Rectangle>();
    public CustomListView()
    {
        OwnerDraw = true;//This will help the OnDrawColumnHeader be called.
    }
    protected override void OnDrawItem(DrawListViewItemEventArgs e)
    {
        e.DrawDefault = true;
        base.OnDrawItem(e);
    }
    protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
    {
        e.DrawDefault = true;
        base.OnDrawSubItem(e);
    }
    protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
    {
        columns[e.ColumnIndex] = RectangleToScreen(e.Bounds);
        e.DrawDefault = true;
        base.OnDrawColumnHeader(e);
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x7b)//WM_CONTEXTMENU
        {
            int lp = m.LParam.ToInt32();
            int x = ((lp << 16) >> 16);
            int y = lp >> 16;
            foreach (KeyValuePair<int, Rectangle> p in columns)
            {
                if (p.Value.Contains(new Point(x, y)))
                {
                    //MessageBox.Show(Columns[p.Key].Text); <-- Try this to test if you want.
                    //Show your HeaderContextMenu corresponding to a Column here.
                    break;
                }
            }                
        }
        base.WndProc(ref m);
    }
}

1
至少这个解决方案可以在每列需要不同的“上下文菜单”时起作用 :) - King King

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接