【问题标题】:Winforms : Intercepting Mouse Event on Main Form first, not on ControlsWinforms:首先在主窗体上拦截鼠标事件,而不是在控件上
【发布时间】:2011-06-26 20:05:32
【问题描述】:

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

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

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

除了我想避免在 12 个控件的鼠标事件中单独将鼠标事件重定向到主窗体方法 12 次(这是我迄今为止发现的丑陋的解决方法)

有什么想法吗?

【问题讨论】:

  • 子控件是否也处理鼠标点击/移动事件?
  • 其中一些,不是全部。我当然想避免在 12 个控件中将鼠标事件重定向到主窗体方法 12 次​​span>
  • 您可以使用message filter。 (琐碎的答案自动转换为评论。)

标签: c# winforms


【解决方案1】:

根据您的 cmets,

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

因此,您只需实现该功能一次,然后您的所有控件将“重新抛出”鼠标事件以被主窗体捕获。

希望这会有所帮助。

【讨论】:

  • 是的,我确实想到了……但这让我更改遗留代码的次数超出了我的意愿。只是想知道是否没有办法完全绕过控制...
【解决方案2】:

订阅所有控件的 MouseMove 事件(考虑对嵌套控件进行递归)

foreach (Control control in Controls)
    control.MouseMove += RedirectMouseMove;

并在此事件处理程序中引发 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);
}

请记住,控件接收带有控件本地坐标的 MouseEvent。所以你需要把它转换成表格坐标。 嵌套控件可能有缺点,但我把它留给你(例如调用 Parent.PointToClient)

更新:您仍然可以处理控制事件 - 只需再订阅一次事件。

【讨论】:

  • 正如另一条评论中所述,我想知道是否没有办法完全绕过控制...
  • 但是 KeyPreview 的工作方式几乎相同(期望事件参数的 Handled 属性) - 它绕过控件并首先在表单级别处理事件。
【解决方案3】:

重写 Control.PreProcessMessage 方法:

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.preprocessmessage.aspx

编辑:

似乎 PreProcessMessage 可能不适用于鼠标事件。尝试改写 WndPrc。它肯定可以拦截鼠标消息,但是你需要看看它是否按照你想要的顺序拦截它们:

http://bytes.com/topic/c-sharp/answers/752144-preprocessmessage

【讨论】:

  • 我在哪里做呢?在主窗体上?那会绕过控制吗?
【解决方案4】:

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

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

要完成后一件事,您需要 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!
        }
    }

实际上,当我想拦截点击在我的表单之外时,我会使用它(没有其他方法)。在您的情况下,出于性能考虑,我会绑定到每个控件的 MouseClick(全局挂钩很重)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-25
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多