2009-03-08 10 views
89

WPF eine steile Lernkurve zu finden.Wie behandelt man WndProc Nachrichten in WPF?

In guten alten Windows Forms, würde ich außer Kraft setzen nur WndProc und starten Umgang mit Nachrichten, wie sie hereinkam.

Kann mir jemand ein Beispiel dafür, wie die gleiche Sache in WPF zu erreichen?

Antwort

45

Eigentlich soweit ich das verstehe ist in der Tat in WPF mit HwndSource und HwndSourceHook möglich. Siehe this thread on MSDN als ein Beispiel. (Relevante Code enthalten unten)

// '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; 
} 

Nun, ich bin mir nicht ganz sicher, warum Sie wollen würde Windows Messaging-Nachrichten in einer WPF-Anwendung behandeln (es sei denn, es die naheliegendste Form von Interop ist für mit einem anderen WinForms-Anwendung arbeiten). Die Design-Ideologie und die Art der API ist in WPF von WinForms sehr unterschiedlich, also würde ich vorschlagen, dass Sie sich nur mit WPF vertraut machen, um genauer zu sehen, warum gibt es keine Entsprechung von WndProc.

+36

Nun, USB Device (dis) connect Ereignisse scheinen über diese Nachrichtenschleife zu kommen, also ist es keine schlechte Sache zu wissen, wie man WPF verbindet – flq

+4

@Noldorin: Können Sie bitte Referenzen (Artikel/Bücher) zur Verfügung stellen, die helfen können Ich verstehe den Teil "Die Design-Ideologie und die Art der API ist sehr unterschiedlich in WPF von WinForms, ... warum gibt es kein Äquivalent von WndProc"? – atiyar

+1

'WM_MOUSEWHEEL' zum Beispiel, der einzige Weg, um diese Nachrichten zuverlässig zu fangen, war durch Hinzufügen der' WndProc' zu einem WPF-Fenster. Das funktionierte für mich, während der offizielle 'MouseWheelEventHandler' einfach nicht wie erwartet funktionierte. Ich war nicht in der Lage, die richtigen WPF-Tachyonen richtig aufgereiht zu bekommen, um zuverlässiges Verhalten mit "MouseWheelEventHandler" zu bekommen, daher die Notwendigkeit eines direkten Zugriffs auf den "WndProc". –

-8

Die kurze Antwort ist, dass Sie nicht können. WndProc funktioniert, indem er Nachrichten an ein HWND auf einer Win32-Ebene übergibt. WPF-Fenster haben kein HWND und können daher nicht an WndProc-Nachrichten teilnehmen. Die Basis-WPF-Nachrichtenschleife sitzt zwar oben auf WndProc, abstrahiert sie jedoch von der WPF-Kernlogik.

Sie können einen HWndHost verwenden und an einem WndProc dafür erhalten. Das ist jedoch sicher nicht das, was Sie tun möchten. Für die meisten Zwecke arbeitet WPF nicht mit HWND und WndProc. Ihre Lösung beruht fast sicher auf einer Änderung in WPF nicht in WndProc.

+7

"WPF-Fenster haben kein HWND" - Das ist einfach nicht wahr. –

0

Es gibt Möglichkeiten, Nachrichten mit einem WndProc in WPF zu verarbeiten (z. B. mit einer HwndSource usw.), aber diese Techniken sind im Allgemeinen für Interop mit Nachrichten reserviert, die nicht direkt über WPF verarbeitet werden können. Die meisten WPF-Steuerelemente sind nicht einmal Windows in der Win32-Version (und in der Erweiterung Windows.Forms), also werden sie keine WndProcs haben.

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

Sie können dies über den System.Windows.Interop Namespace, die eine Klasse HwndSource Namen enthält.

Beispiel für die Verwendung dieser

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

vollständig von der hervorragenden Blog-Post genommen: Using a custom WndProc in WPF apps by Steve Rands (beachten Sie, ist Link nicht mehr gültig)

Diese Seite ist jetzt nach unten, aber Sie können es von dem Wayback sehen Motor: http://web.archive.org/web/20091019124817/http://www.steverands.com/2009/03/19/custom-wndproc-wpf-apps/

+0

Die Verbindung ist unterbrochen. Könnten Sie es bitte reparieren? –

+1

@Martin, weil Steve Rands Website nicht mehr existiert. Die einzige Lösung, die ich mir vorstellen kann, ist, sie zu entfernen. Ich denke, es bringt noch einen Mehrwert, wenn die Seite in der Zukunft zurückkehrt, also entferne ich sie nicht - aber wenn Sie nicht einverstanden sind, können Sie sie bearbeiten. –

+0

Ist es möglich, WndProc-Nachrichten ohne ein Fenster zu empfangen? – Mo0gles

4

Sie können eine andere Erklärung für das Anfügen an WndProc here finden.

0

Wenn es Ihnen nichts ausmacht, auf WinForms zu verweisen, können Sie eine MVVM-orientierte Lösung verwenden, die den Dienst nicht mit der Ansicht koppelt. Sie müssen ein System.Windows.Forms.NativeWindow erstellen und initialisieren, das ein kleines Fenster ist, das Nachrichten empfangen kann.

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

Verwenden SpongeHandle für Nachrichten registrieren Sie interessiert sind und dann außer Kraft setzen WndProc sie zu verarbeiten:

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

Der einzige Nachteil ist, dass Sie System.Windows.Forms Referenz enthalten müssen, aber Ansonsten ist dies eine sehr gekapselte Lösung.

auf dieses Mehr kann here lesen

0

Sie zu den 'SystemEvents' Klasse anhängen der eingebauten in Win32-Klasse:

using Microsoft.Win32; 

in einer WPF-Fensterklasse:

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