ItemsControl 在 DataContext 设置为 null 后保留对先前 DataContext 的引用

ItemsControl keeps reference to previous DataContext after DataContext set to null

更新:澄清一下,这是一个关于内存泄漏的问题,而不是关于 UI 无法刷新以反映新的 null DataContext(那部分工作正常)。

看来,如果您将 ItemsControlDataContext 设置为 null,它将继续保留对其旧数据上下文的引用,直到您为其分配一个新数据上下文不为空。您可以使用问题底部的代码确认这一点。单击 "Set DataContext to null",然后根据需要多次单击 "Collect garbage"。 "Foo finalized" 消息永远不会出现。然后点击"Set DataContext to empty object",再次收集垃圾,马上就能看到终结器运行。

我的应用程序发生内存泄漏,原因是我的一个 ItemsControls(一个 DataGrid)会在 TheGrid.DataContext = null 之后释放对旧数据上下文的所有引用.为什么不呢?这是预期的行为吗?

更新:点击"Set DataContext to null"后VS2015诊断工具提供的root路径如下所示。在 Foo 本身来自我的代码之后,我不认为任何事情:

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();
        }
    }
}

你可以在这里得到解释

使用列表会导致强引用(内存泄漏)。简单的解决方案就是将 List 更改为 ObservableCollection,除非视图(本身)被释放