【问题标题】:ItemsControl keeps reference to previous DataContext after DataContext set to null在 DataContext 设置为 null 后,ItemsControl 保持对先前 DataContext 的引用
【发布时间】:2016-07-08 13:31:46
【问题描述】:

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

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

由于错误地假设我的一个ItemsControlsDataGrid)会在TheGrid.DataContext = null 之后释放其对旧数据上下文的所有引用,因此我的应用程序发生了内存泄漏。为什么不呢?这是预期的行为吗?

更新:点击“Set DataContext to null”后VS2015诊断工具提供的root路径如下所示。在Foo 本身来自我的代码之后,我什么都没想:

  • Foo
  • ListDictionary+DictionaryNode
  • ListDictionary
  • ListDictionary+DictionaryNode
  • ListDictionary
  • HybridDictionary
  • Object[][强力手柄]

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

【问题讨论】:

  • 在绑定null 之后尝试刷新null 吗? TheGrid.Items.Refresh();
  • @SeM 结果还是一样——来自旧 DC 的终结器在我分配非空值之前不会运行。
  • 我不确定你在做什么,但是使用上面的代码,只要我单击设置为 null,我的 DataGrid 就会清除。这不会发生在你身上吗?
  • @d.moncada 是的,网格已清除,但它仍然在某处保留旧 DataContext 中对 IEnumerable&lt;Foo&gt; 的引用,这意味着 Foos 的整个列表已泄露(有在这个玩具示例中只有一个)。
  • "如果绑定到一个没有实现 INotifyCollectionChanged 接口的集合,WPF 会创建一个对该集合的强引用。因此,它会留在内存中"jetbrains.com/help/dotmemory/10.0/Inspections.html

标签: c# .net wpf memory-leaks itemscontrol


【解决方案1】:

你可以在这里得到解释 https://stackoverflow.com/a/19511796/2696230

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

【讨论】:

  • 我根据 cmets 得出了这个结论,但是没有人将他们的评论变成答案,所以你得到了积分! :)
猜你喜欢
  • 1970-01-01
  • 2016-09-24
  • 2011-09-03
  • 1970-01-01
  • 2012-01-19
  • 2018-03-12
  • 2013-12-26
  • 2014-09-21
  • 1970-01-01
相关资源
最近更新 更多