所以我使用带有 MVVM + DataTemplate 方法的 WPF 3.5 在 GUI 上加载 2 个视图。我在内存分析时观察到,作为项目控件的项目容器的一部分生成的项目被固定到内存中,即使在视图被卸载后也不会被 GC!
我刚刚进行了测试,发现即使是最简单的代码也可以重现它......你们可以自己检查。
XAML:
<Window x:Class="ContentControlVMTest.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ContentControlVMTest"
Title="Window2" Height="300" Width="300">
<DockPanel LastChildFill="True">
<CheckBox Click="CheckBox_Click" Content="Test1?"
DockPanel.Dock="Top" Margin="5"/>
<ContentControl x:Name="contentControl">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:Test3}">
<TextBlock Text="{Binding C}" Margin="5"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Test1}">
<DockPanel LastChildFill="True" Margin="5">
<TextBlock Text="{Binding A}"
DockPanel.Dock="Top"
Margin="5"/>
<ListBox ItemsSource="{Binding Bs}"
DisplayMemberPath="B"
Margin="5"/>
</DockPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</Window>
代码背后:
public class Test3
{
public string C { get; set; }
}
public class Test2
{
public string B { get; set; }
}
public class Test1
{
public string A { get; set; }
private List<Test2> _Bs;
public List<Test2> Bs
{
get
{
return _Bs;
}
set
{
_Bs = value;
}
}
}
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
this.KeyDown += Window_KeyDown;
}
private void Window_KeyDown
(object sender, System.Windows.Input.KeyEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl))
if (Keyboard.IsKeyDown(Key.LeftShift))
if (Keyboard.IsKeyDown(Key.LeftAlt))
if (Keyboard.IsKeyDown(Key.G))
{
GC.Collect(2, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(2, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(3, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(3, GCCollectionMode.Forced);
}
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
if (((CheckBox)sender).IsChecked.GetValueOrDefault(false))
{
var x = new Test1() { A = "Test1 A" };
x.Bs = new List<Test2>();
for (int i = 1; i < 10000; i++ )
{
x.Bs.Add(new Test2() { B = "Test1 B " + i });
}
contentControl.Content = x;
}
else
{
contentControl.Content = new Test3() { C = "Test3 C" };
}
}
}
我通过 Left Shift + Alt + Ctrl + G 执行强制 GC。or 视图和 View Model 的所有项目在Test1
正确Test3
卸载后都会失效。所以这正如预期的那样。
但是模型中生成的集合Test1
(具有Test2
对象)仍然固定在内存中。它表明该数组是列表框的项目容器使用的数组,因为它显示了列表框中的去虚拟化项目的数量!当我们在视图模式下最小化或恢复视图时,这个固定数组会改变它的大小Test1
!一次是 16 项,下一次是 69 项。
这意味着 WPF 会固定在项目控件中生成的项目!谁能解释一下?这有什么明显的缺点吗?
多谢。