C# Winforms:首先在主窗体上拦截鼠标事件,而不是在控件上

C# Winforms:首先在主窗体上拦截鼠标事件,而不是在控件上,c#,winforms,C#,Winforms,当然有一种方便的方法可以做到这一点: 我已经在我的主窗体上实现了一个鼠标拖动行为的“移动窗口”,我希望鼠标单击/移动事件被窗体拦截,而不是被窗体中的控件拦截 我想为鼠标事件找到一个与“KeyPreview”属性等效的/复制的属性 此外,我希望避免在12个控件的鼠标事件中12次将鼠标事件重定向到主窗体方法(这是迄今为止我发现的最糟糕的解决方法) 有什么想法吗?根据你的评论 在基类中实现鼠标事件的重定向功能,然后使所有控件都派生自该基类 因此,您只需实现一次功能,然后所有控件都将“重新播放”主窗体捕

当然有一种方便的方法可以做到这一点:

我已经在我的主窗体上实现了一个鼠标拖动行为的“移动窗口”
,我希望鼠标单击/移动事件被窗体拦截,而不是被窗体中的控件拦截

我想为鼠标事件找到一个与“KeyPreview”属性等效的/复制的属性

此外,我希望避免在12个控件的鼠标事件中12次将鼠标事件重定向到主窗体方法(这是迄今为止我发现的最糟糕的解决方法)


有什么想法吗?

根据你的评论

在基类中实现鼠标事件的重定向功能,然后使所有控件都派生自该基类

因此,您只需实现一次功能,然后所有控件都将“重新播放”主窗体捕获的鼠标事件


希望这有帮助。

订阅所有控件MouseMove事件(考虑对嵌套控件递归执行此操作)

并在此事件处理程序中引发MouseMove

private void RedirectMouseMove(object sender, MouseEventArgs e)
{
    Control control = (Control)sender;
    Point screenPoint = control.PointToScreen(new Point(e.X, e.Y));
    Point formPoint = PointToClient(screenPoint);
    MouseEventArgs args = new MouseEventArgs(e.Button, e.Clicks, 
        formPoint.X, formPoint.Y, e.Delta);
    OnMouseMove(args);
}
请记住,控件接收带有控件本地坐标的MouseEvents。所以你需要把它转换成形式坐标。 嵌套控件可能有缺点,但我将其留给您(例如,调用Parent.PointToClient)


更新:您仍将能够处理控制事件-只需再订阅一次事件。

您可以使用全局鼠标挂钩拦截鼠标事件,有一个

覆盖控制。预处理消息方法:

编辑:

似乎预处理消息可能不适用于鼠标事件。尝试覆盖WndPrc。它确实可以拦截鼠标消息,但您需要查看它是否按照您想要的顺序拦截鼠标消息:

您可以使用轻松拦截系统范围的鼠标位置

单击鼠标后,您应该检查鼠标位置点是否与窗体相交,或者鼠标下的窗口是否为窗体。

要执行后一项操作,需要WindowFromPoint API函数:

    [DllImport( "user32.dll", SetLastError = true )]
    public static extern IntPtr WindowFromPoint( [In] POINTAPI Point );

    private void _mouseListener_MouseClick( object sender, MouseEventArgs e )
    {
        var localPoint = this.PointToClient( e.Location );
        bool containsPoint = this.ClientRectangle.Contains( localPoint );

        var windowHandle = WindowFromPoint( e.Location );
        var ctl = (Form)Form.FromHandle( windowHandle );
        bool mainFormClicked = ctl != null && ctl.Handle == this.Handle;

        if( containsPoint && mainFormClicked  )
        {
              //form click is intercepted!
        }
    }

实际上,当我想截取表单外部的点击时,我会使用它(没有其他方法)。在您的例子中,为了性能起见,我会绑定到每个控件的鼠标单击(全局钩子很重)。

子控件是否也处理鼠标单击/移动事件?其中一些,而不是全部。我当然希望避免在12个控件中将鼠标事件重定向到主窗体方法12次,您可以使用。(琐碎的回答自动转换成评论。)是的,我确实想到了。。。但这让我对遗留代码的更改超出了我的意愿。我只是想知道是否有一种方法可以完全绕过控件…正如在另一篇评论中所述,我想知道是否有一种方法可以完全绕过控件…但KeyPreview的工作方式几乎相同(事件参数的处理属性除外)-它绕过控件并首先在表单级别处理事件。我在哪里做?在主窗体上?那会绕过控制吗?
    [DllImport( "user32.dll", SetLastError = true )]
    public static extern IntPtr WindowFromPoint( [In] POINTAPI Point );

    private void _mouseListener_MouseClick( object sender, MouseEventArgs e )
    {
        var localPoint = this.PointToClient( e.Location );
        bool containsPoint = this.ClientRectangle.Contains( localPoint );

        var windowHandle = WindowFromPoint( e.Location );
        var ctl = (Form)Form.FromHandle( windowHandle );
        bool mainFormClicked = ctl != null && ctl.Handle == this.Handle;

        if( containsPoint && mainFormClicked  )
        {
              //form click is intercepted!
        }
    }