如何在C# WPF应用中挂钩窗口事件及窗口变化?
嘿,我刚好折腾过WPF里追踪应用内窗口事件的需求,给你几个靠谱的方案,不用再依赖全局钩子啦:
针对WPF应用内窗口事件追踪的解决方案
- 方案一:利用WPF Application类自带的窗口集合与事件
WPF的Application类本身就提供了追踪应用内窗口的能力,你可以直接订阅它的WindowAdded和WindowRemoved事件监控窗口的创建与销毁;同时对每个窗口订阅Activated、Deactivated、LocationChanged、SizeChanged等事件,就能覆盖大部分窗口状态变化场景。
示例代码(放在App.xaml.cs里):
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // 监控新窗口创建 Application.Current.WindowAdded += (s, args) => { var window = args.Window; Console.WriteLine($"窗口创建:{window.Title}"); // 订阅该窗口的各类事件 window.Activated += (sender, eventArgs) => Console.WriteLine($"窗口激活:{window.Title}"); window.Deactivated += (sender, eventArgs) => Console.WriteLine($"窗口失活:{window.Title}"); window.LocationChanged += (sender, eventArgs) => Console.WriteLine($"窗口移动:{window.Title} 新位置:{window.Left},{window.Top}"); window.SizeChanged += (sender, eventArgs) => Console.WriteLine($"窗口大小变化:{window.Title} 新尺寸:{window.Width}x{window.Height}"); window.Closed += (sender, eventArgs) => Console.WriteLine($"窗口关闭:{window.Title}"); }; // 监控窗口移除 Application.Current.WindowRemoved += (s, args) => { Console.WriteLine($"窗口移除:{args.Window.Title}"); }; }
- 方案二:自定义窗口基类统一处理
如果你的应用有多个窗口,一个个订阅事件太麻烦,可以创建一个自定义的窗口基类,把事件处理逻辑统一写在基类里,所有业务窗口都继承这个基类即可。
示例代码:
public class TrackableWindow : Window { public TrackableWindow() { Activated += TrackableWindow_Activated; Deactivated += TrackableWindow_Deactivated; LocationChanged += TrackableWindow_LocationChanged; SizeChanged += TrackableWindow_SizeChanged; Closed += TrackableWindow_Closed; } private void TrackableWindow_Closed(object sender, EventArgs e) { Console.WriteLine($"窗口关闭:{Title}"); } private void TrackableWindow_SizeChanged(object sender, SizeChangedEventArgs e) { Console.WriteLine($"窗口大小变化:{Title} 新尺寸:{Width}x{Height}"); } private void TrackableWindow_LocationChanged(object sender, EventArgs e) { Console.WriteLine($"窗口移动:{Title} 新位置:{Left},{Top}"); } private void TrackableWindow_Deactivated(object sender, EventArgs e) { Console.WriteLine($"窗口失活:{Title}"); } private void TrackableWindow_Activated(object sender, EventArgs e) { Console.WriteLine($"窗口激活:{Title}"); } }
之后你的业务窗口只需继承TrackableWindow:
public class MainWindow : TrackableWindow { // 你的业务逻辑 }
- 方案三:通过HwndSource捕获底层窗口消息(进阶)
如果需要更细粒度的窗口事件(比如窗口最小化、最大化的细节),可以通过HwndSource获取窗口的句柄,然后添加窗口消息钩子,处理Windows原生消息(比如WM_ACTIVATE、WM_MOVE、WM_SIZE等)。
示例代码(在窗口的Loaded事件中添加):
private void Window_Loaded(object sender, RoutedEventArgs e) { var hwndSource = PresentationSource.FromVisual(this) as HwndSource; if (hwndSource != null) { hwndSource.AddHook(WndProc); } } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { const int WM_ACTIVATE = 0x0006; const int WM_MOVE = 0x0003; const int WM_SIZE = 0x0005; const int WM_SYSCOMMAND = 0x0112; const int SC_MINIMIZE = 0xF020; const int SC_MAXIMIZE = 0xF030; const int SC_RESTORE = 0xF120; switch (msg) { case WM_ACTIVATE: bool isActivated = wParam.ToInt32() != 0; Console.WriteLine(isActivated ? $"窗口激活:{Title}" : $"窗口失活:{Title}"); break; case WM_MOVE: Console.WriteLine($"窗口移动:{Title} 新位置:{Left},{Top}"); break; case WM_SIZE: Console.WriteLine($"窗口大小变化:{Title} 新尺寸:{Width}x{Height}"); break; case WM_SYSCOMMAND: int command = wParam.ToInt32() & 0xFFF0; if (command == SC_MINIMIZE) Console.WriteLine($"窗口最小化:{Title}"); else if (command == SC_MAXIMIZE) Console.WriteLine($"窗口最大化:{Title}"); else if (command == SC_RESTORE) Console.WriteLine($"窗口恢复:{Title}"); break; } return IntPtr.Zero; }
这些方案都是仅针对当前WPF应用内的窗口,完全不需要全局钩子,既轻量又不会有跨进程的权限问题,应该能满足你的需求~
内容的提问来源于stack exchange,提问作者Yifei
相关产品推荐
相关产品推荐

