Hyn*_*rix 27 c# listbox out-of-memory photo-gallery windows-phone-8
我想在我的自定义库中显示存储在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>
Run Code Online (Sandbox Code Playgroud)
使用以下转换器:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
图像存储在自定义类中:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下代码用于设置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;
};
Run Code Online (Sandbox Code Playgroud)
最后这里是"清理"代码:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
所有这一切工作正常,但代码崩溃OutOfMemoryException后几个图像(特别是当滚动快速).滚动VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1时,该方法称为regulary(例如,每2或3个列表框条目)ListBox.
这个示例代码有什么问题?
为什么内存没有被释放(足够快)?
gle*_*udr 23
哦,我最近杀了一整天才使这个工作!
所以解决方案是:
使您的Image控制免费资源.所以设置
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
Run Code Online (Sandbox Code Playgroud)
正如之前提到的那样.
确保在列表的每个项目上虚拟化_bitmap.你应该按需加载它(LongListSelector.Realized方法),你必须销毁它!它不会自动收集,GC.Collect也不起作用.空引用也不起作用:(但是这里的方法是:制作1x1像素文件.将其复制到程序集中并从中创建资源流以使用1x1像素空白处理图像.将自定义配置方法绑定到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 { }
}
Run Code Online (Sandbox Code Playgroud)
在LongListSelector中为我工作,每张图像宽度为1000张.
如果您错过了数据收集的第2步,您可以看到良好的结果,但滚动了100-200个项目后内存溢出.
Jus*_*gel 13
您刚刚使用Windows Phone在屏幕上显示用户媒体库"pictures"文件夹中的所有图片.这是令人难以置信的内存密集型,考虑到WP8应用程序的150MB限制,难怪你得到了OOM异常.
您应该考虑添加的一些事项:
1)将listboxitem滚动到视图外时,将Source和SourceUri属性设置为null.请参阅Stefan的文章中的"缓存图像"@ 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;
Run Code Online (Sandbox Code Playgroud)
2)如果你在WP8上,请确保设置DecodePixelWidth和/或DecodePixelHeight.这样,图像将被加载到内存中,永久调整大小,只有调整大小的副本存储在内存中.加载到内存中的图像可能比手机本身的屏幕尺寸大得多.因此,将那些缩小到合适尺寸并且仅存储调整大小的图像是至关重要的.设置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;
Run Code Online (Sandbox Code Playgroud)
(来自这里的代码示例)
3)为什么使用Picture.GetImage()而不是Picture.GetThumbnail()?你真的需要图像占据整个屏幕吗?
4)如果这是一个WP8独家应用程序,请考虑从ListBox移动到LongListSelector.LLS具有比ListBox更好的虚拟化.查看代码示例,只需将XAML ListBox元素更改为LongListSelector元素就足够了.