将鼠标事件传递给父控件

35

环境:.NET Framework 2.0,VS 2008。

我正在尝试创建某些.NET控件(标签,面板)的子类,它将通过某些鼠标事件(MouseDown,MouseMove,MouseUp)传递到其父控件(或者传递到顶层窗体)。我可以通过在标准控件实例中创建这些事件的处理程序来实现这一点,例如:

<code><code><code>public class TheForm : Form
{
    private Label theLabel;

    private void InitializeComponent()
    {
        theLabel = new Label();
        theLabel.MouseDown += new MouseEventHandler(theLabel_MouseDown);
    }

    private void theLabel_MouseDown(object sender, MouseEventArgs e)
    {
        int xTrans = e.X + this.Location.X;
        int yTrans = e.Y + this.Location.Y;
        MouseEventArgs eTrans = new MouseEventArgs(e.Button, e.Clicks, xTrans, yTrans, e.Delta);
        this.OnMouseDown(eTrans);
    }
}
</code></code></code>

因为父控件中触发事件的方法是受保护的,我无法将事件处理程序移动到控件的子类中,且没有用于父控件的限定符:

无法通过类型限定符 System.Windows.Forms.Control 访问受保护的成员 System.Windows.Forms.Control.OnMouseDown(System.Windows.Forms.MouseEventArgs); 限定符必须是类型 TheProject.NoCaptureLabel(或派生自该类型)。

我正在研究在子类中重写控件的WndProc方法,但希望有人能给我提供更简洁的解决方案。


这是针对这段代码的正确错误信息吗?一个是MouseUp,另一个是MouseDown。 - Instance Hunter
我对“pass through”这个词的含义也有些不清楚。 - Instance Hunter
错误信息不正确,应该是MouseDown。我所说的“穿透”是指当某些事件在控件上引发时,我会在控件的父级上以编程方式引发它们。 - GentlemanCoder
3个回答

71

是的,经过大量搜索,我找到了这篇文章:"浮动控件,提示框样式",它使用WndProc将消息从WM_NCHITTEST更改为HTTRANSPARENT,使得Control对鼠标事件透明。

为了实现这个功能,创建一个继承自Label的控件,并简单地添加以下代码即可。

protected override void WndProc(ref Message m)
{
    const int WM_NCHITTEST = 0x0084;
    const int HTTRANSPARENT = (-1);

    if (m.Msg == WM_NCHITTEST)
    {
        m.Result = (IntPtr)HTTRANSPARENT;
    }
    else
    {
        base.WndProc(ref m);
    }
}

我已在使用.NET Framework 4客户端框架的Visual Studio 2010中进行了测试。


谢谢 akatran-- 我已经继续前进了,甚至都不记得我当时是怎么解决这个问题的,但看起来你解决了它。 - GentlemanCoder
非常感谢,这也帮助了我 - 清晰、简洁并解决了问题! - Eric
1
值得注意的是,这将从事件循环中删除虚假的透明控件 - 他们没有机会处理MouseMove事件,因为它直接转到父处理程序。为了保留每个控件本身在将事件传递给父级之前处理该事件的能力,您可以执行以下操作:https://dev59.com/mG7Xa4cB1Zd3GeqPolMw#14814756 - J...
1
顺便提一句,将条件更改为(m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEHOVER)可以使鼠标悬停行为在我的情况下也传递到控件下面。也许这对其他人有所帮助。 - jpierson

6
WS_EX_TRANSPARENT 扩展窗口样式确实可以实现这一点(这是原地工具提示使用的方式)。您可以考虑应用此样式,而不是编写大量处理程序来代替您完成这项工作。
为此,请重写CreateParams方法:
protected override CreateParams CreateParams
{
  get
  {
    CreateParams cp=base.CreateParams;
    cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT
    return cp;
  }
}

进一步阅读:

(本文为IT技术相关内容,以上链接提供更多信息)

1
谢谢您的建议。我尝试了一下,但在我的情况下并没有起作用。也许它只适用于顶级窗口? - GentlemanCoder

2
你需要在你的基类中编写一个公共/受保护的方法来触发事件,然后从派生类中调用这个方法。
或者,这是你想要的吗?
public class MyLabel : Label
{
    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        //Do derived class stuff here
    }
}

我不这么认为。OnMouseDown 引发 事件,而不是处理它。我需要一个事件处理程序将事件传递给其父级。我不能执行您的第一个建议,因为基类是标准的Windows控件,而不是我编写的类。 - GentlemanCoder
@SandeepDatta,父控件和基类是不同的,对吗? - Kira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接