1

This is an annoyance I am seeing with .NET WinForms mouse events. To reproduce, do the following:

  1. Left mouse down on a Button Control. (This changes the capture control to that button)
  2. Move mouse off of that button.
  3. While still holding on to the left mouse button, click the right mouse button. (This changes the capture control to whatever the mouse cursor is over now)
  4. Release the left mouse button. The button never receives the mouse up event. (The mouse up event goes to the current capture control.)

I only care about the left mouse button, but a user accidentally pressing the right button prevents the left-mouse-up event from getting back to the originally clicked button.

One possible solution is to use the MouseLeave or MouseCaptureChanged events to detect when the right click is happening off of the button and know when the capture control has changed. I have tried this, and it works pretty well.

Another way is to use MessageFilters and filter out right button events. But some buttons needs the right clicks, so you don't want to filter out ALL right clicks, just for buttons that need this feature.

So I am asking to see if anyone knows of a better solution. It would be nice if windows had some kind of flag to do the following: button1.RightMouseButtonCanChangeMouseCapture = false;

  • I do get the MouseUp event on the original control, but only AFTER the context menu is dismissed. – Idle_Mind Sep 10 '20 at 15:58
  • This is not something your app should handle. A User will have the same *experience* System-wide. I doubt your app should teach Users how to handle mouse clicks in graphic Systems. – Jimi Sep 10 '20 at 16:10
  • @Idle_Mind In my case, there is no context menu in the application. – Chris Miller Sep 10 '20 at 18:02
  • @Jimi I agree that it looks System-Wide from my testing. However, it's not good enough for me to tell the customer this is his problem and not mine. – Chris Miller Sep 10 '20 at 18:02
  • Explain, clearly, the facts. Don't suppose your clients are idiots. Don't treat them as such. Don't try to condition your app to be idiot-proof, it won't work. – Jimi Sep 10 '20 at 18:04
  • What are you doing in the MouseUp event? Can you give a bigger picture of the use case? – Idle_Mind Sep 10 '20 at 18:06
  • [The Murphy Laws](http://www.murphys-laws.com/murphy/murphy-technology.html): *Build a system that even a fool can use and only a fool will want to use it*. – Jimi Sep 10 '20 at 18:13
  • @Idle_Mind The application transmits audio when the button is pressed, and stops transmitting when released. It's a huge problem if the app starts to transmit audio and then never stops because the Mouse-Up event was never received because a Right-Click was accidently pushed in the emergency. There are multiple ways to fix this problem, but I was looking for a more correct way of configuring MouseCapture to only work for left mouse events, and not right mouse events. I was hoping I had missed something in the Windows API. – Chris Miller Sep 10 '20 at 18:21
  • I'd probably use the MessageFilter approach. Set a flag when the button itself is clicked, then reset it when the mouse is released, and raise a custom event that the form traps. Are you working in C# or VB.Net? – Idle_Mind Sep 10 '20 at 18:24
  • @Idle_Mind My current approach is similar. Use a MessageFilter to track the left button state, then throw out right/middle mouse button events only when the left button is down. The only downside is that I will never be able to use both left and right buttons at the same time, but I shouldn't ever need this. Working with C#. – Chris Miller Sep 10 '20 at 18:30
  • You could prevent moving the Cursor off of the button. On the left mouse down event, set a flag and store the current position. In the MouseMove handler check the flag and if set set the Cursor position to the stored value. – TnTinMn Sep 10 '20 at 19:33
  • 1
    @TnTinMn, if you're going to do that, then just set the [Cursor.Clip](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.cursor.clip?view=netcore-3.1) to the screen rectangle of the button. This will literally confine the cursor to that box. Then set it back to null on mouse up to release it. – Idle_Mind Sep 10 '20 at 19:41
  • @Idle_Mind, thanks for the info. I've never looked at that property before. – TnTinMn Sep 10 '20 at 19:52

1 Answers1

0

This seems to work well in my testing with the MessageFilter approach:

public partial class Form1 : Form
{

    private MyFilter mf;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        mf = new MyFilter();
        Application.AddMessageFilter(mf);
        mf.MouseLeftUp += Mf_MouseLeftUp;
    }

    private void button1_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            mf.ButtonDown = true;
            Console.WriteLine("Transmitting...");
        }            
    }

    private void Mf_MouseLeftUp()
    {
        Console.WriteLine("Transmission Stopped.");
    }

}

public class MyFilter : IMessageFilter
{

    public bool ButtonDown = false;

    private const int WM_LBUTTONUP = 0x0202;

    public event dlgMouseLeftUp MouseLeftUp;
    public delegate void dlgMouseLeftUp();

    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONUP:
                if (ButtonDown)
                {
                    ButtonDown = false;
                    MouseLeftUp?.Invoke();
                }
                break;

        }
        return false; // allow normal dispatching of messages
    }

}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40