Ich möchte alle Bilder anzeigen, die im Windows Phone 8-Fotoordner in meiner benutzerdefinierten Galerie gespeichert sind, die eine ListBox
für die Anzeige der Bilder verwendet.Warum erhalte ich eine OutOfMemoryException, wenn ich Bilder in meiner ListBox habe?
Der ListBox
Code lautet wie folgt:
<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>
Mit folgendem Konverter:
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();
}
}
Bilder in einer benutzerdefinierten Klasse gespeichert sind:
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;
}
}
}
Der folgende Code verwendet wird, um die ListBox
Datenquelle festzulegen:
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;
};
Schließlich ist hier die „Bereinigung“ Code:
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;
}
}
Das alles funktioniert gut, aber der Code stürzt mit einem OutOfMemoryException
nach ein paar Bilder (vor allem, wenn schnell Scrollen). Die Methode VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1
wird regelmäßig aufgerufen (z. B. alle 2 oder 3 Listbox-Einträge), wenn die ListBox
gescrollt wird.
Was ist falsch an diesem Beispielcode?
Warum wird Speicher nicht freigegeben (schnell genug)?
Was ist 'Bild' und was macht die' GetImage() 'Methode? Sie setzen das '_bitmap'-Feld nur auf 'null', aber das' _picture'-Feld bleibt allein, könnte es das Objekt sein, das einige Daten enthält? Es ist auch keine gute Vorgehensweise, Felder öffentlich zugänglich zu machen. Implementieren Sie "IDisposable" in "PreviewImageItem" und nennen Sie 'Dispose()' in Ihrer 'VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1' Methode ... – khellang
In der Bereinigung sollten Sie auch die' _picture' Eigenschaft aufheben –
Das Bild ist vom Typ "Microsoft.Xna. Framework.Media.Picture "und benötigt nicht viel Speicher. Der meiste Speicher wird von den BitmapImages verwendet, die aus den Streams erstellt werden, die von den Picture-Objekten bereitgestellt werden. – Hyndrix