2012-02-27 81 views
2

据斯特凡威克的博客绑定的数据,从图像释放内存仅仅是因为这样做简单:清除图像缓存(释放内存)时,图像在XAML

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

但是,我怎么能达到同样的效果如果我使用的数据在XAML绑定这样?:

// inside MainPage.xaml 
<Button Tap="GetImages">Get Images</Button> 
    <ListBox ItemSource="{Binding Links}"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <!-- I am not binding to Image's Source directly, because I want to use the bitmap's 'Background creation' option and it's download progress event in the future --> 
       <Image> 
        <Image.Source> 
        <BitmapImage UriSource="{Binding}" CreateOptions="BackgroundCreation"/> 
        </Image.Source> 
       </Image> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
</ListBox> 


//inside MainPage.xaml.cs 
    public void GetImages(object sender, RoutedEventArgs e) { 
     (DataContext as ViewModel).GetMeSomeImages(); 
    } 

// inside ViewModel.cs 
    public void GetMeSomeImages() { 
     List<string> links = ThisMethodGetsLinks(); 
     Links.Clear(); 
     foreach(var link in links) 
      Links.Add(link); 
    } 

    ObservableCollection<string> _links; 
    public ObservableCollection<string> Links { 
    get { 
      if (_links == null) 
       _links = new ObservableCollection<string>(); 
      return _links; 
     } 
    } 

在这种情况下,每个按钮自来水将占用更多的内存,直到手机/仿真器崩溃。即使Listbox的ItemSource属性被清除,图像也不会从内存中释放。

+0

你是如何试图释放列表框中图像的内存的? – 2012-02-28 00:38:51

+0

好吧,目前在我的应用程序中,我绑定了一个像这样的图像,其中MyImage是一个BitmapImage,我创建并存储在视图模型中的可观察集合中,但我需要手动启动后台线程并通过Webclient下载。然后,一旦我请求新的图像,我'清除'Listbox的ItemSource被设置为的ObservableCollection (通过将每个MyImage的UriSource设置为null)似乎可以保持内存不变,但它比Xaml解决方案更加冗长。我一直在扫描互联网寻找基于Xaml的解决方案,但没有运气。 – krdx 2012-02-28 01:02:04

回答

2

BitmapCreateOptions Enumeration定义BackgroundCreation枚举正是如此:

导致一个的BitmapSource要尽快声明它初始化。该选项为以前使用的URI使用图像缓存。如果图像不在图像缓存中,图像将在单独的后台线程上下载并解码。

这使我认为,当您更改UriSource属性,并且旧图像处置后,后台线程处理下载的位图不会通知,并且后台线程继续下载图像。这可能是因为手机实现了它自己的所有图像的缓存(请注意BitmapCreateOptions枚举的“IgnoreImageCache”元素的存在)。

这可能是罪魁祸首,但是另一种可能性是ListBox的虚拟化实际上并没有发生。最常见的原因是如果列表中的项目没有明确定义为具有相同的高度。在ListBox中虚拟化使用VirtualizingStackPanel,并要求每个项目的高度相同。如果任何物品的高度不同,则取消虚拟行为。下面是一些代码,可以帮助您确定是否为virt。实际上是否正在发生。虚拟化的另一件事是,目前您的图像没有设置高度,直到下载图像数据。这意味着在下载图像之前,所有图像的高度均为0像素。如果所有图像的高度均为0像素,那么这意味着所有图像都是根据virt确定的。逻辑,他们都应该开始下载。

总之,尝试这些东西:

  1. 更改CreateOptions不同的东西(或不设置的话)
  2. 精确设置列表框内部的图像标记的高度。 (这是必须的)
  3. 使用下面的代码来查看是否是virt。已完成。

简单的结构来保存图像数据:

using System.Diagnostics; 

public class BoundImage 
{ 
    private string imageURL; 

    public static int TotalImagesRequested = 0; 

    public BoundImage(string url) 
    { 
     imageURL = url; 
    } 

    public string ImageURL 
    { 
     get 
     { 
      TotalImagesRequested++; 

      // Watch the output window and see if TotalImagesRequested is 
      // growing to a crazy high amount (if it is it will eventually 
      // reach the total Count of the _links variable. But your 
      // app may crash before that happens. 
      Debug.WriteLine("Images being requested: " + TotalImagesRequested); 
      return imageURL; 
     } 
    } 
} 

用于暴露链接改变的特性:

//inside MainPage.xaml.cs 
public void GetImages(object sender, RoutedEventArgs e) 
{ 
    (DataContext as ViewModel).GetMeSomeImages(); 
} 

// inside ViewModel.cs 
public void GetMeSomeImages() 
{ 
    List<string> links = ThisMethodGetsLinks(); 
    Links.Clear(); 

    _links = new ObservableCollection<BoundImage>(); 
    foreach(string link in links) 
    { 
     _links.Add(new BoundImage(link)); 
    } 
} 

ObservableCollection<BoundImage> _links; 
public ObservableCollection<BoundImage> Links 
{ 
    get 
    { 
     if (_links == null) 
      _links = new ObservableCollection<BoundImage>(); 
     return _links; 
    } 
    set 
    { 
     _links = value; 
    } 
} 

改变的XAML挂钩结合IMAGEURL BoundImage的属性:

// inside MainPage.xaml 
<Button Tap="GetImages">Get Images</Button> 
    <ListBox ItemSource="{Binding Links}"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <!-- I am not binding to Image's Source directly, because I want to use the bitmap's 'Background creation' option and it's download progress event in the future --> 
      <Image> 
       <Image.Source> 
       <BitmapImage UriSource="{Binding ImageURL}" CreateOptions="BackgroundCreation"/> 
       </Image.Source> 
      </Image> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox>