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

WPF MVVM中订阅窗口PreviewKeyUp/PreviewKeyDown事件是否会引发内存泄漏?

WPF 订阅父窗口键盘事件内存泄漏问题解答

核心结论

  • 该写法存在明确的内存泄漏风险,GC 不会自动回收被订阅引用锁住的 UserControl 实例
  • 必须在 Unloaded 事件中手动取消事件订阅,避免内存持续占用

原因说明

WPF 事件订阅的本质是发布者持有订阅者的强引用:
你代码中的发布者是 Window 实例,订阅者是当前 UserControl1 实例。只要 Window 没有被销毁回收,它就会一直持有 UserControl1 的引用,哪怕你把 UserControl1 从视觉树中完全移除、业务层也不再持有该 UserControl 的引用,GC 也无法回收这个 UserControl 的内存,最终随着动态加载 UserControl 的次数变多,内存占用会持续上涨。

正确实现方案

  1. 首先在 Unloaded 事件中添加退订逻辑,注意要做空值判断,避免 UserControl 脱离视觉树后无法获取到 Window 实例的异常:
private void UserControl1_Unloaded(object sender, RoutedEventArgs e)
{
    var window = Window.GetWindow(this);
    if (window != null)
    {
        window.PreviewKeyUp -= UserControl1_PreviewKeyUp;
        window.PreviewKeyDown -= UserControl1_PreviewKeyDown;
    }
}
  1. 可选优化:由于 Loaded、Unloaded 事件可能因为 UserControl 的重复挂载/卸载触发多次,建议增加标志位避免重复订阅:
private bool _isKeyEventSubscribed = false;

private void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
    if (_isKeyEventSubscribed) return;
    var window = Window.GetWindow(this);
    if (window == null) return;
    window.PreviewKeyUp += UserControl1_PreviewKeyUp;
    window.PreviewKeyDown += UserControl1_PreviewKeyDown;
    _isKeyEventSubscribed = true;
}

private void UserControl1_Unloaded(object sender, RoutedEventArgs e)
{
    if (!_isKeyEventSubscribed) return;
    var window = Window.GetWindow(this);
    if (window != null)
    {
        window.PreviewKeyUp -= UserControl1_PreviewKeyUp;
        window.PreviewKeyDown -= UserControl1_PreviewKeyDown;
    }
    _isKeyEventSubscribed = false;
}

内容的提问来源于stack exchange,提问作者Melissa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 02:54:08