Parent Control Mouse Enter/Leave Events With Child Controls

前端 未结 5 1075
天涯浪人
天涯浪人 2020-11-27 18:49

I have a C# .NET 2.0 WinForms app. My app has a control that is a container for two child controls: a label, and some kind of edit control. You can think of it like this,

5条回答
  •  一个人的身影
    2020-11-27 19:01

    I had the exact same need. Paul Williams' answer provided me with the core idea, but I had difficulty understanding the code. I found another take here, and together, the two examples helped me develop my own version.

    To initialize, you pass the container control of interest into the ContainerMessageFilter constructor. The class collects the window handles of the container and all child controls within it.

    Then, during operation, the class filters the WM_MOUSEMOVE message, checking the messages's HWnd to determine what control the mouse is moving within. In this way, it determines when the mouse has moved within or outside the set of controls within the container that it is watching.

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Forms;
    
    public class ContainerMessageFilter : IMessageFilter {
        private const int WM_MOUSEMOVE = 0x0200;
    
        public event EventHandler MouseEnter;
        public event EventHandler MouseLeave;
    
        private bool insideContainer;
        private readonly IEnumerable handles;
    
        public ContainerMessageFilter( Control container ) {
            handles = CollectContainerHandles( container );
        }
    
        private static IEnumerable CollectContainerHandles( Control container ) {
            var handles = new List { container.Handle };
    
            RecurseControls( container.Controls, handles );
    
            return handles;
        }
    
        private static void RecurseControls( IEnumerable controls, List handles ) {
            foreach ( Control control in controls ) {
                handles.Add( control.Handle );
    
                RecurseControls( control.Controls, handles );
            }
        }
    
        public bool PreFilterMessage( ref Message m ) {
            if ( m.Msg == WM_MOUSEMOVE ) {
                if ( handles.Contains( m.HWnd ) ) {
                    // Mouse is inside container
                    if ( !insideContainer ) {
                        // was out, now in
                        insideContainer = true;
                        OnMouseEnter( EventArgs.Empty );
                    }
                }
                else {
                    // Mouse is outside container
                    if ( insideContainer ) {
                        // was in, now out
                        insideContainer = false;
                        OnMouseLeave( EventArgs.Empty );
                    }
                }
            }
    
            return false;
        }
    
        protected virtual void OnMouseEnter( EventArgs e ) {
            var handler = MouseEnter;
            handler?.Invoke( this, e );
        }
    
        protected virtual void OnMouseLeave( EventArgs e ) {
            var handler = MouseLeave;
            handler?.Invoke( this, e );
        }
    }
    

    In the following usage example, we want to monitor mouse entry and exit for a Panel and the child controls that it contains:

    public partial class Form1 : Form {
        private readonly ContainerMessageFilter containerMessageFilter;
    
        public Form1() {
            InitializeComponent();
    
            containerMessageFilter = new ContainerMessageFilter( panel1 );
            containerMessageFilter.MouseEnter += ContainerMessageFilter_MouseEnter;
            containerMessageFilter.MouseLeave += ContainerMessageFilter_MouseLeave;
            Application.AddMessageFilter( containerMessageFilter );
        }
    
        private static void ContainerMessageFilter_MouseLeave( object sender, EventArgs e ) {
            Console.WriteLine( "Leave" );
        }
    
        private static void ContainerMessageFilter_MouseEnter( object sender, EventArgs e ) {
            Console.WriteLine( "Enter" );
        }
    
        private void Form1_FormClosed( object sender, FormClosedEventArgs e ) {
            Application.RemoveMessageFilter( containerMessageFilter );
        }
    }
    

提交回复
热议问题