web-dev-qa-db-fra.com

Comment gérer les messages WndProc dans WPF?

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?

105
Shuft

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.

56
Noldorin

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

129
Robert MacLean
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;
}
14
softwerx

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

2
Tyrrrz

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.

0
Logan Capaldo

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