Pertanyaan Bagaimana saya dapat mengkonversi pesan mouse Win32 ke acara mouse WPF?


Saya memiliki kontrol Win32 (OpenGL) yang harus saya embed dalam aplikasi WPF kami. Ini harus menanggapi, dan mempropagandakan, acara mouse dan keyboard.

Saya telah membuat instance turunan HwndHost untuk menghosting jendela asli dan telah menggantikan fungsi WndProc di kelas ini. Untuk mempropagandakan pesan win32 ke WPF, saya menangani pesan mouse tertentu dan memetakannya ke acara WPF, kemudian gunakan kelas InputManager statis untuk membesarkannya.

Masalahnya adalah, ketika saya pergi untuk menangani mereka, koordinat mouse menjadi kacau.

Berikut ini contoh kode yang saya gunakan untuk meningkatkan acara:

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);
}

Ketika penangan acara WPF saya dipicu dan saya mencoba mengambil koordinat mouse (mis. e.GetPosition((IInputElement) sender)) Saya menerima nilai yang salah (dan mereka selalu memiliki nilai yang sama di mana pun kejadian asli terjadi dalam kendali saya).

Nilai-nilai yang saya dapatkan tampaknya terkait dengan lokasi layar dari jendela WPF yang menghosting aplikasi karena mereka berubah ketika posisi jendela berubah, tetapi mereka tidak sesuai dengan lokasi sebenarnya dari jendela aplikasi baik.

saya pikir ini mungkin ada hubungannya dengan status internal dari WPF InputManager, dan fakta bahwa bidang swasta MouseDevice._inputSource aku s null ketika acara saya dibangkitkan, tetapi percobaan saya dengan refleksi .net belum membuahkan hasil apa pun di sana.

Saya benar-benar tidak tahu apa lagi yang bisa dicoba. Dukungan keyboard bekerja di luar kotak, itu hanya dukungan lokasi mouse yang saya tidak bisa bekerja dengan benar.


11
2018-01-21 03:35


asal


Jawaban:


Setelah satu hari lagi dihabiskan di Reflector menganalisis .net sourcecode yang dipertanyakan saya telah menemukan solusinya. Satu harus terlebih dahulu perdana WPF InputManager dengan menaikkan PreviewInputReportEventArgs acara yang dirutekan.

Sayangnya, peristiwa ini dan struktur argumen acara yang diperlukan untuk menaikkannya semuanya ditandai internal, meninggalkan refleksi satu-satunya cara untuk meningkatkan acara ini. Bagi siapa saja dengan masalah yang sama, inilah kode C # yang diperlukan untuk melakukannya:

    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);
    }

dimana:

  • timestamp adalah hitungan jumlah sistem ketika kejadian mouse terjadi (biasanya Environment.TickCount)
  • pointX dan pointY adalah koordinat mouse, relatif ke area klien jendela aplikasi tingkat atas
  • wheel adalah delta roda mouse

Perhatikan bahwa ini hanya perlu untuk menaikkan "Pratinjau" acara. Setelah menaikkan event ini, event mouse standar dapat dinaikkan dan WPF akan mengembalikan koordinat lokasi mouse yang benar saat e.GetPosition() disebut.


5
2018-01-22 07:01



Cara saya melakukannya adalah dengan pendekatan yang berbeda. Saya menjaga transparan WPF Window ter-overlay pada HwndHost saya menggunakan AllowsTransparency milik. Saya menghubungkan acara menarik di jendela overlay dan mengarahkan mereka ke kontrol saya menggunakan RaiseEvent seperti ini:

//in HwndHost code
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    this.RaiseEvent(e);
}

Satu-satunya hal yang perlu Anda lakukan adalah menyinkronkan posisi jendela HwndHost dan overlay, yang tidak terlalu sulit.


0
2018-06-30 19:14



Anda bisa mendapatkan posisi absolut dari mouse (dalam koordinat layar) kapan saja dengan menggunakan fungsi p / Invoke:

[StructLayout(LayoutKind.Sequential)]
private struct point {
    public int x;
    public int y;
}

[DllImport("user32.dll", EntryPoint="GetCursorPos")]
private static extern void GetCursorPos(ref point pt);

Dari sana Anda harus dapat dengan mudah menghitung koordinat relatif.


-1
2018-01-21 05:20