正确清理 WPF 用户控件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1550212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 21:04:55  来源:igfitidea点击:

Proper cleanup of WPF user controls

wpfuser-controlsdisposefinalizer

提问by jrista

I am relatively new to WPF, and some things with it are quite foreign to me. For one, unlike Windows Forms, the WPF control hierarchy does not support IDisposable. In Windows Forms, if a user control used any managed resources, it was very easy to clean up the resources by overriding the Dispose method that every control implemented.

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

In WPF, the story is not that simple. I have searched for this for several hours, and encountered two basic themes:

在 WPF 中,故事并没有那么简单。我已经搜索了几个小时,遇到了两个基本主题:

The first theme is Microsoft clearly stating that WPF does not implement IDisposable because the WPF controls have no unmanaged resources. While that may be true, they seem to have completely missed the fact that user extensions to their WPF class hierarchy may indeed use managed resources (directly or indirectly through a model). By not implementing IDisposable, Microsoft has effectively removed the only guaranteed mechanism by which unmanaged resources used by a custom WPF control or window can be cleaned up.

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

Second, I found a few references to Dispatcher.ShutdownStarted. I have tried to use the ShutdownStarted event, but it does not seem to fire for every control. I have a bunch of WPF UserControl's that I have implemented a handler for ShutdownStarted, and it never gets called. I am not sure if it only works for Windows, or perhaps the WPF App class. However it is not properly firing, and I am leaking open PerformanceCounter objects every time the app closes.

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

Is there a better alternative to cleaning up unmanaged resources than the Dispatcher.ShutdownStarted event? Is there some trick to implementing IDisposable such that Dispose will be called? I would much prefer to avoidusing a finalizer if at all possible.

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

采纳答案by Mark Heath

I'm afraid that Dispatcher.ShutdownStarted really does seem to be the only mechanism WPF provides for disposing of resources in UserControls. (See a very similar questionI asked a while ago).

恐怕 Dispatcher.ShutdownStarted 似乎确实是 WPF 提供的用于在 UserControls 中处理资源的唯一机制。(请参阅我不久前问的一个非常相似的问题)。

Another way to approach the problem is to move all of your disposable resources (if at all possible) out of the code behind and into separate classes (such as the ViewModel when using the MVVM pattern). Then at a higher level you could handle your main window closing and notify all the ViewModels via a Messenger class.

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

I am surprised you don't get the Dispatcher.ShutdownStarted event. Are your UserControls attached to the top-level window at the time?

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

回答by venezia

The IDisposable interface has (almost) no meaning under WPF, because the mechanism is different from Winforms. In WPF, you must bear in mind the visual and logical tree: that's fundamental.
So, any visual object generally lives as child of some other object. The base of the WPF building mechanism is to attach the visual object hierarchically, then detach and destroy when they aren't useful.

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

I think you may check the OnVisualParentChangedmethod exposed since the UIElement: this method is called either when a visual object is attached and when is detached. That could be the right place to dispose the unmanaged objects (sockets, files, etc).

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

回答by Jaime Marín

I was looking for this too and after testing differents options I implemented the solution of venezia

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

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

        base.OnVisualParentChanged(oldParent);
    }

I realized that when parent call Children.Clear()Method and had already items added to Children, DependencyObject had a value. But when parent added an item (Children.Add(CustomControl)) and children was empty DependencyObject was null.

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

回答by Pete OHanlon

While others have given you really useful information about this problem, there is a little bit of information that you may not have that will explain a lot about why there is no IDisposable. Basically, WPF (and Silverlight) makes heavy use of WeakReferences - this allows you to reference an object which the GC can still collect.

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