Why do I lose mouse events over a picturebox ?

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
This is regarding a WinForms application written in c# using .Net 4.7.2.

I have a form that has a Top/Down splitter panel. The lower split panel contains Left/Right splitter panel. The Right split panel contains two panels, only one of which is visible at any given time: one contains a flowpanel containing image thumbnails and the other (which I will call the "imagepanel") contains a picturebox with Docking set to full. Right-clicking on a thumbnail hides the flowpanel and displays the imagepanel with the selected thumbnail shown in full in the picturebox. Right-clicking on the PictureBox hides the imagepanel and displays the flowpanel.

When I right-click on a thumbnail for the very first time after running my application, the MouseMove event for the picturebox fires as expected. I have a test label elsewhere that displays the coordinates of the cursor that demonstrates that the MouseMove event is firing. When I right-click on the Picturebox to go back to the flow panel and click on another thumbnail (or the same thumbnail), the MouseMove event never fires again unless I physically left-click on the picturebox.

I have tried setting Focus to the picturebox with PictureBox1.Focus();

I have tried selecting the picturebox with PictureBox1.Select();

I have tried simulating a left mouse-click. I know the simulated mouse click works because when I simulate the left mouse click over a button, it fires the Click event for the button. When I simulate the mouse click over the picturebox, it has no effect.

When I write this.ActiveControl.Name to the console in a variety of places, I always get the name of the Top/Down splitter panel, including the Click event of the PictureBox.

Why am I losing MouseMove events over the picturebox without having to click on it again ? I don't want my users to have to add this extra step.

I have tried creating just a simple application with minimal code to attempt to reproduce the issue, but, of course, I couldn't reproduce the issue.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
If a minimal application to try to reproduce the problem isn't reproducing the problem, then there's obviously something in the original app that is causing the issue. Try running Spy++ to see where the actual Windows messages are getting sent to in particular situations. If I have to guess the Z-order of the windows maybe messed up in the failure case.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
If a minimal application to try to reproduce the problem isn't reproducing the problem, then there's obviously something in the original app that is causing the issue. Try running Spy++ to see where the actual Windows messages are getting sent to in particular situations. If I have to guess the Z-order of the windows maybe messed up in the failure case.
Thank you for your response. I will try running Spy++ as you suggest.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
I tried running Spy++. Not sure exactly what I was looking for, but I didn't see anything that I thought would be helpful. Most of the threads had blank messages.
 

JohnH

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
1,194
Location
Norway
Programming Experience
10+
Why are you using MouseMove event to detect mouse click? Shouldn't you use MouseClick event?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Alas, the art of using Spy++ is slowly getting lost even amongst Windows developers... Things to check:
1) If your app is 64-bit, use the 64-bit version of Spy++ (spyxx_amd64.exe) instead of the 32-bit version (spyxx.exe).

2) Unless you know which specific window and window message you want to watch, it's usually better to watch the the messages sent to the topmost window, and all so watch child window messages. For example, here I'm watching Notepad.exe and all the messages sent to it and it's child windows:
Screenshot_1.png


3) To not get too flooded with all the messages going by, cherry-pick the specific message, or class of messages that you are interested in. For example here, I'm only interest in the mouse messages:
Screenshot_2.png


4) Note that the second column of the captured messages is the window handle of window that is being sent the message:
Screenshot_3.png


5) You can use that handle to figure out what window that is in the app window hierarchy:
Screenshot_4.png
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
I don't think the OP is trying to use the mouse move to detect a click. It looks like he is using the mouse move to track the current cursor position. Anyway all the clicks he is talking about seems to be his attempts to workaround the problem by forcing whatever window to get placed correctly in the window ordering so that it will get the mouse move messages again after it was hidden away for a short period.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
I compiled ways of determining the control from a variety of sources. All of these return the PictureBox as the control with the focus, regardless of when I call the ShowFocus() method below. So apparently it is not a "Focus" problem.

C#:
private void ShowFocus()
    {
        var _C_ = _get_all_controls(this);

        foreach (Control c in _C_)
        {
            if (c.Focused)
                Console.WriteLine(c.Name + " is focused now (ALL)");
        }

        foreach (Control c in this.Controls)
        {
            if (c.Focused)
                Console.WriteLine(c.Name + " is focused now (FORM)");
        }


        Control fc = GetFocusedControl();

        if (fc != null)
            Console.WriteLine("Focused Control: " + fc.Name);
    }

    private IEnumerable<Control> _get_all_controls(Control c)
    {
        return c.Controls.Cast<Control>().SelectMany(item =>
            _get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
            control.Name != string.Empty);
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
    internal static extern IntPtr GetFocus();

    public static Control GetFocusedControl()
    {
        Control focusedControl = null;
        // To get hold of the focused control:
        IntPtr focusedHandle = GetFocus();
        if (focusedHandle != IntPtr.Zero)
            // Note that if the focused Control is not a .Net control, then this will return null.
            focusedControl = Control.FromHandle(focusedHandle);
        return focusedControl;
    }
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
It's not a matter of who has the focus. It's matter of who has the mouse capture:
Posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the cursor. Otherwise, the message is posted to the window that has captured the mouse.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
This is the Spy++ output immediately after I click the thumbnail (when the mouse events do not work) and after I left-click the picturebox (LBUTTONDOWN).
I cannot see any difference at all. The mouse MOUSEMOVE event is still firing and coordinates are showing (which match the coordinates I am showing
on a label on my application). The handle (004D1F54) is the same before and after.
1621523398484.png
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
At least that shows that it's not a disappearing window on the WM_MOUSEMOVE being sent to another window. I assume that window handle is the window handle for the PictureBox.

The only thing I can think of is for debugging purposes to try to override the WndProc() of that picture box to see if the WM_MOUSEMOVE messages are getting to it on the C# side. But all that will show is whether the message is either making it there, or not making it there. Obviously if it's not making it there, the MouseMove event won't fire, but I don't have an recommendations on how to fix things so that the message does make it there. If the messages are arriving, but the event is not being recieved, I also don't have an recommendations on how to fix things other than double check that the object that has the mouse move event handler has not been accidentally destroyed or unregistered.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
At least that shows that it's not a disappearing window on the WM_MOUSEMOVE being sent to another window. I assume that window handle is the window handle for the PictureBox.

The only thing I can think of is for debugging purposes to try to override the WndProc() of that picture box to see if the WM_MOUSEMOVE messages are getting to it on the C# side. But all that will show is whether the message is either making it there, or not making it there. Obviously if it's not making it there, the MouseMove event won't fire, but I don't have an recommendations on how to fix things so that the message does make it there. If the messages are arriving, but the event is not being recieved, I also don't have an recommendations on how to fix things other than double check that the object that has the mouse move event handler has not been accidentally destroyed or unregistered.
The mouse move event is being correctly handled as soon as I left-click on the picture box. Even though events are obviously occurring, as shown by Spy++, somehow the picturebox isn't picking it up until after I click on it. This whole thing is very frustrating.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Given that WM_MOUSEMOVE messages are posted (not sent) see the 'P' instead of 'S' in Spy++, it affects the way messages processed in the message queue. By any chance are you calling Application.DoEvents() or Form.ShowDialog() at some point? These tend to change the way messages are pumped and processed.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
Given that WM_MOUSEMOVE messages are posted (not sent) see the 'P' instead of 'S' in Spy++, it affects the way messages processed in the message queue. By any chance are you calling Application.DoEvents() or Form.ShowDialog() at some point? These tend to change the way messages are pumped and processed.
There are no Application.DoEvents() or Form.ShowDialog() calls. Just hiding and unhiding of panels.
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
The strangest thing....

Up until now, on the flow panel that contains the thumbnails, I was left-clicking to select an image and right-clicking to zoom the image (by hiding the flow panel and showing the picturebox.

When I switch this around and left-click to zoom the image and right-click to select an image, I no longer have the mousemove issue and everything is working as expected.

Why would this be happening because of the button used? Can this be remediated?
 

richardinwpb

Member
Joined
May 19, 2021
Messages
10
Programming Experience
10+
The strangest thing....

Up until now, on the flow panel that contains the thumbnails, I was left-clicking to select an image and right-clicking to zoom the image (by hiding the flow panel and showing the picturebox.

When I switch this around and left-click to zoom the image and right-click to select an image, I no longer have the mousemove issue and everything is working as expected.

Why would this be happening because of the button used? Can this be remediated?
I mentioned in my original post that I was simulating a Left Mouse Click, but that it hadn't worked.

When I put everything back the way it used to be...

And I added two consecutive LeftMouseClicks...

It worked!!!

This is the LeftMouseClick code:

C#:
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern bool SetCursorPos(int x, int y);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);

        public const int MOUSEEVENTF_LEFTDOWN = 0x02;
        public const int MOUSEEVENTF_LEFTUP = 0x04;

        //This simulates a left mouse click
        public static void LeftMouseClick(int xpos, int ypos)
        {
            SetCursorPos(xpos, ypos);
            mouse_event(MOUSEEVENTF_LEFTDOWN, xpos, ypos, 0, 0);
            mouse_event(MOUSEEVENTF_LEFTUP, xpos, ypos, 0, 0);
        }
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Glad you found a workaround.

Usually the left click also invokes window activation behavior. But in your case the window is already activated so everything should be happy. On the other hand, it's quite possible there is a corner case not covered in WinForms that is hit by you scenario of hiding and showing panels. (Personally, I've found the WinForms FlowLayoutPanel a little quirky, but that was in form re-layout and DPI or system metrics change scenarios.)
 
Top Bottom