Trouver WPF une courbe d'apprentissage abrupte.
Dans le bon vieux Windows Forms, je remplaçais simplement WndProc
et commençais à gérer les messages au fur et à mesure de leur arrivée.
Est-ce que quelqu'un peut me montrer un exemple de la façon de réaliser la même chose dans WPF?
En fait, autant que je sache, une telle chose est effectivement possible dans WPF en utilisant HwndSource
et HwndSourceHook
. Voir ce fil sur MSDN à titre d'exemple. (Code pertinent inclus ci-dessous)
// '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;
}
Maintenant, je ne sais pas trop pourquoi vous voudriez gérer les messages Windows Messaging dans une application WPF (à moins que ce ne soit la forme d'interopérabilité la plus évidente pour travailler avec une autre application WinForms). L'idéologie de conception et la nature de l'API sont très différentes de WPF à WinForms. Je vous suggère donc de vous familiariser davantage avec WPF pour voir exactement pourquoi il n'y a pas d'équivalent de WndProc.
Vous pouvez le faire via le System.Windows.Interop
espace de noms qui contient une classe nommée HwndSource
.
Exemple d'utilisation this
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;
}
}
}
Complètement tiré de l'excellent article de blog: tilisation d'un WndProc personnalisé dans les applications WPF de 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;
}
Si le fait de référencer WinForm ne vous dérange pas, vous pouvez utiliser une solution davantage axée sur MVVM qui ne couple pas le service à la vue. Vous devez créer et initialiser une System.Windows.Forms.NativeWindow qui est une fenêtre légère pouvant recevoir des messages.
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;
}
}
Utilisez SpongeHandle pour vous inscrire aux messages qui vous intéressent, puis remplacez WndProc pour les traiter:
public class WindowsMessageListenerService : WinApiServiceBase
{
protected override void WndProc(Message message)
{
Debug.WriteLine(message.msg);
}
}
Le seul inconvénient est que vous devez inclure la référence System.Windows.Forms, mais sinon, il s'agit d'une solution très encapsulée.
Plus d'informations à ce sujet peuvent être lues ici
Il existe des moyens de gérer les messages avec un WndProc dans WPF (par exemple, en utilisant un HwndSource, etc.), mais ces techniques sont généralement réservées pour l’interopérabilité avec des messages qui ne peuvent pas être traités directement par WPF. La plupart des contrôles WPF ne sont même pas des fenêtres au sens Win32 (et par extension Windows.Forms), ils n'auront donc pas WndProcs.
Vous pouvez attacher à la classe 'SystemEvents' de la classe intégrée Win32:
using Microsoft.Win32;
dans une classe de fenêtre 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();
}
}