質問
WPFの急な学習曲線を見つける。
古き良きWindows Formsでは、 WndProc
をオーバーライドして、メッセージが入ったときにメッセージの処理を開始します。
WPFで同じことを実現する方法の例を教えてもらえますか?
解決
実際、私が理解している限り、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;
}
今、WPFアプリケーションでWindows Messagingメッセージを処理する理由がよくわかりません(別のWinFormsアプリを操作するための最も明白な相互運用形態でない限り)。デザインのイデオロギーとAPIの性質はWPFとWinFormsで大きく異なるため、WndProcに相当するものがないことを正確に確認するために、WPFに精通することをお勧めします。
他のヒント
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;
}
}
}
優れたブログ投稿から完全に抜粋: WPFアプリでのカスタムWndProcの使用by 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;
}
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参照を含める必要があることですが、それ以外の場合、これは非常にカプセル化されたソリューションです。
詳細については、こちら
をご覧ください。WPFでWndProcを使用してメッセージを処理する方法はありますが(たとえば、HwndSourceを使用するなど)、一般にこれらの手法は、WPFで直接処理できないメッセージとの相互運用のために予約されています。ほとんどのWPFコントロールは、Win32(および拡張機能ではWindows.Forms)の意味でのウィンドウではないため、WndProcsはありません。
組み込みの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();
}
}
WPFは、WinFormsタイプのwndprocsでは動作しません
適切なWPF要素でHWndHostをホストしてから、Hwndhostのwndprocをオーバーライドできますが、知る限りではAFAIKが近づいています。
http://msdn.microsoft.com/en-us/library /ms742522.aspx
http://blogs.msdn.com/nickkramer /archive/2006/03/18/554235.aspx
簡単な答えは、できません。 WndProcは、Win32レベルでHWNDにメッセージを渡すことで機能します。 WPFウィンドウにはHWNDがないため、WndProcメッセージに参加できません。基本WPFメッセージループはWndProcの上にありますが、コアWPFロジックから抽象化します。
HWndHostを使用して、WndProcを取得できます。しかし、これはほぼ間違いなくあなたがやりたいことではありません。ほとんどの目的で、WPFはHWNDおよびWndProcで動作しません。ソリューションは、ほぼ確実にWndProcではなくWPFを変更することに依存しています。