我们可以在Windows中更改事件路由策略吗?

时间:2010-12-30 08:57:33

标签: .net winforms window

在.NET窗口窗体中,窗体具有控件集合,并且在内部所有这些窗口都是由Microsoft窗口提供的窗口子系统。有点类似于MFC,它是窗口api周围的浅包装。表单/窗口中的控件创建树结构,并且叶节点接收事件,例如, MouseMove事件将直接通过鼠标下方的窗口/控件接收。

但是在演示框架中,microsoft提供了RoutedEvent,它可以具有以下策略之一

  1. 隧道路由事件使用隧道策略,事件实例在树中向下路由,从根到源元素。
  2. 冒泡路由事件使用冒泡策略,其中事件实例通过树向上路由,从事件源到根。
  3. 直接路由事件不会通过元素树进行路由。
  4. 我的猜测是,演示框架只创建一个主窗口,并为子元素自行绘制以支持事件路由策略

    现在我可以在普通窗口窗体中更改此策略。我想隧道冒泡,目前窗口系统使用直接。我希望它接收MouseEnter / MouseLeave事件,即使它上面有控件。一种方法是全球鼠标/键盘钩。但是,我想避免这种情况。

1 个答案:

答案 0 :(得分:7)

Windows冒泡但它纯粹基于消息。例如MouseWheel气泡,但MouseEnter和Leave都没有。添加冒泡行为在技术上是可行的,但很难做到正确。父/子树中的每个窗口都需要明确地合作并向其父级冒泡。

无论如何,它并没有解决这个特定情况下的问题。关键问题是,不能保证子控件的MouseLeave将确保父项的MouseEnter。鼠标移动消息不够准确,它们不保证报告每个遍历的像素。当您的子窗口靠近其父级边缘时,当您快速移动鼠标时,很容易无法获得父级的MouseEnter。

这是通过鼠标捕获Control.Capture属性解决的。这确保即使鼠标不再位于窗口客户端矩形内,也会生成鼠标移动消息。但是,在监视单个窗口的鼠标而不是多个窗口时,这很有效。单击控件后,控件本身通常会捕获鼠标,取消您启动的鼠标。例如,按钮就是这样做的,它希望看到MouseUp事件,以便它可以重绘按钮以显示按钮状态。

坚韧的饼干。我知道这个特殊问题的唯一正确方法是使用Timer。 200毫秒通常足够好,当鼠标进入时启动它。在Tick事件处理程序中,使用Mouse.Position和Control.PointToClient检查鼠标是否仍位于外部窗口矩形内。