web-dev-qa-db-ja.com

WPFでWndProcメッセージを処理する方法は?

WPFの急な学習曲線を見つける。

古き良きWindows Formsでは、WndProcをオーバーライドし、メッセージが入ったときに処理を開始します。

誰かがWPFで同じことを達成する方法の例を教えてもらえますか?

105
Shuft

実際、私が理解している限り、HwndSourceHwndSourceHookを使用して、WPFでそのようなことが実際に可能です。例として 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;
}

今、WPFアプリケーションでWindows Messagingメッセージを処理する理由がよくわかりません(別のWinFormsアプリを操作するための最も明白な相互運用形態でない限り)。デザインのイデオロギーとAPIの性質はWPFとWinFormsで非常に異なるため、正確にwhyを見るためにWPFをよく理解することをお勧めしますWndProcに相当するものはありません。

56
Noldorin

これは、HwndSourceという名前のクラスを含むSystem.Windows.Interop名前空間を介して実行できます。

これの使用例

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

優れたブログ投稿から完全に抜粋: Steve RandsによるWPFアプリでのカスタムWndProcの使用

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

WinFormsの参照を気にしない場合は、ビューとサービスを結び付けない、よりMVVM指向のソリューションを使用できます。メッセージを受信できる軽量ウィンドウであるSystem.Windows.Forms.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);
    }
}

唯一の欠点は、System.Windows.Forms参照を含める必要があることですが、それ以外の場合、これは非常にカプセル化されたソリューションです。

これについての詳細は here で読むことができます。

2
Tyrrrz

WPFでWndProcを使用してメッセージを処理する方法はありますが(たとえば、HwndSourceを使用するなど)、通常、これらの手法は、WPFで直接処理できないメッセージとの相互運用のために予約されています。ほとんどのWPFコントロールは、Win32(および拡張機能ではWindows.Forms)の意味でのウィンドウではないため、WndProcsはありません。

0
Logan Capaldo

組み込みのWin32クラスの「SystemEvents」クラスにアタッチできます。

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