C sharp鼠标穿透

C# winform窗体中 怎么通过在子控件中将鼠标事件调用父控件的OnMouseMove等方法对父控件的鼠标事件进行重写,实现调用子控件的鼠标移动事件的同时也能响应到父级控件的鼠标事件。例如我现在碰到的问题,在一个panel里面放置了若干个按钮button,当我将鼠标移到按钮上时,触发不了panel的MouseMove事件。还请大家指导一下,感谢!

首先分析一下思路:众所周知,window系统是一个消息驱动的系统,那么想要实现你想要的效果其实就可以通过转发消息实现。具体代码看下面:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Test
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
            PenetrableButton penetrableButton = PenetrableButton.AddPenetrableButton(this, 100, 100, 200, 200, "PenetrableButton");
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            this.Text = e.X + "," + e.Y;
            base.OnMouseMove(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            this.Text = "MouseLeftButtonDown:" + e.X + "," + e.Y;
            base.OnMouseDown(e);
        }
    }

    public class PenetrableButton : Button
    {
        private List<int> penetrableMessageList;
        private PenetrableButton()
        {
            penetrableMessageList = new List<int>();
            penetrableMessageList.Add(Wind32Util.WM_MOUSEMOVE);
            penetrableMessageList.Add(Wind32Util.WM_MOUSELEFTBUTTONDOWN);
        }

        public static PenetrableButton AddPenetrableButton(Form parent, int top, int left, int width, int height, string text)
        {
            PenetrableButton penetrableButton = new PenetrableButton();
            penetrableButton.Top = top;
            penetrableButton.Left = left;
            penetrableButton.Width = width;
            penetrableButton.Height = height;
            penetrableButton.Text = text;
            if (parent != null)
                parent.Controls.Add(penetrableButton);
            return penetrableButton;
        }

        protected override void OnMouseMove(MouseEventArgs mevent)
        {
            this.Text = mevent.X + "," + mevent.Y;
            base.OnMouseMove(mevent);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            this.Text = "MouseLeftButtonDown:" + e.X + "," + e.Y;
            base.OnMouseDown(e);
        }

        protected override void WndProc(ref Message m)
        {
            // 转发消息给父控件
            if (this.Parent.Handle != null && penetrableMessageList.Contains(m.Msg))
            {
                // 根据相对位置修正鼠标坐标
                Wind32Util.SendMessage(this.Parent.Handle, m.Msg, m.WParam, (IntPtr)(m.LParam.ToInt32() + ((this.Top << 16) | this.Left)));
            }
            base.WndProc(ref m);
        }
    }

    public class Wind32Util
    {
        // 鼠标移动消息
        public const int WM_MOUSEMOVE = 0x200;
        public const int WM_MOUSELEFTBUTTONDOWN = 0x201;


        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    }
}


1.自己创建一个派生自Button的类,并且重写WndProc消息处理函数,发现是自己感兴趣的消息就通过SendMessage把这个消息发送给目标。
2.由于存在相对位置的关系,所以不能直接转发消息,需要修正鼠标的坐标,具体鼠标的坐标是通过lParam得到的,公式是:y<<16|x。
3.上面的例子仅仅给你举了转发MouseMove和MouseLeftButtonDown这两个消息的例子,应该可以举一反三,改成转发自己感兴趣的任何消息。
4.例子里面的转发对象也仅仅举了转发给自己的父级控件,也应该举一反三能改成转发给任何自己感兴趣的对象。
5.转发的时候需要注意,Button类和其他类是否都有这个消息的处理case,如果没有,转发了也不会被处理。
最后附上效果图:

img