2009-03-08 9 views
89

Znalezienie WPF na stromą krzywą uczenia się.Jak obsługiwać komunikaty WndProc w WPF?

In good ol”Windows Forms, to bym po prostu zastąpić WndProc i uruchomić obsługi wiadomości, ponieważ przyszedł.

Może ktoś pokazać mi przykład, jak osiągnąć to samo w WPF?

Odpowiedz

45

Właściwie, o ile rozumiem, taka rzecz jest rzeczywiście możliwa w WPF przy użyciu HwndSource i HwndSourceHook. Przykładem jest this thread on MSDN. (Odpowiedni kod zawarte poniżej)

// 'this' is a Window 
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); 
source.AddHook(new HwndSourceHook(WndProc)); 

private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
{ 
    // do stuff 

    return IntPtr.Zero; 
} 

Teraz nie jestem pewien, dlaczego chcesz chcą obsługiwać komunikaty systemu Windows Messaging w aplikacji WPF (chyba, że ​​jest to najbardziej oczywistą formą współdziałanie do pracy z innej aplikacji WinForms). Ideologia projektowania i natura API jest bardzo różna w WPF od WinForms, więc proponuję po prostu zapoznać się z WPF więcej, aby zobaczyć dokładnie , dlaczego nie ma odpowiednika WndProc.

+36

Cóż, urządzenia USB (dis) łączą zdarzenia, które zdają się przechodzić przez tę pętlę wiadomości, więc nie jest źle wiedzieć, jak podłączyć się z WPF – flq

+4

@Noldorin: Czy możesz podać referencje (artykuły/książki), które mogą pomóc rozumiem część "Ideologia projektowania i natura API jest bardzo różna w WPF od WinForms, ... dlaczego nie ma odpowiednika WndProc"? – atiyar

+1

'WM_MOUSEWHEEL' na przykład, jedynym sposobem na niezawodne przechwycenie tych wiadomości było dodanie' WndProc' do okna WPF. To działało dla mnie, podczas gdy oficjalne narzędzie "MouseWheelEventHandler" po prostu nie działało zgodnie z oczekiwaniami. Nie byłem w stanie uzyskać prawidłowych tachionów WPF ustawionych dokładnie tak, aby uzyskać niezawodne zachowanie za pomocą 'MouseWheelEventHandler', stąd potrzeba bezpośredniego dostępu do' WndProc'. –

-8

Krótka odpowiedź brzmi: nie można. WndProc działa poprzez przekazywanie wiadomości do HWND na poziomie Win32. Okna WPF nie mają HWND i dlatego nie mogą uczestniczyć w komunikatach WndProc. Podstawowa pętla komunikatów WPF znajduje się na szczycie WndProc, ale odciąga je od głównej logiki WPF.

Możesz użyć HWndHost i uzyskać dla niego WndProc. Jednak prawie na pewno nie jest to, co chcesz robić. Dla większości celów WPF nie działa na HWND i WndProc. Twoje rozwiązanie prawie na pewno polega na wprowadzaniu zmian w WPF, nie w WndProc.

+7

"Okna WPF nie mają HWND" - To po prostu nieprawda. –

0

Istnieją sposoby obsługi wiadomości z WndProc w WPF (np. Przy użyciu HwndSource itp.), Ale ogólnie te techniki są zarezerwowane dla współdziałania z wiadomościami, które nie mogą być bezpośrednio obsługiwane za pośrednictwem WPF. Większość kontrolek WPF nie ma nawet okien w systemie Win32 (i przez rozszerzenie Windows.Forms), więc nie będą miały WndProcs.

14
HwndSource src = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); 
src.AddHook(new HwndSourceHook(WndProc)); 


....... 


public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
{ 

    if(msg == THEMESSAGEIMLOOKINGFOR) 
    { 
     //Do something here 
    } 

    return IntPtr.Zero; 
} 
118

Można to zrobić za pośrednictwem System.Windows.Interop nazw zawierającej klasę o nazwie HwndSource.

Przykład wykorzystania tej

using System; 
using System.Windows; 
using System.Windows.Interop; 

namespace WpfApplication1 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnSourceInitialized(EventArgs e) 
     { 
      base.OnSourceInitialized(e); 
      HwndSource source = PresentationSource.FromVisual(this) as HwndSource; 
      source.AddHook(WndProc); 
     } 

     private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
     { 
      // Handle messages... 

      return IntPtr.Zero; 
     } 
    } 
} 

Całkowicie zaczerpnięty z doskonałą blogu: Using a custom WndProc in WPF apps by Steve Rands (uwaga, związek nie jest już ważna)

Ta strona jest w dół, ale teraz można go zobaczyć z Wayback engine: http://web.archive.org/web/20091019124817/http://www.steverands.com/2009/03/19/custom-wndproc-wpf-apps/

+0

Link jest uszkodzony. Czy możesz to naprawić? –

+1

@Martin, bo strona Steve'a Randa już nie istnieje. Jedyną poprawką, jaką mogę wymyślić, jest jej usunięcie. Myślę, że nadal przydaje się, jeśli witryna powróci w przyszłości, więc nie usuwam jej - ale jeśli nie zgadzasz się, możesz ją edytować. –

+0

Czy można odbierać wiadomości WndProc bez okna? – Mo0gles

4

Możesz znaleźć inne wyjaśnienie dołączenia do WndProc here.

0

Jeśli nie masz nic przeciwko odwoływaniu się do WinForms, możesz użyć bardziej zorientowanego na MVVM rozwiązania, które nie łączy usługi z widokiem. Należy utworzyć i zainicjować System.Windows.Forms.NativeWindow, które jest lekkim oknem, które może odbierać wiadomości.

public abstract class WinApiServiceBase : IDisposable 
{ 
    /// <summary> 
    /// Sponge window absorbs messages and lets other services use them 
    /// </summary> 
    private sealed class SpongeWindow : NativeWindow 
    { 
     public event EventHandler<Message> WndProced; 

     public SpongeWindow() 
     { 
      CreateHandle(new CreateParams()); 
     } 

     protected override void WndProc(ref Message m) 
     { 
      WndProced?.Invoke(this, m); 
      base.WndProc(ref m); 
     } 
    } 

    private static readonly SpongeWindow Sponge; 
    protected static readonly IntPtr SpongeHandle; 

    static WinApiServiceBase() 
    { 
     Sponge = new SpongeWindow(); 
     SpongeHandle = Sponge.Handle; 
    } 

    protected WinApiServiceBase() 
    { 
     Sponge.WndProced += LocalWndProced; 
    } 

    private void LocalWndProced(object sender, Message message) 
    { 
     WndProc(message); 
    } 

    /// <summary> 
    /// Override to process windows messages 
    /// </summary> 
    protected virtual void WndProc(Message message) 
    { } 

    public virtual void Dispose() 
    { 
     Sponge.WndProced -= LocalWndProced; 
    } 
} 

Zastosowanie SpongeHandle zarejestrować wiadomości, które Cię interesują, a następnie zastąpić WndProc przetwarzać je:

public class WindowsMessageListenerService : WinApiServiceBase 
{ 
    protected override void WndProc(Message message) 
    { 
     Debug.WriteLine(message.msg); 
    } 
} 

Jedynym minusem jest to, że trzeba to System.Windows.Forms odniesienie, ale w przeciwnym razie jest to bardzo zamknięte rozwiązanie.

Więcej na ten temat można przeczytać here

0

Możesz dołączyć do Class 'SystemEvents' wbudowanego w klasie Win32:

using Microsoft.Win32; 

w WPF klasy okna:

SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; 
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; 
SystemEvents.SessionEnding += SystemEvents_SessionEnding; 
SystemEvents.SessionEnded += SystemEvents_SessionEnded; 

private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) 
{ 
    await vm.PowerModeChanged(e.Mode); 
} 

private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) 
{ 
    await vm.PowerModeChanged(e.Mode); 
} 

private async void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e) 
{ 
    await vm.SessionSwitch(e.Reason); 
} 

private async void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) 
{ 
    if (e.Reason == SessionEndReasons.Logoff) 
    { 
     await vm.UserLogoff(); 
    } 
} 

private async void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e) 
{ 
    if (e.Reason == SessionEndReasons.Logoff) 
    { 
     await vm.UserLogoff(); 
    } 
} 
Powiązane problemy