15

当我在 WPF 中使用数据绑定时,我的目标控件正在侦听绑定源上的事件。例如,我可能会ListView监听CollectionChanged.ObservableCollection

如果预计事件源的生命周期会超过事件侦听器的生命周期,则存在潜在的内存泄漏,应使用弱事件模式。

WPF 数据绑定是否遵循弱事件模式?如果我的ObservableCollection寿命比我的长ListView,我会ListView被垃圾收集吗?


这就是为什么我怀疑 WPF 控件没有实现弱事件模式的原因。如果他们这样做了,我希望两者都DerivedListView Collected!输出DerivedTextBlock Collected!到控制台。相反,只有DerivedTextBlock Collected!是。

修复代码中的错误后,将收集两个对象。我不知道该怎么想。

Window1.xaml.cs

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace LeakDetector
{
    public class DerivedListView : ListView
    {
        ~DerivedListView()
        {
            Console.WriteLine("DerivedListView Collected!");
        }
    }

    public class DerivedTextBlock : TextBlock
    {
        ~DerivedTextBlock()
        {
            Console.WriteLine("DerivedTextBlock Collected!");
        }
    }

    public partial class Window1 : Window
    {
        // The ListView will bind to this collection and listen for its
        // events. ObColl will hold a reference to the ListView.
        public ObservableCollection<int> ObColl { get; private set; }

        public Window1()
        {
            this.ObColl = new ObservableCollection<int>();
            InitializeComponent();

            // Trigger an event that DerivedListView should be listening for
            this.ObColl.Add(1);

            // Get rid of the DerivedListView
            this.ParentBorder.Child = new DerivedTextBlock();

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

            this.ParentBorder.Child = null;

            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

            Console.WriteLine("Done");
        }
    }
}

Window1.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:LeakDetector"
    x:Class="LeakDetector.Window1"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Height="300" Width="300"
    Title="Leak Detector">
    <Border x:Name="ParentBorder">
        <local:DerivedListView ItemsSource="{Binding Path=ObColl}" />
    </Border>
</Window>
4

2 回答 2

11

本质上,WPF 控件本身与弱事件没有任何关系。相反,有一些与 WPF 的绑定引擎相关的类实现了弱事件模式。PropertyChangedEventManager类实现了 WeakEventManager。如果您使用 Reflector,您会看到几个类在 MS.Internal.Data 命名空间中实现了 IWeakEventListener(特别是一个直接使用 PropertyChangedEventManager 的 MS.Internal.Data.PropertyPathWorker 类)。WPF 在内部使用这些对象来执行数据绑定。

ItemsControls 和 CollectionChanged 事件是另一回事,与 Bindings 无关。看,您可以在后面的代码中执行类似“listView.ItemsSource = myObservableCollection”的操作,并且集合更改通知仍然有效。这里根本不涉及绑定对象。在这里,一组不同的“弱事件相关类”正在发挥作用。 ItemCollectionItemContainerGenerator实现 IWeakEventListener,它们与CollectionChangedEventManager(实现 WeakEventManager)一起工作。

于 2010-09-14T22:12:59.273 回答
2

您链接到的 MSDN 文章的第二句非常清楚地指出 WPF 确实使用了弱事件模式。事实上,甚至可以说 WPF引入了该模式。

编辑:

我希望找到一些明确说明“WPF 控件实现弱事件模式”的文档。– 埃姆杜德利

在做了一些研究之后,我认为这个问题的答案是“否”,我认为答案是“否”的原因是 WPF 不希望 UI 控件是暂时的。虽然有一个CollectionChangedEventManager专门为针对CollectionChanged事件的弱事件构建的类,但支持数据绑定的控件似乎都没有实现IWeakEventListener,这对于对集合使用弱事件是必需的。

我认为模式和用法是为 ViewModel 而不是为 View 而构建的,它比 View 更可能是暂时的。

编辑2:

修复代码中的错误后,将收集两个对象。因此我相信 WPF 控件使用弱事件模式。

有趣的结果。如果他们确实实现了弱事件,他们必须在内部进行。

于 2010-09-14T19:47:16.463 回答