.net - 如何将 Win32 鼠标消息转换为 WPF 鼠标事件?

标签 .net wpf winapi hwndhost

我有一个需要嵌入到我们的 WPF 应用程序中的 Win32 (OpenGL) 控件。它必须响应并传播鼠标和键盘事件。

我创建了一个 HwndHost 派生实例来托管 native 窗口,并覆盖了此类中的 WndProc 函数。为了向 WPF 传播 win32 消息,我处理特定的鼠标消息并将它们映射到 WPF 事件,然后使用静态 InputManager 类来引发它们。

问题是,当我去处理它们时,鼠标坐标搞砸了。

下面是我用来引发事件的代码示例:

IntPtr MyHwndHost::WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, bool% handled)
{
  switch (msg)
  {
    case WM_LBUTTONDOWN:
    {
        MouseButtonEventArgs^ eventArgs = gcnew MouseButtonEventArgs(
          Mouse::PrimaryDevice,
          Environment::TickCount,
          MouseButton::Left);

        eventArgs->RoutedEvent = UIElement::PreviewMouseDownEvent;
        eventArgs->Source = this;

        // Raise the WPF event from the specified WPF event source.
        InputManager::Current->ProcessInput(eventArgs);

        eventArgs->RoutedEvent = UIElement::MouseDownEvent;
        InputManager::Current->ProcessInput(eventArgs);

        CommandManager::InvalidateRequerySuggested();

        handled = true;
        return IntPtr::Zero;
    }
    break;
  }

  return HwndHost::WndProc(hwnd, msg, wParam, lParam, handled);
}

当我的 WPF 事件处理程序被触发并且我尝试检索鼠标坐标(例如 e.GetPosition((IInputElement) sender) )时,我收到了错误的值(并且无论原始事件在我的控制范围内发生的位置,它们始终是相同的值)。

我得到的值似乎与承载应用程序的 WPF 窗口的屏幕位置有关,因为它们会随着窗口位置的变化而变化,但它们也不对应于应用程序窗口的实际位置。

我认为这可能与 WPF InputManager 的内部状态以及私有(private)字段 MouseDevice._inputSource 的事实有关。是 null当我的事件被提出时,但我对 .net 反射的实验在那里没有产生任何结果。

我真的不知道还能尝试什么。键盘支持开箱即用,只是鼠标位置支持我无法正常工作。

最佳答案

在 Reflector 中花了一天时间分析有问题的 .net 源代码后,我找到了解决方案。必须首先通过引发 PreviewInputReportEventArgs 来启动 WPF InputManager。路由事件。

不幸的是,这个事件和引发它所需的事件参数结构都被标记为 internal ,让反射成为引发此事件的唯一方法。对于有同样问题的任何人,这是执行此操作所需的 C# 代码:

    void RaiseMouseInputReportEvent(Visual eventSource, int timestamp, int pointX, int pointY, int wheel)
    {
        Assembly targetAssembly = Assembly.GetAssembly(typeof(InputEventArgs));
        Type mouseInputReportType = targetAssembly.GetType("System.Windows.Input.RawMouseInputReport");

        Object mouseInputReport = mouseInputReportType.GetConstructors()[0].Invoke(new Object[] {
            InputMode.Foreground,
            timestamp,
            PresentationSource.FromVisual(eventSource),
            RawMouseActions.AbsoluteMove | RawMouseActions.Activate,
            pointX,
            pointY,
            wheel,
            IntPtr.Zero });

        mouseInputReportType
            .GetField("_isSynchronize", BindingFlags.NonPublic | BindingFlags.Instance)
            .SetValue(mouseInputReport, true);

        InputEventArgs inputReportEventArgs = (InputEventArgs) targetAssembly
            .GetType("System.Windows.Input.InputReportEventArgs")
            .GetConstructors()[0]
            .Invoke(new Object[] {
                Mouse.PrimaryDevice,
                mouseInputReport });

        inputReportEventArgs.RoutedEvent = (RoutedEvent) typeof(InputManager)
            .GetField("PreviewInputReportEvent", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)
            .GetValue(null);

        InputManager.Current.ProcessInput((InputEventArgs) inputReportEventArgs);
    }

在哪里:
  • timestamp是鼠标事件发生时的系统滴答计数(通常为 Environment.TickCount )
  • pointXpointY是鼠标坐标,相对于顶级应用程序窗口的客户区
  • wheel是鼠标滚轮增量

  • 请注意,只需要引发“预览”事件。引发此事件后,可以引发标准鼠标事件,并且 WPF 将在 e.GetPosition() 时返回正确的鼠标位置坐标。叫做。

    关于.net - 如何将 Win32 鼠标消息转换为 WPF 鼠标事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21248643/

    相关文章:

    wpf - 有没有一种简单的方法来指定路径为 "level"向上的 WPF 数据绑定(bind)?

    c# - 如何计算 WPF 数据网格中存在的项目并将其显示在另一个页面中?

    wpf - 将图像停靠在 StackPanel WPF 右侧

    java.lang.UnsatisfiedLinkError : Error looking up function 'GetModuleFileNameEx' 错误

    c++ - 当样式为 WS_CHILD 时,通过 ps.rcPaint 查找脏区域不起作用

    c# - Google 通讯录数据 API 401 错误

    javascript - 在同一页面上出现两次的用户控件需要 JavaScript 的唯一 div id

    .net - 为什么 .Net 没有 Set 数据结构?

    c++ - 将结构写入和读取文件

    c# - XML 错误 : data at root level is invalid