WPF';实例';在幕后生存

本文关键字:后生 实例 WPF | 更新日期: 2023-09-27 18:22:10

我有一个WPF Window子类。该窗口包含一个ItemsControl,它的ItemsSource在window子类ctor中初始化。该窗口使用ShowDialog()显示,每次都显示在一个新实例上。我看到的是,即使窗口和ItemsControl每次显示时都是新实例,分配给ItemsSource的IEnumerable也会以某种方式被记住(看起来),因此不会在每次显示窗口时调用GetEnumerator(),因此更改不会反映出来。但这种情况并不总是发生,有时会实际调用GetEnumerator()。如果每次为ItemsSource分配一个新的IEnumerable,则始终调用GetEnumerator。

是否有某种奇怪的缓存在幕后进行,或者可能是ItemsControl的底层实例(经常)被重用(可能受到垃圾收集的影响)?

XAML:

<Window x:Class="BalticWpfControlLib.SystemInfoWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BalticWpfControlLib"
        Title="System Information" Height="400" Width="300"  WindowStartupLocation="CenterOwner" >
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
        <ItemsControl Name="MaintenanceInfos">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderThickness="0,0,0,1" Margin="4,4,4,0" BorderBrush="Gray">
                        <local:MaintenanceInfoUserControl Margin="0,0,0,4"/>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Window>

代码背后:

public partial class SystemInfoWindow : Window
{
    public SystemInfoWindow(IEnumerable<IMaintenanceInfo> maintenanceInfos)
    {
        InitializeComponent();
        System.Diagnostics.Debug.WriteLine("ItemsControl instance={0}", System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(MaintenanceInfos));
        System.Diagnostics.Debug.Assert(null == MaintenanceInfos.ItemsSource);
        MaintenanceInfos.ItemsSource = maintenanceInfos;
    }
}

显示对话框的代码:

private void ShowSysInfo_Click(Object sender, RoutedEventArgs args)
{
    var dlg = new SystemInfoWindow(facade.MaintenanceInfos);
    dlg.Owner = Window.GetWindow(this);
    dlg.ShowDialog();
}

facade.MaintenanceInfos是一个始终返回相同IEnumerable<IMaintenanceInfo>实例的属性。

WPF';实例';在幕后生存

进行时

MaintenanceInfos.ItemsSource = maintenanceInfos;

在科学背后,WPF确实在做一些类似的事情

MaintenanceInfos.ItemsSource = CollectionViewSource.GetDefaultView(maintenanceInfos)

该函数为IEnumerable中传递的相同内容返回相同的ColectionView,并且ColectionView保存诸如当前所选项目的信息。我不能100%确定,但我认为该视图也缓存了底层IEnumerable,并且只有在通过事件通知视图集合已更改时才重新查询它。

这就是神秘缓存的可能来源,同一个ICollectionView实例是从单独窗口中的两个GetDefaultView调用返回的,并且它不会重新查询IEnumerable,因为它从未接收到来自传入对象的集合更改事件。