0

The table here demonstrates that the DoubleClick event is raised when a user double-clicks either the left or right mouse button on a TreeView or ListView; I only want this event to be triggered on a left mouse button double-click. The remarks here show that DoubleClick is raised before MouseDoubleClick, so there is no way to cancel or short-circuit DoubleClicked.

Logically then, it would appear that the correct solution is to use MouseDoubleClick and not DoubleClick, but the remarks for the former state:

DoubleClick events are logically higher-level events of a control. They may be raised by other user actions, such as shortcut key combinations.

... which seems to imply that I should actually be using DoubleClick, but again I can't because that doesn't allow me to ignore or cancel the right mouse button double-clicks.

Is there any relatively simple way to prevent DoubleClick from firing on a right mouse button double-click, or do I need to get a bit dirty and manually handle Windows messages?

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
  • 1
    *"to get a bit dirty"* - it's not dirty to subclass in winforms, it's elegant ([proof](https://stackoverflow.com/a/6130543/1997232))! – Sinatr Apr 23 '19 at 09:15
  • It is too hard to get dirty, the WndProc() of a ListView uses too many private variables. The distinction between DoubleClick and MouseDoubleClick is driven a bit too far, it is for ListView which has no mechanism to generate a double click without a mouse. Derive your own class from ListView. Override OnDoubleClick and do nothing. Override OnMouseDoubleClick() and call either base.OnDoubleClick or base.OnMouseDoubleClick, depending on the button. Do keep in mind you are trying to solve a problem nobody has. – Hans Passant Apr 23 '19 at 22:07

1 Answers1

0

UPDATED

The thing happens in void WndProc(ref Message m). I tried to reproduce the problem with different control (PictureBox). Left double click gave:

protected virtual void WndProc(ref Message m)
{
    // ...
    switch (m.Msg)
    {
        // ...
        case 514:
            WmMouseUp(ref m, MouseButtons.Left, 1);
            return;
        // ...
    }
    // ...
}

And right double click resulted into call to:

protected virtual void WndProc(ref Message m)
{
    // ...
    switch (m.Msg)
    {
        // ...
        case 517:
            WmMouseUp(ref m, MouseButtons.Right, 1);
            return;
        // ...
    }
    // ...
}

Where void WmMouseUp(ref Message m, MouseButtons button, int clicks) looks like:

private void WmMouseUp(ref Message m, MouseButtons button, int clicks)
{
    // ...
    OnDoubleClick(new MouseEventArgs(button, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0));
    OnMouseDoubleClick(new MouseEventArgs(button, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0));
    // ...
}

Both OnDoubleClick and OnMouseDoubleClick fire with MouseEventArgs. That's why this solution works.

private void control_DoubleClick(object sender, EventArgs e)
{
    if ((e as MouseEventArgs)?.Button == MouseButtons.Right)
        return;

    // do your stuff
}

But situation is different with ListView:

protected virtual void WndProc(ref Message m)
{
    // ...
    switch (m.Msg)
    {
        // ...
        case 514:
        case 517: // m.Msg value
        case 520:
        {
            NativeMethods.LVHITTESTINFO lvhi = new NativeMethods.LVHITTESTINFO();
            int indexOfClickedItem = GetIndexOfClickedItem(lvhi);
            if (!base.ValidationCancelled && listViewState[262144] && indexOfClickedItem != -1)
            {
                listViewState[262144] = false;
                OnDoubleClick(EventArgs.Empty);
                OnMouseDoubleClick(new MouseEventArgs(downButton, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0));
            }
            if (!listViewState[524288])
            {
                OnMouseUp(new MouseEventArgs(downButton, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0));
                listViewState[1048576] = false;
            }
            ItemCollectionChangedInMouseDown = false;
            listViewState[524288] = true;
            base.CaptureInternal = false;
            return;
        }
        // ...
    }
    // ...
}

It fires OnDoubleClick with EventArgs.Empty. So first solution doesn't work.

Workaround could be using a patched ListView:

public class PatchedListView : ListView
{
    protected override void WndProc(ref Message m)
    {
        var suppress = m.Msg == 517 &&
                       Enum.TryParse(typeof(ListView).GetField("downButton", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this).ToString(),
                                     out MouseButtons mouseButtons) &&
                       mouseButtons == MouseButtons.Right;
        if (suppress)
            return;

        base.WndProc(ref m);
    }
}
CSDev
  • 3,177
  • 6
  • 19
  • 37