【问题标题】:WPF - simulate mouse events on off-screen rendered GridWPF - 在屏幕外渲染的网格上模拟鼠标事件
【发布时间】:2009-12-17 15:19:55
【问题描述】:

我正在将具有多个元素(按钮、文本框等)的 WPF 网格渲染到位图,然后将其用作 Direct3D 场景中 3D 表面的纹理。对于用户交互,我创建了一条从 2D 鼠标光标位置到 3D 场景的 3D 射线,找到与 gui 表面的交点。所以我知道用户在 WPF 网格上单击的位置,但从那里我被卡住了:

如何在 WPF 元素上模拟鼠标事件,而它们实际上并未显示在打开的窗口中而是呈现在屏幕外?

最近,我正在研究 UIAutomation 和 RaiseEvent,但它们用于将事件发送到单个元素,而不是整个可视化树。手动遍历树并在光标位置查找元素将是一种选择,但我还没有找到一种方法来准确地做到这一点。 VisualTreeHelper.HitTest 是一个好的开始,但不是找到 TextBox,而是找到了 TextBoxView,而不是 ListBox,它找到了 Border。

编辑:在 HitTest 的结果回调中返回 HitTestResultBehavior.Continue 让我可以遍历给定点的所有元素。我现在可以将鼠标事件发送到所有这些元素,但 MouseEventArgs 对象的值是真实鼠标的值。所以我必须创建一个自定义的 MouseDevice,这显然是不可能的。

PointHitTestParameters p = new PointHitTestParameters(new Point(
    ((Vector2)hit).X * sourceElement.ActualWidth, 
    (1 - ((Vector2)hit).Y) * sourceElement.ActualHeight));
VisualTreeHelper.HitTest(sourceElement,
     new HitTestFilterCallback(delegate(DependencyObject o)
     {
         return HitTestFilterBehavior.Continue;
     }),
     new HitTestResultCallback(delegate(HitTestResult r)
     {
         UIElement el = r.VisualHit as UIElement;
         if (el != null)
         {
             MouseButtonEventArgs e = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left);
             if (leftMouseDown) e.RoutedEvent = Mouse.MouseDownEvent;
             else e.RoutedEvent = Mouse.MouseUpEvent;
             el.RaiseEvent(e);
         }
         return HitTestResultBehavior.Continue;
     }), p);

【问题讨论】:

    标签: wpf events input simulation off-screen


    【解决方案1】:

    您可以通过 PostMessage(..) win32 方法将窗口消息(如 WM_MOUSEMOVE)发送到 WPF 窗口的 HWND。我相信这些消息会被 WPF 读取并像来自用户一样执行。

    【讨论】:

    • 这个方法的第一个问题是元素不应该在窗口上。但即使我创建了一个并将元素放在上面,消息也不会由 WPF 处理。我在窗口、UserControl、网格上使用了事件处理程序,最后在按钮和矩形等元素上使用了事件处理程序。但是,封装 HwndSource 的 WPF-Window 正在接收窗口消息。当我使用 HwndSource 类添加自己的消息挂钩时,我可以看到这一点。
    • 看 HwndSource 反汇编我认为你是对的。消息应该像来自用户一样被执行。但显然他们不是,我不知道为什么。
    • 经过数小时的反汇编和逐步调试,我在 HwndMouseInputProvider.ReportInput 中找到了原因:WPF 窗口未处于活动状态且 GetCapture() API 函数未返回窗口句柄。因此,非活动窗口确实会接收消息,但 WPF 互操作处理程序会丢弃它们。我认为很容易修复,但只是在 WPF 窗口上调用 Activate() 或使用 SetCapture() 设置句柄并没有帮助。我会更深入地挖掘......
    • 好吧,我的错。我在发送消息后直接使用 SetCapture() 重置句柄,忘记了窗口消息系统不会立即发送消息。现在它适用于我发送的第一条消息,但由于鼠标捕获句柄已设置为 WPF 窗口,因此无法识别 WM_LMOUSEDOWN 之后的 WM_LMOUSEUP 等交互。
    【解决方案2】:

    如果你真的很勇敢,可以试试my hack to get access to the WPF IDirect3DDevice9Ex。从那里您可以将交换链中的后备缓冲区复制到您的 d3d 场景中。如果您使用的是 9Ex (Vista D3D9),则可以利用共享资源(在设备和进程之间共享表面/纹理/等)功能来帮助提高性能。

    您可能仍然需要对 Windows 消息进行一些技巧以实现交互性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2019-03-02
      • 1970-01-01
      • 1970-01-01
      • 2011-04-19
      • 2015-04-08
      • 1970-01-01
      相关资源
      最近更新 更多