如何将Win32鼠标消息转换为WPF鼠标事件? [英] How can I convert Win32 mouse messages to WPF mouse events?

查看:36
本文介绍了如何将Win32鼠标消息转换为WPF鼠标事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Win32 (OpenGL) control that I need to embed in our WPF application. It must respond to, and propogate, mouse and keyboard events.

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

I've created a HwndHost derived instance to host the native window and have overridden the WndProc function in this class. To propogate win32 messages to WPF I handle specific mouse messages and map them to WPF events, then use the static InputManager class to raise them.

问题是,当我去处理它们时,鼠标坐标被弄乱了.

这里有一个我用来引发事件的代码示例:

Heres a sample of the code I'm using to raise the events:

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)),我收到了错误的值(它们始终是相同的值)不管原始事件发生在我的控制范围之内.

When my WPF event handlers are triggered and I try to retrieve the mouse coordinates (e.g. e.GetPosition((IInputElement) sender)) I recieve the wrong values (and they are always the same values regardless of where the original event occurred within my control).

我得到的值似乎与承载应用程序的WPF窗口的屏幕位置有关,因为它们在窗口位置更改时会发生变化,但它们也不与应用程序窗口的实际位置相对应.

The values I get seem to be related to the screen location of the WPF window that hosts the application because they change when the window position changes, but they don't correspond to the actual location of the application window either.

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

I think this may have something to do with the internal state of the WPF InputManager, and the fact that the private field MouseDevice._inputSource is null when my events are raised, but my experiments with .net reflection haven't yielded any results there.

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

I don't really know what else to try. Keyboard support worked out of the box, it's just the mouse location support that I can't get working correctly.

推荐答案

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

After another day spent in Reflector analysing the .net sourcecode in question I have found the solution. One must first prime the WPF InputManager by raising a PreviewInputReportEventArgs routed event.

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

Unfortunately, this event and the event argument structures required to raise it are all marked internal, leaving reflection the only way to raise this event. For anyone with the same problem, here's the C# code required to do so:

    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 )
  • pointX pointY 是相对于顶级应用程序窗口的客户区域的鼠标坐标
  • wheel 是鼠标滚轮增量
  • timestamp is the system tick count when the mouse event occurred (usually Environment.TickCount)
  • pointX and pointY are the mouse coordinates, relative to the top level application window's client area
  • wheel is the mouse wheel delta

请注意,仅需要引发预览"事件.引发此事件后,可以引发标准鼠标事件,并且在调用 e.GetPosition()时WPF将返回正确的鼠标位置坐标.

Note that it is only necessary to raise the "Preview" event. After raising this event the standard mouse events can be raised and WPF will return the correct mouse location coordinates when e.GetPosition() is called.

这篇关于如何将Win32鼠标消息转换为WPF鼠标事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆