views:

61

answers:

2

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  A: 

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.

Randolpho
I was hoping to find some documentation which explicitly states "WPF controls implement the weak event pattern."
emddudley
@emddudley - quoting from the page you link to "(WPF) introduces a design pattern that can be used to address this issue, ... This design pattern is known as the weak event pattern." How much more documentation do you need?
ChrisF
Yes... but did Microsoft use it when making their controls? See the example code I've put in the question.
emddudley
@emdddudley: I've edited my answer to reply to your comment
Randolpho
+2  A: 

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).

karmicpuppet
Thank you, I appreciate the explanation of the underlying implementation.
emddudley