Come gestire i messaggi WndProc in WPF?
Domanda
Trovare WPF una curva di apprendimento ripida.
In buona parte di Windows Form, vorrei semplicemente ignorare WndProc
e iniziare a gestire i messaggi non appena venivano.
Qualcuno può mostrarmi un esempio di come ottenere la stessa cosa in WPF?
Soluzione
In realtà, per quanto ho capito una cosa del genere è davvero possibile in WPF usando HwndSource
e HwndSourceHook
. Vedi questa discussione su MSDN come esempio. (Codice pertinente incluso di seguito)
// '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;
}
Ora, non sono del tutto sicuro del motivo per cui vorresti gestire i messaggi di Windows Messaging in un'applicazione WPF (a meno che non sia la forma più ovvia di interoperabilità per lavorare con un'altra app WinForms). L'ideologia del design e la natura dell'API sono molto diverse in WPF da WinForms, quindi suggerirei di familiarizzare con WPF per vedere esattamente perché non esiste un equivalente di WndProc.
Altri suggerimenti
Puoi farlo tramite lo spazio dei nomi System.Windows.Interop
che contiene una classe chiamata HwndSource
.
Esempio di utilizzo di questo
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;
}
}
}
Completamente tratto dall'eccellente post sul blog: Utilizzo di un WndProc personalizzato nelle app WPF di Steve Rands
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;
}
Se non ti dispiace fare riferimento a WinForms, puoi utilizzare una soluzione più orientata a MVVM che non abbina il servizio alla vista. Devi creare e inizializzare una System.Windows.Forms.NativeWindow che è una finestra leggera in grado di ricevere messaggi.
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;
}
}
Usa SpongeHandle per registrarti per i messaggi che ti interessano e quindi sovrascrivi WndProc per elaborarli:
public class WindowsMessageListenerService : WinApiServiceBase
{
protected override void WndProc(Message message)
{
Debug.WriteLine(message.msg);
}
}
L'unico aspetto negativo è che devi includere il riferimento System.Windows.Forms, ma per il resto questa è una soluzione molto incapsulata.
Altre informazioni al riguardo possono essere lette qui
Esistono modi per gestire i messaggi con un WndProc in WPF (ad es. usando un HwndSource, ecc.), ma generalmente quelle tecniche sono riservate all'interoperabilità con messaggi che non possono essere gestiti direttamente tramite WPF. La maggior parte dei controlli WPF non sono nemmeno windows nel senso di Win32 (e per estensione Windows.Forms), quindi non avranno WndProcs.
Puoi collegarti alla classe 'SystemEvents' della classe Win32 integrata:
using Microsoft.Win32;
in una classe di finestra WPF:
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();
}
}
WPF non funziona su wndprocs di tipo WinForms
Puoi ospitare un HWndHost in un elemento WPF appropriato, quindi sovrascrivere il wndproc di Hwndhost, ma AFAIK è il più vicino possibile.
http://msdn.microsoft.com/en-us/library /ms742522.aspx
http://blogs.msdn.com/nickkramer /archive/2006/03/18/554235.aspx
La risposta breve è che non puoi. WndProc funziona passando i messaggi a un HWND a livello Win32. Le finestre WPF non hanno HWND e quindi non possono partecipare ai messaggi WndProc. Il ciclo di messaggi WPF di base si trova in cima a WndProc ma li allontana dalla logica WPF di base.
Puoi usare un HWndHost e ottenere un WndProc per questo. Tuttavia, questo non è quasi certamente quello che vuoi fare. Per la maggior parte degli scopi, WPF non funziona su HWND e WndProc. La tua soluzione si basa quasi sicuramente su un cambiamento in WPF e non in WndProc.