C# 当我的列表框中有图像时,为什么会出现OutOfMemoryException?
我想在自定义图库中显示Windows Phone 8 photo文件夹中存储的所有图像,该图库使用C# 当我的列表框中有图像时,为什么会出现OutOfMemoryException?,c#,listbox,out-of-memory,windows-phone-8,photo-gallery,C#,Listbox,Out Of Memory,Windows Phone 8,Photo Gallery,我想在自定义图库中显示Windows Phone 8 photo文件夹中存储的所有图像,该图库使用列表框显示图像 列表框代码如下: <phone:PhoneApplicationPage.Resources> <MyApp:PreviewPictureConverter x:Key="PreviewPictureConverter" /> </phone:PhoneApplicationPage.Resources>
列表框
显示图像
列表框
代码如下:
<phone:PhoneApplicationPage.Resources>
<MyApp:PreviewPictureConverter x:Key="PreviewPictureConverter" />
</phone:PhoneApplicationPage.Resources>
<ListBox Name="previewImageListbox" VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel CleanUpVirtualizedItemEvent="VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1">
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding Converter={StaticResource PreviewPictureConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
图像存储在自定义类中:
class PreviewImageItem
{
public Picture _picture = null;
public BitmapImage _bitmap = null;
public PreviewImageItem(Picture pic)
{
_picture = pic;
}
public BitmapImage ImageData
{
get
{
System.Diagnostics.Debug.WriteLine("Get picture " + _picture.ToString());
_bitmap = new BitmapImage();
Stream data = _picture.GetImage();
try
{
_bitmap.SetSource(data); // Out-of memory exception (see text)
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception : " + ex.ToString());
}
finally
{
data.Close();
data.Dispose();
data = null;
}
return _bitmap;
}
}
}
以下代码用于设置列表框
数据源:
private List<PreviewImageItem> _galleryImages = new List<PreviewImageItem>();
using (MediaLibrary library = new MediaLibrary())
{
PictureCollection galleryPics = library.Pictures;
foreach (Picture pic in galleryPics)
{
_galleryImages.Add(new PreviewImageItem(pic));
}
previewImageListbox.ItemsSource = _galleryImages;
};
所有这些都可以正常工作,但代码在几张图像之后(尤其是快速滚动时)会崩溃,并出现OutOfMemoryException
。滚动列表框
时,会定期调用方法VirtualizangStackPanel\u CleanUpVirtualizedItemEvent\u 1
(例如,每2或3个列表框条目一次)
这个示例代码有什么问题
为什么内存没有被释放(足够快)?您刚刚使用Windows Phone在屏幕上显示用户媒体库“图片”文件夹中的所有图片。这是难以置信的内存密集型,考虑到WP8应用程序的150MB限制,难怪会出现OOM异常 <> P> >应考虑的几个事项: 1) 将listboxitem滚动出视图时,将Source和SourceUri属性设置为null。请参见Stefan文章中的“缓存图像”@ 2) 如果您在WP8上,请确保设置和/或解码像素高度。这样,图像将被加载到内存中,并永久调整大小,并且只有调整大小的副本存储在内存中。加载到内存中的图像可能比手机本身的屏幕大得多。因此,将这些图像裁剪到合适的大小并仅存储大小调整后的图像至关重要。设置BitmapImage.DecodePixelWidth=480(最大值)以帮助解决此问题
var bmp = new BitmapImage();
// no matter the actual size,
// this bitmap is decoded to 480 pixels width (aspect ratio preserved)
// and only takes up the memory needed for this size
bmp.DecodePixelWidth = 480;
bmp.UriSource = new Uri(@"Assets\Demo.png", UriKind.Relative);
ImageControl.Source = bmp;
(代码示例来自)
3) 为什么要使用Picture.GetImage()而不是()?你真的需要图像占据整个屏幕吗
4)考虑从ListBox移动到LongListSelector,如果这是一个WP8独占应用程序。LLS的虚拟化比ListBox好得多。查看您的代码示例,只需将XAML ListBox元素更改为LongListSelector元素就足够了
哦,我最近花了整整一天的时间才让这一切顺利进行 因此,解决方案是: 使您的图像控件成为免费资源。因此,设定BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
如前所述
确保在列表的每个项目上虚拟化_位图。您应该按需加载它(LongListSelector.Realized方法),您必须销毁它!它不会自动收集,GC.collect也不会工作。
空引用也不起作用:(
但方法如下:
创建1x1像素文件。将其复制到程序集中,并从中生成资源流,以处置1x1像素为空的图像。将自定义处置方法绑定到LongListSelector.UnRealized事件(例如,容器处理列表项)
在LongListSelector中为我工作,有1000张图像,每个图像的宽度为400
如果您错过了数据收集的2个步骤,您可以看到良好的结果,但在滚动100-200个项目后内存溢出。尝试以下方法:此处的示例项目:什么是
图片
以及GetImage()的作用是什么
方法如何?您只将\u bitmap
字段设置为null
,但\u picture
字段被单独保留,是否是该对象保存了一些数据?此外,公开字段也不是一个好的做法。在PreviewImageItem
中实现IDisposable
,并调用Dispose()
在您的VirtualzingStackPanel\u CleanUpVirtualizedItemEvent\u 1
方法中…在清理中,您应该将\u picture
属性以及图片的类型设置为“Microsoft.Xna.Framework.Media.picture”并且不需要太多内存。大多数内存由BitMapImage使用,BitMapImage是从图片对象提供的流创建的。您的代码中可能有一个明显的错误,我看不到,但您也应该检查这个问题:嗨,Hydrix,您编写的代码对我非常有用。我只想知道您在哪里使用了这个dispose方法,因为您使用的是listbox(因为没有未实现的事件)。您传递的参数是什么。是listbox容器吗?请告诉我,使用虚拟化堆栈面板滚动性能很慢,我如何使其快速。请帮助我。限制解码分辨率的提示非常好(我完全忘记了这一点,尽管这很明显)。缩略图流的质量太低了。还有一件事也很重要,那就是调用System.GC.Collect()在快速滚动的情况下为空后。我有点困惑,我的ListView通过数据绑定获取其数据,因此我对滚动没有任何直接影响。使用技术1.我可以卸载图像,但如果用户向后滚动,图像现在是黑色的,不会被框架再次加载…知道吗?Tim,如果你在WP8上,你知道吗应该将LongListSelector用于ItemAcrealized和ItemUnrealized事件。ListBox的糟糕程度令人难以置信。只需“升级”到LLS,我就解决了我无法解决的各种内存泄漏问题。谢谢!我再次遇到内存问题。唯一解决问题的方法是使用“DisposeImage”方法!很高兴它能帮助你。我认为这是WP8平台中的一个bug。我面临着同样的问题。谢谢你的解决方案。@gleb.kudr你能在这里帮助我吗@Goofy你尝试过我的方法吗?在使用它之后释放每个容器资源吗?
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
var bmp = new BitmapImage();
// no matter the actual size,
// this bitmap is decoded to 480 pixels width (aspect ratio preserved)
// and only takes up the memory needed for this size
bmp.DecodePixelWidth = 480;
bmp.UriSource = new Uri(@"Assets\Demo.png", UriKind.Relative);
ImageControl.Source = bmp;
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
public static void DisposeImage(BitmapImage image)
{
Uri uri= new Uri("oneXone.png", UriKind.Relative);
StreamResourceInfo sr=Application.GetResourceStream(uri);
try
{
using (Stream stream=sr.Stream)
{
image.DecodePixelWidth=1; //This is essential!
image.SetSource(stream);
}
}
catch { }
}