C# 如何在 WPF 中处理 WndProc 消息?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/624367/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle WndProc messages in WPF?
提问by Shuft
In Windows Forms, I'd just override WndProc
, and start handling messages as they came in.
在 Windows 窗体中,我只需覆盖WndProc
,并在消息传入时开始处理它们。
Can someone show me an example of how to achieve the same thing in WPF?
有人可以向我展示如何在 WPF 中实现相同目标的示例吗?
采纳答案by Noldorin
Actually, as far as I understand such a thing is indeed possible in WPF using HwndSource
and HwndSourceHook
. See this thread on MSDNas an example. (Relevant code included below)
实际上,据我所知,在 WPF 中使用HwndSource
and确实可以实现这样的事情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;
}
Now, I'm not quite sure why you'd want to handle Windows Messaging messages in a WPF application (unless it's the most obvious form of interop for working with another WinForms app). The design ideology and the nature of the API is very different in WPF from WinForms, so I would suggest you just familiarise yourself with WPF more to see exactly whythere is no equivalent of WndProc.
现在,我不太确定您为什么要在 WPF 应用程序中处理 Windows Messaging 消息(除非它是与另一个 WinForms 应用程序一起使用的最明显的互操作形式)。WPF 中的设计思想和 API 的性质与 WinForms 非常不同,因此我建议您更多地熟悉 WPF,以确切了解为什么没有 WndProc 的等价物。
回答by user72491
WPF doesn't operate on WinForms type wndprocs
WPF 不在 WinForms 类型 wndprocs 上运行
You can host an HWndHost in an appropriate WPF element then override the Hwndhost's wndproc, but AFAIK that's as close as you're going to get.
您可以在适当的 WPF 元素中托管 HWndHost,然后覆盖 Hwndhost 的 wndproc,但 AFAIK 与您将要获得的一样接近。
http://msdn.microsoft.com/en-us/library/ms742522.aspx
http://msdn.microsoft.com/en-us/library/ms742522.aspx
http://blogs.msdn.com/nickkramer/archive/2006/03/18/554235.aspx
http://blogs.msdn.com/nickkramer/archive/2006/03/18/554235.aspx
回答by JaredPar
The short answer is you can't. WndProc works by passing messages to a HWND on a Win32 level. WPF windows have no HWND and hence can't participate in WndProc messages. The base WPF message loop does sit on top of WndProc but it abstracts them away from core WPF logic.
简短的回答是你不能。WndProc 通过将消息传递到 Win32 级别的 HWND 来工作。WPF 窗口没有 HWND,因此不能参与 WndProc 消息。基本 WPF 消息循环确实位于 WndProc 之上,但它将它们从核心 WPF 逻辑中抽象出来。
You can use a HWndHost and get at a WndProc for it. However this is almost certainly not what you want to do. For the majority of purposes, WPF does not operate on HWND and WndProc. Your solution almost certainly relies on making a change in WPF not in WndProc.
您可以使用 HWndHost 并为其获取 WndProc。然而,这几乎肯定不是您想要做的。对于大多数用途,WPF 不在 HWND 和 WndProc 上运行。您的解决方案几乎肯定依赖于在 WPF 中而不是在 WndProc 中进行更改。
回答by Logan Capaldo
There are ways to handle messages with a WndProc in WPF (e.g. using a HwndSource, etc.), but generally those techniques are reserved for interop with messages that can't directly be handled through WPF. Most WPF controls aren't even windows in the Win32 (and by extension Windows.Forms) sense, so they won't have WndProcs.
有一些方法可以在 WPF 中使用 WndProc 处理消息(例如使用 HwndSource 等),但通常这些技术保留用于与不能直接通过 WPF 处理的消息的互操作。大多数 WPF 控件甚至不是 Win32(以及扩展 Windows.Forms)意义上的窗口,因此它们不会有 WndProcs。
回答by softwerx
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;
}
回答by Robert MacLean
You can do this via the System.Windows.Interop
namespace which contains a class named HwndSource
.
您可以通过System.Windows.Interop
包含名为HwndSource
.
Example of using this
使用这个的例子
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;
}
}
}
Completely taken from the excellent blog post: Using a custom WndProc in WPF apps by Steve Rands
完全取自Steve Rands的优秀博客文章:在 WPF 应用程序中使用自定义 WndProc
回答by Wes
Here is a link on overriding WindProc using Behaviors: http://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35
这是使用行为覆盖 WindProc 的链接:http://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35
[Edit: better late than never] Below is my implementation based on the above link. Although revisiting this I like the AddHook implementations better. I might switch to that.
[编辑:迟到总比不到好] 以下是我基于上述链接的实现。虽然重新审视这个我更喜欢 AddHook 实现。我可能会转向那个。
In my case I wanted to know when the window was being resized and a couple other things. This implementation hooks up to the Window xaml and sends events.
就我而言,我想知道何时调整窗口大小以及其他一些事情。此实现连接到 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>
回答by Tyrrrz
If you don't mind referencing WinForms, you can use a more MVVM-oriented solution that doesn't couple service with the view. You need to create and initialize a System.Windows.Forms.NativeWindow which is a lightweight window that can receive messages.
如果您不介意引用 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;
}
}
Use SpongeHandle to register for messages you're interested in and then override WndProc to process them:
使用 SpongeHandle 注册您感兴趣的消息,然后覆盖 WndProc 来处理它们:
public class WindowsMessageListenerService : WinApiServiceBase
{
protected override void WndProc(Message message)
{
Debug.WriteLine(message.msg);
}
}
The only downside is that you have to include System.Windows.Forms reference, but otherwise this is a very encapsulated solution.
唯一的缺点是您必须包含 System.Windows.Forms 引用,否则这是一个非常封装的解决方案。
More on this can be read here
回答by AndresRohrAtlasInformatik
You can attach to the 'SystemEvents' class of the built-in Win32 class:
您可以附加到内置 Win32 类的“SystemEvents”类:
using Microsoft.Win32;
in a WPF window class:
在 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();
}
}