Reputation: 9383
Update: To clarify, this is a question about memory leaks, not about the UI failing to refresh to reflect the new null DataContext (that part works fine).
It appears that if you set an ItemsControl
's DataContext
to null, it will continue to hold a reference to its old datacontext until such time as you assign it a new one that is not null. You can confirm this with the code at the bottom of the question. Click "Set DataContext to null", then click "Collect garbage" as many times as you want. The "Foo finalized" message will never appear. Then click "Set DataContext to empty object", collect garbage again, and you'll see the finalizer run right away.
I had a memory leak in my application due to a faulty assumption that one of my ItemsControls
(a DataGrid
) would release all its references to its old datacontext after TheGrid.DataContext = null
. Why doesn't it? Is this expected behavior?
Update: The path to root provided by the VS2015 Diagnostic Tools after clicking "Set DataContext to null" looks like this. I don't think anything after the Foo
itself comes from my code:
Foo
ListDictionary+DictionaryNode
ListDictionary
ListDictionary+DictionaryNode
ListDictionary
HybridDictionary
Object[]
[Strong Handle]XAML:
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ItemsControl ItemsSource="{Binding Foos}" />
<Button Content="Set DataContext to null" Click="SetDataContextToNullClicked"/>
<Button Content="Set DataContext to empty object" Click="SetDataContextToEmptyObjectClicked" />
<Button Content="Collect garbage" Click="CollectGarbageClicked"/>
</StackPanel>
</Window>
C#:
namespace WpfApplication
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
public class Foo
{
~Foo()
{
Debug.Print("Foo finalized");
}
}
public class FoosViewModel // implementing INotifyPropertyChanged here doesn't help
{
public FoosViewModel(IEnumerable<Foo> foos)
{
Foos = foos;
}
public IEnumerable<Foo> Foos { get; }
}
public partial class MainWindow
{
public MainWindow()
{
DataContext = new FoosViewModel(Enumerable.Repeat(new Foo(), 1));
InitializeComponent();
}
private void SetDataContextToNullClicked(object sender, RoutedEventArgs e)
{
DataContext = null;
}
private void SetDataContextToEmptyObjectClicked(object sender, RoutedEventArgs e)
{
DataContext = new FoosViewModel(Enumerable.Empty<Foo>());
}
private void CollectGarbageClicked(object sender, RoutedEventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
Upvotes: 0
Views: 875
Reputation: 1763
you can get explanation in here https://stackoverflow.com/a/19511796/2696230
Using list will result in a strong reference ( a memory leak ). the simple solution is just to change List
to ObservableCollection
, unless the view (itself) is released
Upvotes: 1