问题
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