ใน Windows Forms ฉันจะลบล้างWndProc
และเริ่มจัดการข้อความเมื่อเข้ามา
ใครช่วยแสดงตัวอย่างวิธีบรรลุสิ่งเดียวกันใน 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;
}
ตอนนี้ฉันไม่ค่อยแน่ใจว่าทำไมคุณถึงต้องการจัดการข้อความ Windows Messaging ในแอปพลิเคชัน WPF (เว้นแต่จะเป็นรูปแบบการทำงานร่วมกันที่ชัดเจนที่สุดสำหรับการทำงานกับแอป WinForms อื่น) อุดมการณ์การออกแบบและลักษณะของ API นั้นแตกต่างกันมากใน WPF จาก WinForms ดังนั้นฉันขอแนะนำให้คุณทำความคุ้นเคยกับ WPF ให้มากขึ้นเพื่อดูว่าเหตุใดจึงไม่มี WndProc เทียบเท่า
WM_MOUSEWHEEL
ตัวอย่างเช่นวิธีเดียวที่จะดักจับข้อความเหล่านั้นได้อย่างน่าเชื่อถือคือการเพิ่มWndProc
หน้าต่าง WPF สิ่งนี้ใช้ได้ผลสำหรับฉันในขณะที่เจ้าหน้าที่MouseWheelEventHandler
ไม่ได้ผลตามที่คาดไว้ ฉันไม่สามารถรับ tachyons WPF ที่ถูกต้องเรียงกันเพื่อให้ได้พฤติกรรมที่เชื่อถือได้MouseWheelEventHandler
ดังนั้นจึงจำเป็นต้องเข้าถึงโดยตรงไปยังไฟล์WndProc
.
คุณสามารถทำเช่นนี้ผ่านทางSystem.Windows.Interop
namespace 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 โดย 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 แต่อย่างอื่นนี่เป็นโซลูชันที่ห่อหุ้มไว้มาก
สามารถอ่านเพิ่มเติมได้ที่นี่
นี่คือลิงค์เกี่ยวกับการลบล้าง WindProc โดยใช้ Behaviors: http://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35
[แก้ไข: ดีกว่าไม่ช้า] ด้านล่างนี้คือการใช้งานของฉันตามลิงค์ด้านบน แม้ว่าจะกลับมาดูสิ่งนี้ฉันชอบการใช้งาน AddHook ที่ดีกว่า ฉันอาจเปลี่ยนไปใช้แบบนั้น
ในกรณีของฉันฉันต้องการทราบว่าเมื่อมีการปรับขนาดหน้าต่างและอีกสองสิ่ง การนำไปใช้งานนี้เชื่อมต่อกับ Window xaml และส่งเหตุการณ์
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...
คำตอบเหมือนข้างบน
คุณสามารถแนบกับคลาส '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();
}
}
มีหลายวิธีในการจัดการข้อความด้วย WndProc ใน WPF (เช่นการใช้ HwndSource เป็นต้น) แต่โดยทั่วไปเทคนิคเหล่านี้สงวนไว้สำหรับการทำงานร่วมกันกับข้อความที่ไม่สามารถจัดการโดยตรงผ่าน WPF การควบคุม WPF ส่วนใหญ่ไม่ใช่หน้าต่างใน Win32 (และโดยส่วนขยาย Windows.Forms) ดังนั้นจึงไม่มี WndProcs
WndProc
การลบล้าง แต่ก็System.Windows.Interop
ช่วยให้คุณได้รับHwndSource
วัตถุโดยวิธีการHwndSource.FromHwnd
หรือPresentationSource.FromVisual(someForm) as HwndSource
คุณสามารถผูกผู้รับมอบสิทธิ์ที่มีรูปแบบพิเศษได้ ผู้รับมอบสิทธิ์นี้มีอาร์กิวเมนต์เดียวกันกับWndProc
วัตถุข้อความ
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