Jawaban:
Sebenarnya, sejauh yang saya pahami hal seperti itu memang mungkin di WPF menggunakan HwndSource
dan HwndSourceHook
. Lihat utas ini di MSDN sebagai contoh. (Kode yang relevan disertakan di bawah)
// '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;
}
Sekarang, saya tidak begitu yakin mengapa Anda ingin menangani pesan Perpesanan Windows dalam aplikasi WPF (kecuali itu adalah bentuk interop yang paling jelas untuk bekerja dengan aplikasi WinForms lain). Ideologi desain dan sifat API sangat berbeda di WPF dari WinForms, jadi saya sarankan Anda membiasakan diri dengan WPF lebih banyak untuk melihat dengan tepat mengapa tidak ada yang setara dengan WndProc.
WM_MOUSEWHEEL
misalnya, satu-satunya cara untuk menjebak pesan tersebut adalah dengan menambahkan WndProc
ke jendela WPF. Ini berhasil untuk saya, sedangkan pejabat MouseWheelEventHandler
tidak bekerja seperti yang diharapkan. Saya tidak bisa mendapatkan tachyons WPF yang benar yang berbaris tepat untuk mendapatkan perilaku yang dapat diandalkan MouseWheelEventHandler
, oleh karena itu perlu akses langsung ke WndProc
.
Anda dapat melakukan ini melalui System.Windows.Interop
namespace yang berisi kelas bernama HwndSource
.
Contoh penggunaan ini
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;
}
}
}
Sepenuhnya diambil dari posting blog yang luar biasa: Menggunakan WndProc khusus di aplikasi WPF oleh 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;
}
Jika Anda tidak keberatan merujuk WinForms, Anda dapat menggunakan solusi yang lebih berorientasi MVVM yang tidak memasangkan layanan dengan tampilan. Anda perlu membuat dan menginisialisasi System.Windows.Forms.NativeWindow yang merupakan jendela ringan yang dapat menerima pesan.
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;
}
}
Gunakan SpongeHandle untuk mendaftar pesan yang Anda minati dan kemudian ganti WndProc untuk memprosesnya:
public class WindowsMessageListenerService : WinApiServiceBase
{
protected override void WndProc(Message message)
{
Debug.WriteLine(message.msg);
}
}
Satu-satunya downside adalah Anda harus menyertakan referensi System.Windows.Forms, tetapi selain itu ini adalah solusi yang sangat dikemas.
Lebih lanjut tentang ini bisa dibaca di sini
Berikut ini tautan untuk mengganti WindProc menggunakan Behaviors: http://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35
[Sunting: lebih baik terlambat daripada tidak sama sekali] Di bawah ini adalah implementasi saya berdasarkan tautan di atas. Meskipun mengunjungi kembali ini, saya lebih menyukai implementasi AddHook. Saya mungkin beralih ke itu.
Dalam kasus saya, saya ingin tahu kapan jendela diubah ukurannya dan beberapa hal lainnya. Implementasi ini terhubung ke Window xaml dan mengirimkan event.
using System;
using System.Windows.Interactivity;
using System.Windows; // For Window in behavior
using System.Windows.Interop; // For Hwnd
public class WindowResizeEvents : Behavior<Window>
{
public event EventHandler Resized;
public event EventHandler Resizing;
public event EventHandler Maximized;
public event EventHandler Minimized;
public event EventHandler Restored;
public static DependencyProperty IsAppAskCloseProperty = DependencyProperty.RegisterAttached("IsAppAskClose", typeof(bool), typeof(WindowResizeEvents));
public Boolean IsAppAskClose
{
get { return (Boolean)this.GetValue(IsAppAskCloseProperty); }
set { this.SetValue(IsAppAskCloseProperty, value); }
}
// called when the behavior is attached
// hook the wndproc
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += (s, e) =>
{
WireUpWndProc();
};
}
// call when the behavior is detached
// clean up our winproc hook
protected override void OnDetaching()
{
RemoveWndProc();
base.OnDetaching();
}
private HwndSourceHook _hook;
private void WireUpWndProc()
{
HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;
if (source != null)
{
_hook = new HwndSourceHook(WndProc);
source.AddHook(_hook);
}
}
private void RemoveWndProc()
{
HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;
if (source != null)
{
source.RemoveHook(_hook);
}
}
private const Int32 WM_EXITSIZEMOVE = 0x0232;
private const Int32 WM_SIZING = 0x0214;
private const Int32 WM_SIZE = 0x0005;
private const Int32 SIZE_RESTORED = 0x0000;
private const Int32 SIZE_MINIMIZED = 0x0001;
private const Int32 SIZE_MAXIMIZED = 0x0002;
private const Int32 SIZE_MAXSHOW = 0x0003;
private const Int32 SIZE_MAXHIDE = 0x0004;
private const Int32 WM_QUERYENDSESSION = 0x0011;
private const Int32 ENDSESSION_CLOSEAPP = 0x1;
private const Int32 WM_ENDSESSION = 0x0016;
private IntPtr WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled)
{
IntPtr result = IntPtr.Zero;
switch (msg)
{
case WM_SIZING: // sizing gets interactive resize
OnResizing();
break;
case WM_SIZE: // size gets minimize/maximize as well as final size
{
int param = wParam.ToInt32();
switch (param)
{
case SIZE_RESTORED:
OnRestored();
break;
case SIZE_MINIMIZED:
OnMinimized();
break;
case SIZE_MAXIMIZED:
OnMaximized();
break;
case SIZE_MAXSHOW:
break;
case SIZE_MAXHIDE:
break;
}
}
break;
case WM_EXITSIZEMOVE:
OnResized();
break;
// Windows is requesting app to close.
// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa376890%28v=vs.85%29.aspx.
// Use the default response (yes).
case WM_QUERYENDSESSION:
IsAppAskClose = true;
break;
}
return result;
}
private void OnResizing()
{
if (Resizing != null)
Resizing(AssociatedObject, EventArgs.Empty);
}
private void OnResized()
{
if (Resized != null)
Resized(AssociatedObject, EventArgs.Empty);
}
private void OnRestored()
{
if (Restored != null)
Restored(AssociatedObject, EventArgs.Empty);
}
private void OnMinimized()
{
if (Minimized != null)
Minimized(AssociatedObject, EventArgs.Empty);
}
private void OnMaximized()
{
if (Maximized != null)
Maximized(AssociatedObject, EventArgs.Empty);
}
}
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:behaviors="clr-namespace:RapidCoreConfigurator._Behaviors"
Title="name" Height="500" Width="750" BorderBrush="Transparent">
<i:Interaction.Behaviors>
<behaviors:WindowResizeEvents IsAppAskClose="{Binding IsRequestClose, Mode=OneWayToSource}"
Resized="Window_Resized"
Resizing="Window_Resizing" />
</i:Interaction.Behaviors>
...
</Window>
Here is a link...
jawaban tepat , seperti di atas.
Anda dapat melampirkan ke kelas 'SystemEvents' dari kelas Win32 bawaan:
using Microsoft.Win32;
di kelas jendela 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();
}
}
Ada cara untuk menangani pesan dengan WndProc di WPF (misalnya menggunakan HwndSource, dll.), Tetapi umumnya teknik tersebut dicadangkan untuk interop dengan pesan yang tidak dapat ditangani secara langsung melalui WPF. Kebanyakan kontrol WPF bahkan bukan windows dalam pengertian Win32 (dan dengan ekstensi Windows.Forms), jadi mereka tidak akan memiliki WndProcs.
WndProc
untuk ditimpa, namun System.Windows.Interop
memungkinkan Anda untuk mendapatkan HwndSource
objek dengan cara HwndSource.FromHwnd
atau PresentationSource.FromVisual(someForm) as HwndSource
, yang dapat Anda ikat ke delegasi berpola khusus. Delegasi ini memiliki banyak argumen yang sama dengan WndProc
objek Message.
WPF tidak beroperasi pada wndprocs tipe WinForms
Anda dapat meng-host HWndHost dalam elemen WPF yang sesuai kemudian menimpa wndproc Hwndhost, tetapi AFAIK sedekat yang akan Anda dapatkan.
http://msdn.microsoft.com/en-us/library/ms742522.aspx
http://blogs.msdn.com/nickkramer/archive/2006/03/18/554235.aspx
Jawaban singkatnya adalah Anda tidak bisa. WndProc bekerja dengan meneruskan pesan ke HWND pada level Win32. Jendela WPF tidak memiliki HWND dan karenanya tidak dapat berpartisipasi dalam pesan WndProc. Loop pesan WPF dasar tidak berada di atas WndProc tetapi memisahkannya dari logika inti WPF.
Anda dapat menggunakan HWndHost dan mendapatkan WndProc untuk itu. Namun ini hampir pasti bukan yang ingin Anda lakukan. Untuk sebagian besar tujuan, WPF tidak beroperasi di HWND dan WndProc. Solusi Anda hampir pasti bergantung pada membuat perubahan di WPF, bukan di WndProc.