Как обрабатывать сообщения WndProc в WPF?

Поиск WPF крутой кривой обучения.

в хороших старых Windows Forms я бы просто переопределил WndProc, и начинайте обрабатывать сообщения по мере их поступления.

может ли кто-нибудь показать мне пример того, как достичь того же самого в WPF?

8 ответов


на самом деле, насколько я понимаю, такая вещь действительно возможно в WPF с помощью HwndSource и HwndSourceHook. См.этот поток на MSDN в качестве примера. (Соответствующий код включен ниже)

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

Теперь я не совсем уверен, почему вы хотите обрабатывать сообщения Windows Messaging в приложении WPF (если это не самая очевидная форма взаимодействия для работы с другим приложением WinForms). Идеология дизайна и характер API сильно отличаются в WPF от WinForms, поэтому я предложил бы вам просто ознакомиться с WPF больше, чтобы увидеть именно почему нет эквивалента WndProc.


вы можете сделать это с помощью System.Windows.Interop пространство имен, содержащее класс с именем HwndSource.

пример использования

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

полностью взяты из отличного сообщения в блоге:С помощью настраиваемого WndProc, который в WPF приложениях Стив рандов


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

есть способы обработки сообщений с помощью WndProc в WPF (например, с помощью HwndSource и т. д.), но обычно эти методы зарезервированы для взаимодействия с сообщениями, которые не могут быть напрямую обработаны через WPF. Большинство элементов управления WPF даже не являются окнами в Win32 (и по расширению Windows.Формы) смысл, поэтому у них не будет WndProcs.


вы можете присоединить к классу "SystemEvents" встроенного класса Win32:

using Microsoft.Win32;

в классе окна 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();
    }
}

Если вы не возражаете ссылаться на WinForms, вы можете использовать более ориентированное на MVVM решение, которое не связывает сервис с представлением. Необходимо создать и Инициализировать систему.Окна.Формы.NativeWindow-это легкое окно, которое может принимать сообщения.

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

используйте SpongeHandle для регистрации интересующих вас сообщений, а затем переопределите WndProc для их обработки:

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

единственным недостатком является то, что вы должны включать систему.Окна.Формы ссылка, но в остальном это очень инкапсулированное решение.

подробнее об этом можно прочитать здесь


WPF не работает на WinForms типа wndprocs

вы можете разместить HWndHost в соответствующем элементе WPF, а затем переопределить wndproc Hwndhost, но AFAIK это так близко, как вы собираетесь получить.

http://msdn.microsoft.com/en-us/library/ms742522.aspx

http://blogs.msdn.com/nickkramer/archive/2006/03/18/554235.aspx


короткий ответ - вы не можете. WndProc работает путем передачи сообщений HWND на уровне Win32. Окна WPF не имеют HWND и, следовательно, не могут участвовать в сообщениях WndProc. Базовый цикл сообщений WPF находится поверх WndProc, но абстрагирует их от основной логики WPF.

вы можете использовать HWndHost и получить WndProc для него. Однако это почти наверняка не то, что вы хотите сделать. Для большинства целей WPF не работает на HWND и WndProc. Ваше решение почти наверняка полагается на внесение изменений в WPF, а не в WndProc.