当我的ListBox中有图像时,为什么会出现OutOfMemoryException?

我想显示存储在Windows Phone 8照片文件夹中的所有图像在我的自定义画廊,它使用ListBox来显示图像。

ListBox代码如下所示:

  <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> 

用下面的转换器:

 public class PreviewPictureConverter : System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { PreviewImageItem c = value as PreviewImageItem; if (c == null) return null; return c.ImageData; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } 

图像存储在自定义类中:

 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; } } } 

以下代码用于设置ListBox数据源:

 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; }; 

最后这里是“清理”代码:

 private void VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1(object sender, CleanUpVirtualizedItemEventArgs e) { PreviewImageItem item = e.Value as PreviewImageItem; if (item != null) { System.Diagnostics.Debug.WriteLine("Cleanup"); item._bitmap = null; } } 

所有这些工作正常,但代码崩溃后几个图像OutOfMemoryException (特别是快速滚动时)。 ListBox滚动时,将调用VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1方法regulary(例如每2个或3个列表框项)。

这个示例代码有什么问题?

为什么内存不够(足够快)?

哦,我最近杀了一整天,使这个工作!

所以解决办法是:

使您的图像控制免费资源。 所以设置

 BitmapImage bitmapImage = image.Source as BitmapImage; bitmapImage.UriSource = null; image.Source = null; 

就像之前提到的那样。

确保你在列表的每一项上虚拟化_bitmap。 你应该加载它(LongListSelector.Realized方法),你必须销毁它! 它不会自动收集,GC.Collect也不会工作。 空引用不工作:(但是,这里是方法:使1×1像素文件。将其复制到程序集,并从它的资源stream以1×1像素空白处理您的图像。将自定义处置方法绑定到LongListSelector.UnRealized事件(e。容器处理你的列表项目)。

 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 { } } 

在LongListSelector中为我工作,每幅图像宽度为1000,图像宽度为400。

如果你错过了数据收集的第2步,你可以看到好的结果,但是滚动100-200个项目后内存溢出。

您只需将Windows Phone中的所有图片显示在屏幕上的用户媒体库“图片”文件夹中即可。 这是令人难以置信的内存密集型,考虑到WP8应用程序的150MB的限制,这也难怪你会得到OOMexception。

有几件事你应该考虑增加:

1)将ListBoxitem滚动到视图外时,将Source和SourceUri属性设置为null。 请参阅Stefan的文章“caching图片”@ http://blogs.msdn.com/b/swick/archive/2011/04/07/image-tips-for-windows-phone-7.aspx

  BitmapImage bitmapImage = image.Source as BitmapImage; bitmapImage.UriSource = null; image.Source = null; 

2)如果你在WP8上,请确保设置DecodePixelWidth和/或DecodePixelHeight。 这样一个图像将被加载到内存中,永久resize,只有resize的副本存储在内存中。 加载到内存中的图像可能比手机本身的屏幕大得多。 所以裁剪到合适的大小,只存储大小的图像是至关重要的。 设置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()而不是Picture.GetThumbnail ()? 你真的需要这个形象来占据整个屏幕?

4)考虑从ListBox移动到LongListSelector,如果这是一个WP8独家应用程序。 LLS比ListBox有更好的虚拟化。 看看你的代码示例,只需要将XAML ListBox元素更改为LongListSelector元素即可。

尝试这种方法: 图像下载与自动内存清理 。 示例项目在这里: https : //simca.codeplex.com/