4

I display a different ContextMenuStrip when I right click the ListView column header, and another inside 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;
        }
    }
}

This works very well. The problem is the FIRST TIME I right click inside the ListView - the headers contextMenuStrip is shown.

albert
  • 1,493
  • 1
  • 15
  • 33
  • I think there is another simple approach, I don't understand well the `WM_PARENTNOTIFY` and other messages in your code, it looks like that `WM_PARENTNOTIFY` is sent to your `ListView` and `WM_MOUSEACTIVATE` follows, which makes your `contextMenuSet=2`. You should be sure those messages should be sent at the time you want, I think this solution is not reliable and should be avoided unless you understand thoroughly in which cases `WM_PARENTNOTIFY` and `WM_MOUSEACTIVATE` are sent to your `ListView`. – King King Jul 24 '13 at 15:36
  • I neither understand well this code , I found it in codeproject (in commnents) and works well. http://www.codeproject.com/Articles/23330/Handling-Right-Click-Events-in-ListView-Column-Hea?msg=4435185#xx4435185xx – albert Jul 24 '13 at 15:38

2 Answers2

6

Relying on the activation state is too hacky. It is far simpler, the WM_CONTEXTMENU message passes the handle of the window that generated the message. So you can simply compare it to the handle of the listview. If it doesn't match then you know it was the header control:

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);
    }
}

Technically you should use LVM_GETHEADER but this should work just fine.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

I've tried finding a clean way to get Column Header Rectangle of a ListView to check if the Point at which user right-clicks is in a Column Header or not. However, I've just found that the Column Header Rectangle of a ListView seems to be revealed only in a DrawColumnHeader event handler. This solution is all what I can think of to help you out:

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);
    }
}
King King
  • 61,710
  • 16
  • 105
  • 130