当图像是在xaml中绑定的数据时清除图像缓存(释放内存)

时间:2012-02-27 23:18:31

标签: silverlight windows-phone-7 xaml

根据Stefan Wick的博客,从图像中释放内存就像在做:

  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属性,但图像不会从内存中释放。

1 个答案:

答案 0 :(得分:2)

BitmapCreateOptions Enumeration因此定义了BackgroundCreation枚举:

  

使BitmapSource在声明后立即初始化。此选项将图像缓存用于以前使用的URI。如果图像不在图像缓存中,则将在单独的后台线程上下载和解码图像。

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

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

总之,试试这些:

  1. 将CreateOptions更改为不同的(或根本不设置)
  2. 在列表框中明确设置图像标记的高度。 (这是必须的)
  3. 使用以下代码查看virt。已经实现了。
  4. 包含图像数据的简单结构:

    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更改为Hook绑定到BoundImage的ImageURL属性:

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