I want to pick up the mouse Enter and Leave correctly on the form with overlapping C# controls.

Asked 2 years ago, Updated 2 years ago, 112 views

Thank you for your help.

I'm creating a Windows form application in C#.Net 4.5, but I have a mouse in the form (Enter) and
from the form. I'm trying to pick up the mouse that came out (Leave).

Enter a description of the image here

However, if there is another control, such as the red part, as shown in the figure above,
in this red part There is no mouse event on the Form side.

I tried using the red controls such as OnMouseEnter and onMouseLeave, but in that case,
Even if I move from the red part of the image to the gray part, the mouse is separated.
It doesn't work.The opposite is true.

No matter what control is on the form, the form MouseEnter and MouseLeave are
How do I get this to happen?

Thank you for your cooperation.

c# winforms

2022-09-30 16:13

3 Answers

It's not an answer, but I'm a little worried, so I'll say something.
Of course, it can be implemented by hooking or capturing mouse messages, but you shouldn't design it like that.

If possible, explaining "What are hidden parent windows trying to do by detecting mouse intrusion and detachment?" may provide another simple solution.

Now, the reason you shouldn't design or implement it like that is because Windows isn't designed like that.

·Windows is designed to avoid sending mouse messages to invisible windows (the same goes for other common operating systems).

That's why.
Windows sends a mouse message only to the window with the highest overlap order (Z Order) on the screen directly below the mouse cursor.
This suggests the policy of "only visible windows have permission to handle mouse operations."
In other words, don't do anything in the other windows.
You can predict that forcing it will likely cause bugs or annoy other applications.

Therefore, if you need to monitor the state of the mouse in the child window, it is normal to have the child report the state.
"For example, ""Red Control reports to the owner."""The mouse has broken in."

·The following is Snakefoot.

At the beginning of Windows, there was WM_MOUSEMOVE, but there was no WM_MOUSEMOVE, WM_MOUSELEAVE, etc.
It has been available since around NT4.0(or 2000), but due to a later OS change, the application has decided to run TrackMouseEvent() and request a message.
Until then, we generally used a mouse capture to detect intrusion and detachment of the mouse.
Incidentally, messages like WM_MOUSEENTER, which means intrusion, still do not exist in Windows API.
You are making it pseudo based on the internal state of the app.

I have a personal opinion that these conditions are relatively unimportant because they are "mouse messages that may not be user actions."


2022-09-30 16:13

WPF causes MouseEnterMouseLeave events even if the controls overlap as you want.

In the case of WindowsForm, the only way to do this is to link the event to all the controls.
That's the design idea, so I can only say that

You can think of a pattern that links events to all controls as follows:

on_MouseEnter functions can be obtained from MouseEnter information, so you have to set up various codes accordingly.

I often use anything in the Tag of Control, so I put detailed information about the source and take it out in the event.What are you doing?

Please consider it.

publicForm1()
        {
            InitializeComponent();

            addMouseEvent(this);
        }

        private void addMouseEvent (Controlc)
        {
            c. MouseEnter+=on_MouseEnter;
            c. MouseLeave+=on_MouseLeave;
            foreach (Control cc in c. Controls)
            {
                addMouseEvent(cc);
            }
        }

        private void on_MouseEnter(object sender, EventArgse)
        {
            Control c=sender as Control;
            Point cur_pos = System.Windows.Forms.Cursor.Position;
            This.textBox1.AppendText($"on_MouseLeave{c?.Name}{cur_pos}";
        }

        private void on_MouseLeave (object sender, EventArgse)
        {
            Control c=sender as Control;
            Point cur_pos = System.Windows.Forms.Cursor.Position;
            This.textBox1.AppendText($"on_MouseLeave{c?.Name}{cur_pos}";

        }


2022-09-30 16:13

WinForms can also simulate Enter/Leave timing with the following combinations:

Based on the acquired mouse pointer position, a range check is performed by Rectangle.Contains method.
Reference: How do I convert screen coordinates←→client coordinates?
How do I determine if a coordinate or region is contained within the control area?
Use one of the following data:

Remember whether the previous state was inside or outside the window, and if there is a change, it is determined that the timing of Enter/Leave occurs.

Here's the source of the program using LowLevelMouseProc (not including timer examples)

namespace WindowsFormsApp1
{
    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    public partial class Form 1:Form
    {
        public delete int HookProc(intnCode, IntPtrwParam, IntPtrParam);
        HookProc MouseHookLLProcedure;
        static intLLHook = 0;
        static bPrevMouseIn=false;
        public constint HC_ACTION = 0;
        public constint WH_MOSE_LL = 14;
        
        StructureLayout (LayoutKind.Sequential)
        public class point
        {
            public intx;
            public inty;
        }
        StructureLayout (LayoutKind.Sequential)
        public class MouseLLHookStruct
        {
            public POINT pt;
            public int mouseData;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        
        DllImport("kernel32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)
        public static external IntPtr GetModuleHandle(string lpModuleName);
        DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)
        public static external int SetWindowsHookEx(intidHook, HookProclpfn, IntPthInstance, int threadId);
        DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)
        public static external boolean UnhookWindowsHookEx(intidHook);
        DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)
        public static external int CallNextHookEx(intidHook, intnCode, IntPtrwParam, IntPtrParam);
        
        public Form 1()
        {
            InitializeComponent();
            bPrevMouseIn=false;
        }
        private void Form1_Activated(object sender, EventArgse)
        {
            if(hLLHook==0)
            {
                MouseHookLLProcedure=new HookProc (Form1.MouseLLHookProc);
                hLLHook=SetWindowsHookEx(WH_MUSE_LL, MouseHookLLProcedure, GetModuleHandle(null), 0);
            }
        }
        private static int MouseLLHookProc(intnCode, IntPtrwParam, IntPtrParam)
        {
            if(nCode==HC_ACTION)
            {
                MouseLLHookStruct MyMouseHookStruct= (MouseLLHookStruct) Marshall.PtrToStructure (lParam, type of (MouseLLHookStruct));
                Form 1 tempForm = Form.ActiveForm as Form 1;
                if(tempForm!=null)
                {
                    boolefInMouse=false;
                    // for the medial region
                    fInMouse=tempForm.RectangleToScreen(tempForm.ClientRectangle).Contains(MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y);
                    // for the lateral region
                    //fInMouse=tempForm.DesktopBounds.Contains (MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y);
                    if(fInMouse!=bPrevMouseIn)
                    {  // Enter or Leave Occurs
                        tempForm.textBox1.AppendText("X="+MyMouseHookStruct.pt.x.ToString("d4")+ 
                            "  Y="+MyMouseHookStruct.pt.y.ToString("d4")+(fInMouse?"Enter":"Leave")+"\r\n";
                        bPrevMouseIn=fInMouse;//Remember this information
                    }
                }
            }
            return CallNextHookEx (hLLHook, nCode, wParam, lParam);
        }
        private void Form1_FormClosed(object sender, FormClosedEventArgse)
        {
            if(hLLHook!=0)
            {
                booleet = UnhookWindowsHookEx(hLLHook);
                hLLHook = 0;
            }
        }
    }
}

Source the form here

namespace WindowsFormsApp1
{
    partial class Form 1
    {
        private System.ComponentModel.IContainer components=null;
        protected override void disposition (bool disposition)
        {
            if(disposing&&(components!=null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        #region Windows Form Designer generated code
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2=newSystem.Windows.Forms.Button();
            this.button3=newSystem.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            This.SuspendLayout();
            // button1
            this.button1.Location=new System.Drawing.Point(-1,-1);
            This.button1.Name="button1";
            this.button1.Size = new System.Drawing.Size(128,96);
            This.button1.TabIndex=0;
            This.button1.Text="button1";
            This.button1.UseVisualStyleBackColor=true;
            // button2
            This.button2.Location=new System.Drawing.Point(146,-1);
            This.button2.Name="button2";
            this.button2.Size = new System.Drawing.Size(128,96);
            This.button2.TabIndex=1;
            This.button2.Text="button2";
            This.button2.UseVisualStyleBackColor=true;
            // button3
            This.button3.Location=new System.Drawing.Point(294,-1);
            This.button3.Name="button3";
            this.button3.Size = new System.Drawing.Size(128,96);
            This.button3.TabIndex=2;
            This.button3.Text="button3";
            This.button3.UseVisualStyleBackColor=true;
            // textBox1
            this.textBox1.Location=new System.Drawing.Point(0,192);
            This.textBox1.Multiline=true;
            This.textBox1.Name="textBox1";
            this.textBox1.ScrollBars=System.Windows.Forms.ScrollBars.Vertical;
            this.textBox1.Size=new System.Drawing.Size (421,206);
            This.textBox1.TabIndex=3;
            // Form 1
            this.AutoScaleDimensions=new System.Drawing.SizeF (6F, 12F);
            this.AutoScaleMode=System.Windows.Forms.AutoScaleMode.Font;
            This.ClientSize = new System.Drawing.Size (421,397);
            This.Controls.Add (this.textBox1);
            This.Controls.Add (this.button3);
            This.Controls.Add (this.button2);
            This.Controls.Add (this.button1);
            This.Name="Form1";
            This.Text="Form1";
            this.Activated+=new System.EventHandler(this.Form1_Activated);
            This.FormClosed+=new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed);
            This.ResumeLayout(false);
            This.PerformLayout();
        }
        #endregion
        private System.Windows.Forms.Button Button1;
        private System.Windows.Forms.Button Button2;
        private System.Windows.Forms.Button Button3;
        private System.Windows.Forms.TextBox textBox1;
    }
}

View an example here
sample image

Note:

@Uncle-Kei's point is reasonable, but it's another issue, so I looked for an article related to it.

You're going to double-check what Windows might be doing, but by applying WindowFromPoint function, you can determine if your window is at the top of the list?

Gets the handle to the window containing the specified point.

The return value is the handle to the window containing the point.If no window exists at the specified point, the return value is NULL.If the point is on the static text control, the return value is the handle to the window under the static text control.

The WindowFromPoint function does not get a handle to a hidden or disabled window, even if the point is in a window.Applications must use the ChildWindowFromPoint function for unrestricted searches.

The following article deals with the process of obtaining information, and if you can determine that it belongs to your own process, you may be able to do the corresponding process.
I can't use VB/Delphi/C++ as it is, but it will be helpful.
Get information for the specified window
Home>Program>Delphi6 Low-Tech Tips>Get the window under your mouse cursor
Windows API Notes WindowFromPoint
Chapter 102 Change the window title

Other articles like this
How to get the z-order in windows?
Given a hwnd, determine if that window is not hidden by other windows (Z-Ordering)

Either way, it will be heavier than the original process.


2022-09-30 16:13

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.