SharedResourceDictionary
。我制作了一个小例子来演示共享资源字典的问题。
只需创建一个简单的WPF应用程序。添加一个资源Xaml文件。 Shared.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="myBrush" Color="Yellow"/>
</ResourceDictionary>
现在添加一个用户控件。代码后台只是默认的,所以我只展示XAML。
MyUserControl.xaml
<UserControl x:Class="Leak.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SharedResourceDictionary="clr-namespace:Articy.SharedResourceDictionary" Height="128" Width="128">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Leak;component/Shared.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Rectangle Fill="{StaticResource myBrush}"/>
</Grid>
</UserControl>
窗口的后台代码大致如下
Window1.xaml.cs
// [ ... ]
public Window1()
{
InitializeComponent();
myTabs.ItemsSource = mItems;
}
private ObservableCollection<string> mItems = new ObservableCollection<string>();
private void OnAdd(object aSender, RoutedEventArgs aE)
{
mItems.Add("Test");
}
private void OnRemove(object aSender, RoutedEventArgs aE)
{
mItems.RemoveAt(mItems.Count - 1);
}
并且窗口的XAML代码如下:
Window1.xaml
<Window.Resources>
<DataTemplate x:Key="myTemplate" DataType="{x:Type System:String}">
<Leak:MyUserControl/>
</DataTemplate>
</Window.Resources>
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="Add" Click="OnAdd"/>
<Button Content="Remove" Click="OnRemove"/>
</StackPanel>
<TabControl x:Name="myTabs" ContentTemplate="{StaticResource myTemplate}">
</TabControl>
</DockPanel>
</Grid>
</Window>
我知道这个程序并不完美,而且可能可以更简单,但是在找到展示问题的方法时,这就是我想出来的。总之:
开始运行程序并检查内存使用情况,如果你有一个内存分析器,这将变得更加容易。添加(通过点击选项卡显示)和删除页面,你会发现一切都很正常,没有泄漏。
现在,在 UserControl.Resources
部分中使用 SharedResourceDictionary
代替 ResourceDictionary
来包含 Shared.xaml。你会发现,在删除页面后,MyUserControl
仍然保存在内存中,以及其中的 MyUserControl
。
我想这对所有通过XAML实例化的东西都会发生,比如转换器、用户控件等等。奇怪的是,这对自定义控件不会发生。我猜这是因为自定义控件上没有真正实例化任何东西,数据模板等等。
那么首先我们该如何避免这种情况呢?在我们的情况下,使用SharedResourceDictionary是必须的,但是内存泄漏使得它无法在生产中使用。 可以通过使用CustomControls而不是UserControls来避免泄漏,但这并不总是实际可行的。那么为什么UserControls被ResourceDictionary强引用呢? 我想知道为什么之前没有人经历过这种情况,就像我在早些时候的一个问题中说的那样,似乎我们完全错误地使用了资源字典和XAML,否则我想知道为什么它们如此低效。
我希望有人能够对这个问题有所启发。
提前感谢, Nico