我有一个需要嵌入到我们的 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
)pointX
和 pointY
是鼠标坐标,相对于顶级应用程序窗口的客户区wheel
是鼠标滚轮增量请注意,只需要引发“预览”事件。引发此事件后,可以引发标准鼠标事件,并且 WPF 将在
e.GetPosition()
时返回正确的鼠标位置坐标。叫做。
关于.net - 如何将 Win32 鼠标消息转换为 WPF 鼠标事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21248643/