ListBox 虚拟化不卸载项目
ListBox virtualization not unloading items
我正在列表框中加载大量图像。
在我尝试虚拟化之前有一段合理的加载时间。
现在我使用虚拟化,只加载进入视野的元素,完全按照我的意愿加载; 但是一旦它们落在视图之外似乎就不会卸载所以如果我一直向下滚动到底部所有项目都已加载并且我失去了虚拟化的优势。
在我显示任何图像之前,我的应用程序消耗 140MB,然后我显示 10 张图像,我的内存使用量等于 161MB,如果我滚动到最后一张图像,则使用量为 300MB。如果 10 张图像使用 21 MB,则必须加载所有图像 300MB。
通过使用 Snoop 检查加载的 ListBoxItems 的数量进一步确认了此行为。
代码中没有什么特别之处,我只是确保 CanContentScroll
对 启用 是正确的,而不是禁用虚拟化。我自定义了 ItemContainerStyle
和 ListBox
的模板,但它不应该影响虚拟化(或者至少不只是卸载部分),不是吗?
<ListBox ItemsSource="{Binding myItems}" CanContentScroll="True"/>
我是不是漏掉了什么?
结论
虚拟化显然有效。当每个 ItemContainer 落在视图之外时,它们都会在某个时刻被卸载。
问题可能出在 BitmapImage 的缓存方式上。事实上,我正在使用以下 DataTemplate 来显示图像:
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding}" />
</Image.Source>
</Image>
这里发生的是 BitmapImage
和 Stream
的内存泄漏问题。
问题描述here
解决方案是通过 WrappingStream
所描述和实现的 here
读取图像
像这样读取图像并将其分配给Image.Source
:
BitmapImage bitmap = new BitmapImage();
using( var stream = new FileStream( uri, FileMode.Open ) )
using( WrappingStream wrapper = new WrappingStream( stream ) )
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
}
return bitmap;
我正在列表框中加载大量图像。 在我尝试虚拟化之前有一段合理的加载时间。
现在我使用虚拟化,只加载进入视野的元素,完全按照我的意愿加载; 但是一旦它们落在视图之外似乎就不会卸载所以如果我一直向下滚动到底部所有项目都已加载并且我失去了虚拟化的优势。
在我显示任何图像之前,我的应用程序消耗 140MB,然后我显示 10 张图像,我的内存使用量等于 161MB,如果我滚动到最后一张图像,则使用量为 300MB。如果 10 张图像使用 21 MB,则必须加载所有图像 300MB。
通过使用 Snoop 检查加载的 ListBoxItems 的数量进一步确认了此行为。
代码中没有什么特别之处,我只是确保 CanContentScroll
对 启用 是正确的,而不是禁用虚拟化。我自定义了 ItemContainerStyle
和 ListBox
的模板,但它不应该影响虚拟化(或者至少不只是卸载部分),不是吗?
<ListBox ItemsSource="{Binding myItems}" CanContentScroll="True"/>
我是不是漏掉了什么?
结论
虚拟化显然有效。当每个 ItemContainer 落在视图之外时,它们都会在某个时刻被卸载。
问题可能出在 BitmapImage 的缓存方式上。事实上,我正在使用以下 DataTemplate 来显示图像:
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding}" />
</Image.Source>
</Image>
这里发生的是 BitmapImage
和 Stream
的内存泄漏问题。
问题描述here
解决方案是通过 WrappingStream
所描述和实现的 here
像这样读取图像并将其分配给Image.Source
:
BitmapImage bitmap = new BitmapImage();
using( var stream = new FileStream( uri, FileMode.Open ) )
using( WrappingStream wrapper = new WrappingStream( stream ) )
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
}
return bitmap;