Почему я получаю исключение OutOfMemoryException, когда у меня есть изображения в моем ListBox?

Я хочу отображать все изображения, хранящиеся в папке с папкой Windows Phone 8 в моей собственной галерее, которая использует ListBox для отображения изображений.

Код 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е:

 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 _galleryImages = new List(); 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 . Метод VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1 называется регулярным (например, каждые 2 или 3 записи списка) при прокрутке списка ListBox .

Что случилось с этим примером кода?

Почему память не освобождается (достаточно быстро)?

О, я недавно убил целый день, чтобы сделать это!

Таким образом, решение:

Сделайте свои средства управления свободными изображениями. Поэтому установите

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

как уже упоминалось ранее.

Убедитесь, что вы виртуализировали _bitmap для каждого элемента списка. Вы должны загрузить его по требованию (метод LongListSelector.Realized), и вам нужно его уничтожить! Он не собирается собираться автоматически, и GC.Collect тоже не работает. Нулевая ссылка тоже не работает 🙁 Но вот способ: Сделайте 1×1-пиксельный файл. Скопируйте его в сборку и сделайте из него stream ресурсов, чтобы избавиться от изображений с размером пикселя 1×1. Свяжите настраиваемый метод размещения с событием LongListSelector.UnRealized (например, Контейнер обрабатывает ваш элемент списка).

 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, чтобы показать все изображения в папке «картинки» в медиа-библиотеке пользователя на экране. Это невероятно интенсивное использование памяти и учитывая ограничение на 150 МБ для приложений WP8, неудивительно, что вы получаете исключения OOM.

Несколько вещей, которые вы должны добавить:

1) Установите свойства Source и SourceUri равными нулю при прокрутке списка из списка. См. «Кэширование изображений» в статье Стефана здесь @ http://sofru.miximages.com/c%23/ and only takes up the memory needed for this size bmp.DecodePixelWidth = 480; bmp.UriSource = new Uri(@AssetsDemo.png”, UriKind.Relative); ImageControl.Source = bmp;

(образец кода отсюда )

3) Почему вы используете Picture.GetImage () вместо Picture.GetThumbnail ()? Вам действительно нужно изображение, чтобы охватить весь экран?

4) Рассмотрите возможность перехода из ListBox в LongListSelector, если это эксклюзивное приложение WP8. LLS имеет гораздо лучшую визуализацию, чем ListBox. Если посмотреть на образец кода, вам может быть достаточно просто изменить элемент XAML ListBox на элемент LongListSelector.

Попробуйте этот подход: загрузчик изображений с автоматической очисткой памяти . Пример проекта здесь: https://simca.codeplex.com/

Давайте будем гением компьютера.