Do WPF controls use weak events in their bindings?

一曲冷凌霜 提交于 2019-12-04 09:09:56

问题


When I use databinding in WPF, my target controls are listening for events on the binding source. For example, I may have a ListView listening for CollectionChanged events on a ObservableCollection.

If the lifetime of an event source is expected to exceed the lifetime of an event listener, there is a potential memory leak, and the weak event pattern should be used.

Does WPF databinding follow the weak event pattern? If my ObservableCollection lives longer than my ListView, will my ListView be garbage collected?


Here is why I suspect that WPF controls do not implement the weak event pattern. If they did, I would expect both DerivedListView Collected! and DerivedTextBlock Collected! to be output to the console. Instead, only DerivedTextBlock Collected! is.

After fixing a bug in the code, both objects are collected. I'm not sure what to think.

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>

回答1:


In essence, the WPF controls themselves do not have anything to do with weak events. Instead, there are certain classes related to WPF's Binding engine that implement the weak event pattern. The class PropertyChangedEventManager implements WeakEventManager. And if you use Reflector, you'll see that several classes implement IWeakEventListener in the MS.Internal.Data namespace (one in particular is the MS.Internal.Data.PropertyPathWorker class which directly uses the PropertyChangedEventManager). These objects are used by WPF internally to do Data Binding.

ItemsControls and CollectionChanged events are a different story and has nothing to do with Bindings. See, you could do something like "listView.ItemsSource = myObservableCollection" in the code behind and the collection-changed notification will still work. No Binding objects are involved here at all. Here, a different set of "weak-event-related classes" are in play. ItemCollection and ItemContainerGenerator implement IWeakEventListener, and they work in conjunction with the CollectionChangedEventManager(which implements WeakEventManager).




回答2:


The second sentence of the MSDN article to which you linked pretty clearly states that WPF does use the Weak Event Pattern. In fact, it goes so far as to say that WPF introduced the pattern.

Edit:

I was hoping to find some documentation which explicitly states "WPF controls implement the weak event pattern." – emddudley

After doing some research, I think the answer to that question is "no", and I think the reason the answer is "no" is that WPF doesn't expect UI controls to be transitory. While there is a CollectionChangedEventManager class built specifically for weak events against the CollectionChanged event, none of the controls that support databinding appear to implement IWeakEventListener, which would be necessary to use weak events against the collection.

I think the pattern and usage are built for a ViewModel rather than a View, which is more likely to be transitory than a View.

Edit2:

After fixing a bug in the code, both objects are collected. Therefore I believe that WPF controls use the weak event pattern.

Interesting result. If they do implement Weak Events, they must do it internally.



来源:https://stackoverflow.com/questions/3712320/do-wpf-controls-use-weak-events-in-their-bindings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!