You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:44:28