C# 来自面板及其子控件的MouseEnter和MouseLeave事件

C# 来自面板及其子控件的MouseEnter和MouseLeave事件,c#,.net,winforms,mouseevent,C#,.net,Winforms,Mouseevent,我有一个面板,其中包含子控件 如果我处理面板的MouseEnter和MouseLeave事件,以及其子面板的MouseEnter和MouseLeave事件,则事件按以下顺序引发: Panel.MouseEnter Panel.MouseLeave Child1.MouseEnter Child1.MouseLeave Panel.MouseEnter Panel.MouseLeave 但我需要以下命令: Panel.MouseEnter Child1.MouseEnter Child1.Mou

我有一个
面板
,其中包含子控件

如果我处理
面板的
MouseEnter
MouseLeave
事件,以及其子面板的
MouseEnter
MouseLeave
事件,则事件按以下顺序引发:

Panel.MouseEnter
Panel.MouseLeave
Child1.MouseEnter
Child1.MouseLeave
Panel.MouseEnter
Panel.MouseLeave
但我需要以下命令:

Panel.MouseEnter
Child1.MouseEnter
Child1.MouseLeave
Panel.MouseLeave

有可能吗?

我相信有可能。用于验证WinForms应用程序事件的好工具

窗体事件顺序

  • 由Urs Eichmann编写的EventSpy创建。()
  • 使用.NET Framework 3.5并启用Visual Basic的应用程序框架
鼠标进入子控件时正在“离开”面板,这就是它触发事件的原因

您可以在面板
MouseLeave
事件处理程序中沿以下行添加一些内容:

// 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
}
//检查是否真的离开面板
如果(光标位置X<位置X||
Cursor.Position.YLocation.X+Width-1||
光标.Position.Y>位置.Y+高度-1)
{
//面板鼠标是否留下代码
}

这可能不是最优雅的解决方案,但您可以在父控制面板(子类面板)中设置一个类似“bool selected”的bool值属性。然后,当面板的鼠标指针启动时,将其设置为true…然后停止mouseleave逻辑启动,除非将其设置为false

范例

bool selected;

MouseEnter(..,..)
{
 if (!selected)
   selected = true;
 else
   selected = false;

  if (selected)
   /.. Logic Here ../
}

MouseLeave()
{
  if (selected)
   return;

/.. Logic Here ../
}
实际上,我只是让孩子的MouseLeave事件设置参数

Example:

Parent:
  bool doLeave;

  MouseLeave(..,..)
  {
    if (doLeave)
    {
     /.. Logic ../
     doLeave = false;
  }

Child:
 MouseLeave(..., ...)
  {
    DerivedPanel parent = this.Parent as DerivedPanel;
    if (parent != null)
      parent.doLeave = true;
  }

这两种方法都不优雅,但都能奏效。

这是一个棘手的问题,很难可靠地编写代码。一个想法是“捕获”列表中的3个传入事件,并在列表完成后执行所需代码(列表中有3个所需事件)。然后,当您执行完任何代码(或者可能以相反的方式捕获事件组合)后,您可以清空列表,并为下一次发生特定组合事件做好准备。不理想,只是一个想法


当然,这并不能解决潜在的解决问题&汉斯提出的可能遗漏的事件。可能需要更多的上下文。

如果您不介意创建一个usercontrol(从您希望的面板或其他父容器派生), 重写您父母的
OnMouseLeave
方法,如下所示

protected override void OnMouseLeave(EventArgs e)
{

    if(this.ClientRectangle.Contains(this.PointToClient(Control.MousePosition)))
         return;
    else
    {
        base.OnMouseLeave(e);
    }
}

然后,事件引发将按所需顺序进行。

解决方案是跟踪进入/离开的次数。 在总体控制中添加一个计数器:

private int mouseEnterCount = 0;
在MouseEnter处理程序中,执行以下操作:

if (++mouseEnterCount == 1)
{
   // do whatever needs to be done when it first enters the control.
}
if (--mouseEnterCount == 0)
{
   // do whatever needs to be done when it finally leaves the control.
}
在MouseLeave处理程序中,执行以下操作:

if (++mouseEnterCount == 1)
{
   // do whatever needs to be done when it first enters the control.
}
if (--mouseEnterCount == 0)
{
   // do whatever needs to be done when it finally leaves the control.
}

并对所有子控件以及包含的对象执行上述MouseEnter和MouseLeave事件处理程序。

Matthew的答案并不总是有效。尤其是如果子控件设置为其容器的边缘,并且鼠标沿该方向离开控件时。您将永远无法检测到MouseLeave事件


最好的方法是创建一个用户控件容器,然后钩住所有子控件的MouseEnter和MouseLeave事件,以便您可以随时正确地检测鼠标的位置和时间。然后,如果它进入容器的边界,您可以触发自定义MouseEnter事件,当它离开MouseLeave事件时。

检查子组件

if (panel1.GetChildAtPoint(panel1.PointToClient(Cursor.Position)) == null)
{
    // mouse leave panel and children
}

吉米·T.是对的。如果父控件(面板)边缘和子控件之间没有(或很小)空间,则会出现问题

这就是我在UserControl派生类中解决此问题的方法:

    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
    }

我的解决方案是为面板和该面板的父窗体创建一个
MouseEnter
事件。我不参与任何
鼠标移动事件


当光标进入面板时,
MouseEnter
激发。我可以访问所有面板子控件,但什么也没有发生(这正是我想要的)。当我离开面板时,父窗体的
MouseEnter
将启动。

谢谢,尝试过,但它有错误:存在竞争条件,因为
光标。位置
是当前位置,而且不完全是鼠标离开控件时的位置。谢谢和+1让我了解了我的想法:在child MenuDropdown中检测鼠标位置还是不是好主意。但是,如果包含的控件位于边界上,它将无法工作。(对注意到它的用户表示感谢。)不需要创建用户控件。在父控件和子控件的MouseLeave事件中使用了这段代码,效果非常好。