MouseEnter and MouseLeave events from a Panel and its child controls
I have a Panel
that contains child controls.
If I handle the Panel
's MouseEnter
and MouseLeave
events, and its child's MouseEnter
and MouseLeave
events, the events are raised in this order:
Panel.MouseEnter
Panel.MouseLeave
Child1.MouseEnte开发者_JS百科r
Child1.MouseLeave
Panel.MouseEnter
Panel.MouseLeave
But I need the following order:
Panel.MouseEnter
Child1.MouseEnter
Child1.MouseLeave
Panel.MouseLeave
Is that possible?
If you dont mind creating a usercontrol(derived from the panel or other parent container you wish),
Override your parent's OnMouseLeave
method to look like the following..
protected override void OnMouseLeave(EventArgs e)
{
if(this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition)))
return;
else
{
base.OnMouseLeave(e);
}
}
Then, the event raising will be in the required order.
The mouse is "leaving" the panel as it enters the child control which is why it fires the event.
You could add something along the following lines in the panel MouseLeave
event handler:
// Check if really leaving the panel
if (Cursor.Position.X < Location.X ||
Cursor.Position.Y < Location.Y ||
Cursor.Position.X > Location.X + Width - 1 ||
Cursor.Position.Y > Location.Y + Height - 1)
{
// Do the panel mouse leave code
}
The solution is to track the number of enters/leaves. In you overall control add a counter:
private int mouseEnterCount = 0;
In the MouseEnter handler do this:
if (++mouseEnterCount == 1)
{
// do whatever needs to be done when it first enters the control.
}
In the MouseLeave handler do this:
if (--mouseEnterCount == 0)
{
// do whatever needs to be done when it finally leaves the control.
}
and do the above MouseEnter and MouseLeave event handlers for ALL the child controls as well as the containing object.
Matthew's answer will not work always. Especially if the child control is set to the edge of its container and the mouse moves off the controls in that direction. You will never detect the MouseLeave event.
The best approach is to create a user control container then hook all the child controls' MouseEnter and MouseLeave events so that you can properly detect when and where the mouse is at all times. THEN if it enters your container's bounds you can fire a custom MouseEnter event and when it leaves MouseLeave event.
Jimmy T. is right. There will be problems if there is no (or small) space betwean Parent Control (Panel) edge and Child Control.
This is how I solve this problem in UserControl-derived class:
public CSStackPanelItem()
{
InitializeComponent();
MouseEnter += new EventHandler(CSStackPanelItem_MouseEnter);
foreach (Control child in Controls)
{
child.MouseEnter += (s, e) => CSStackPanelItem_MouseEnter(s, e);
child.MouseLeave += (s, e) => OnMouseLeave(e);
}
}
protected override void OnMouseLeave(EventArgs e)
{
if (this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition)))
return; //suppress mouse leave event handling
if (m_bIsHover)
{
m_bIsHover = false;
Invalidate(); //actually my mouse Enter/Leave event
}
base.OnMouseLeave(e);
}
void CSStackPanelItem_MouseEnter(object sender, EventArgs e)
{
m_bIsHover = true;
Invalidate(); //actually my mouse Enter/Leave event
}
This may not be the most elegant solution, but you could set a property in the parent control panel (subclass panel) that is a bool value like "bool selected". Then when the MouseEnter for the panel fires set it to true...then stop the mouseleave logic from firing unless it is set to false.
example
bool selected;
MouseEnter(..,..)
{
if (!selected)
selected = true;
else
selected = false;
if (selected)
/.. Logic Here ../
}
MouseLeave()
{
if (selected)
return;
/.. Logic Here ../
}
In reality I would just have the MouseLeave event of the child set the parameter.
Example:
Parent:
bool doLeave;
MouseLeave(..,..)
{
if (doLeave)
{
/.. Logic ../
doLeave = false;
}
Child:
MouseLeave(..., ...)
{
DerivedPanel parent = this.Parent as DerivedPanel;
if (parent != null)
parent.doLeave = true;
}
Neither are elegant but it will work.
I believe so. A nice tool to have for verifying your WinForms application's events.
Windows.Forms Order of Events
http://missico.spaces.live.com/blog/cns!7178D2C79BA0A7E3!186.entry
- Created with EventSpy written by Urs Eichmann. (ftp://missico.net/EventSpy.zip)
- Using .NET Framework 3.5 and with Visual Basic’s Application Framework enabled.
This is a tricky one, and will be difficult to code reliably for. One idea is to "capture" the 3 incoming events in a list and execute your desired code once the list is complete (has the 3 desired events in the list). Then when you're done executing whatever code (or perhaps capture the combo of events in reverse), you could empty your list and have it ready for the next time that particular combo-event happens. Not ideal, but just a thought.
Of course, that doesn't overcome the potential resolution issues & possible missed events Hans raised. Perhaps more context is in order.
Check the child component..
if (panel1.GetChildAtPoint(panel1.PointToClient(Cursor.Position)) == null)
{
// mouse leave panel and children
}
My solution was to create a MouseEnter
event for the panel and for the parent form of that panel. I don't tie to any MouseLeaving
events.
When the cursor enters the panel, MouseEnter
fires. I can visit all of the panel child controls and nothing happens (which is what I want). When I leave the panel, the parent form's MouseEnter
fires.
I was looking for a solution to this problem so I could make a panel act like a button (ie change color when the user hovers over it). I found the simplest solution was to put another panel over the base panel (and over all it's controls) and make the new panel's BackColr Transparent (highlight the whole BackColr property and type in "Transparent"). Then add the MouseEnter and MouseLeave events to the new panel and make them perform whatever you want (change the background color of the base panel). The user still sees all the controls that are on the base panel but the base panel's mouse leave and mouse enter don't fire as the user moves the mouse over the controls. The user can't interact with the controls on the base panel but for a simple button type control this works OK. Just add a the mouse click event handler to the new panel.
精彩评论