【问题标题】:MouseEnter and MouseLeave events from a Panel and its child controls来自 Panel 及其子控件的 MouseEnter 和 MouseLeave 事件
【发布时间】:2010-04-04 21:53:31
【问题描述】:

我有一个包含子控件的Panel

如果我处理PanelMouseEnterMouseLeave 事件及其子事件的MouseEnterMouseLeave 事件,则按以下顺序引发事件:

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

但我需要以下顺序:

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

这可能吗?

【问题讨论】:

    标签: c# .net winforms mouseevent


    【解决方案1】:

    Jimmy 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
        }
    

    【讨论】:

      【解决方案2】:

      如果您不介意创建用户控件(从面板或其他您希望的父容器派生), 覆盖你父母的OnMouseLeave 方法,如下所示..

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

      然后,事件引发将按照所需的顺序。

      【讨论】:

      • 感谢和 +1,让我了解了我的想法:检测子 MenuDropdown 中的鼠标位置是否
      • 好主意。但是如果包含的控件在边界上,它将不起作用。 (将below 归功于注意到它的用户。)
      • 不需要创建用户控件。只需在父子控件的 MouseLeave 事件中使用此代码即可完美运行。
      【解决方案3】:

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

      private int mouseEnterCount = 0;
      

      在 MouseEnter 处理程序中执行以下操作:

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

      在 MouseLeave 处理程序中执行以下操作:

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

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

      【讨论】:

        【解决方案4】:

        马修的回答并不总是有效。特别是如果子控件设置在其容器的边缘并且鼠标在该方向上移离控件。你永远不会检测到 MouseLeave 事件。

        最好的方法是创建一个用户控件容器,然后挂钩所有子控件的 MouseEnter 和 MouseLeave 事件,这样您就可以始终正确检测鼠标的时间和位置。那么,如果它进入您的容器边界,您可以触发自定义 MouseEnter 事件,并在它离开 MouseLeave 事件时触发。

        【讨论】:

          【解决方案5】:

          鼠标在进入子控件时“离开”面板,这就是它触发事件的原因。

          您可以在面板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
          }
          

          【讨论】:

          • 谢谢,试过了,但它有错误:存在竞争条件,因为Cursor.Position 是当前位置,而不是鼠标离开控件时的确切位置。
          【解决方案6】:

          这可能不是最优雅的解决方案,但您可以在父控制面板(子类面板)中设置一个布尔值,如“bool selected”。然后当面板的 MouseEnter 触发时将其设置为 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;
            }
          

          两者都不优雅,但它会起作用。

          【讨论】:

            【解决方案7】:

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

            Windows.Forms 事件顺序

            http://missico.spaces.live.com/blog/cns!7178D2C79BA0A7E3!186.entry

            • 由 Urs Eichmann 编写的 EventSpy 创建。 (ftp://missico.net/EventSpy.zip)
            • 使用 .NET Framework 3.5 并启用 Visual Basic 的应用程序框架。

            【讨论】:

              【解决方案8】:

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

              当然,这并不能解决 Hans 提出的潜在解决问题和可能错过的事件。也许更多的上下文是为了。

              【讨论】:

                【解决方案9】:

                检查子组件..

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

                【讨论】:

                  【解决方案10】:

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

                  当光标进入面板时,MouseEnter 触发。我可以访问所有面板子控件并且没有任何反应(这就是我想要的)。当我离开面板时,父表单的MouseEnter 会触发。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-04-03
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多