【问题标题】:Proper cleanup of WPF user controls正确清理 WPF 用户控件
【发布时间】:2010-12-05 17:34:49
【问题描述】:

我对 WPF 比较陌生,它的一些东西对我来说很陌生。一方面,与 Windows 窗体不同,WPF 控件层次结构不支持 IDisposable。在 Windows 窗体中,如果用户控件使用任何托管资源,则通过重写每个控件实现的 Dispose 方法来清理资源非常容易。

在 WPF 中,事情并没有那么简单。找了好几个小时,遇到了两个基本的主题:

第一个主题是 Microsoft 明确指出 WPF 不实现 IDisposable,因为 WPF 控件没有非托管资源。虽然这可能是真的,但他们似乎完全忽略了这样一个事实,即用户对其 WPF 类层次结构的扩展可能确实使用托管资源(直接或间接通过模型)。通过不实现 IDisposable,Microsoft 有效地删除了唯一有保证的机​​制,通过该机制可以清除自定义 WPF 控件或窗口使用的非托管资源。

其次,我发现了一些对 Dispatcher.ShutdownStarted 的引用。我曾尝试使用 ShutdownStarted 事件,但它似乎并没有为每个控件触发。我有一堆 WPF UserControl,我已经为 ShutdownStarted 实现了一个处理程序,并且它永远不会被调用。我不确定它是否仅适用于 Windows,或者 WPF App 类。但是它没有正确触发,每次应用程序关闭时我都会泄漏打开的 PerformanceCounter 对象。

有没有比 Dispatcher.ShutdownStarted 事件更好的方法来清理非托管资源?实现 IDisposable 是否有一些技巧可以调用 Dispose ?如果可能的话,我更愿意避免使用终结器。

【问题讨论】:

    标签: wpf user-controls dispose finalizer


    【解决方案1】:

    当我使用一些 IDbConnection 实现驱动程序或实体框架连接到数据库时,我遇到了这个困难。

    我发现在这些情况下,建议是为每个窗口保留一个对象连接/上下文,以便能够跟踪更改/事务。 (link)

    所以我已经覆盖了 OnClosing:

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
          base.OnClosing(e);
          this._context.Dispose();
    }
    

    上下文可以是我的 ViewModel/Control,它实现了 IDisposable 来清理资源。

    使用 OnClosing 处理资源的示例 (link)

    【讨论】:

      【解决方案2】:

      IDisposable 接口在 WPF 下(几乎)没有意义,因为其机制与 Winforms 不同。在 WPF 中,您必须牢记视觉和逻辑树:这是基本的。
      因此,任何视觉对象通常都作为其他对象的子对象存在。 WPF 构建机制的基础是分层附加可视对象,然后在它们无用时分离和销毁。

      我认为您可以检查自UIElement 以来暴露的OnVisualParentChanged 方法:此方法在附加视觉对象和分离时调用。这可能是处理非托管对象(套接字、文件等)的正确位置。

      【讨论】:

      • 感谢您提供有关 OnVisualParentChanged 的​​提示。我会试试看它是否有助于解决我的问题。
      【解决方案3】:

      我也在寻找这个,在测试了不同的选项后,我实现了 venezia 的解决方案

      protected override void OnVisualParentChanged(DependencyObject oldParent)
          {
              if (oldParent != null)
              {
                  MyOwnDisposeMethod(); //Release all resources here
              }
      
              base.OnVisualParentChanged(oldParent);
          }
      

      我意识到当父调用Children.Clear() 方法并且已经将项目添加到子项时,DependencyObject 有一个值。但是当父级添加一个项目(Children.Add(CustomControl))并且子级为空时,DependencyObject 为空。

      【讨论】:

      • 我把它改成了 if (Parent == null) 这样如果我把控件移到另一个容器中,它就不会自毁了
      【解决方案4】:

      虽然其他人已经为您提供了有关此问题的非常有用的信息,但您可能没有一些信息可以解释为什么没有 IDisposable。基本上,WPF(和 Silverlight)大量使用 Wea​​kReferences - 这允许您引用 GC 仍然可以收集的对象。

      【讨论】:

      • 感谢皮特的洞察力。我很好奇您是否有一些链接可以更详细地解释这一点?我很好奇 WeakReferences 的大量使用不会引起问题。它们可以成为小众情况下的强大工具……但我无法想象它们在 WPF 中是如何使用的。
      【解决方案5】:

      恐怕 Dispatcher.ShutdownStarted 似乎确实是 WPF 提供的用于在 UserControls 中处理资源的唯一机制。 (见一个很similar question我刚才问的)。

      解决此问题的另一种方法是将所有可支配资源(如果可能的话)从背后的代码中移出并放入单独的类中(例如使用 MVVM 模式时的 ViewModel)。然后在更高级别上,您可以处理主窗口关闭并通过 Messenger 类通知所有 ViewModel。

      我很惊讶您没有收到 Dispatcher.ShutdownStarted 事件。您的用户控件当时是否附加到顶级窗口?

      【讨论】:

      • +1 用于将一次性资源移出代码隐藏。 WPF 的关键学习点之一是最小化代码隐藏以利用数据绑定架构的强度和表现力。学习是一件痛苦的事情(学习曲线更像是在攀登悬崖),但是当你“获得” WPF 的思维模式时会有所收获。
      • 所有一次性资源实际上都在 ViewModel 中,它们本身就是 IDisposable。我真的很困惑为什么 Dispatcher.ShutdownStarted 事件没有触发。性能计数器控件(及其关联的 ViewModel)确实附加到 WPF 图,因为它嵌入在 中的 中。
      • @Greg D:我通常会得到 WPF 模型。掌握了 WPF 的基础知识后,我就开始使用 MVVM,而且我的 CodeBehind 非常简单(只是默认构造函数及其对 InitializeComponent 的调用)。 WPF 的可组合性和数据绑定功能令人惊叹,如果可以选择,我将永远不会回到 Windows 窗体。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-04
      • 2011-12-20
      • 2013-07-22
      • 1970-01-01
      • 2012-06-27
      相关资源
      最近更新 更多