正确清理WPF用户控件

时间:2009-10-11 08:56:55

标签: wpf user-controls dispose finalizer

我对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?如果可能的话,我更愿意避免使用终结器。

5 个答案:

答案 0 :(得分:12)

我担心Dispatcher.ShutdownStarted似乎确实是WPF在UserControls中处理资源所提供的唯一机制。 (看到我前一段时间的问题similar question)。

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

我很惊讶你没有得到Dispatcher.ShutdownStarted事件。您的UserControl当时是否附加到顶级窗口?

答案 1 :(得分:10)

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

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

答案 2 :(得分:6)

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

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

        base.OnVisualParentChanged(oldParent);
    }

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

所以我已经覆盖了OnClosing:

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

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

示例使用OnClosing处置资源(link