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

Silverlight 在xaml中绑定图像数据时清除图像缓存(释放内存),silverlight,windows-phone-7,xaml,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<

根据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;
         }
    }
//MainPage.xaml内部
获取图像
//内部MainPage.xaml.cs
public void GetImages(对象发送方,RoutedEventArgs e){
(DataContext作为ViewModel);
}
//inside ViewModel.cs
public void GetMeSomeImages(){
列表链接=ThisMethodGetsLinks();
Links.Clear();
foreach(链接中的var链接)
链接。添加(链接);
}
可观测采集链接;
公共收集链接{
得到{
如果(_links==null)
_links=新的observeCollection();
返回链接;
}
}
在这种情况下,每次点击按钮都会消耗额外的内存,直到手机/模拟器崩溃。尽管清除了Listbox的ItemSource属性,图像仍不会从内存中释放。

因此定义了BackgroundCreation枚举:

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

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

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

总之,尝试以下几点:

  • 将CreateOptions更改为其他内容(或者根本不设置)
  • 在列表框内显式设置图像标记的高度。(这是必须的)
  • 使用下面的代码查看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;
        }
    }
    
    // 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>
    
    已更改用于公开链接的属性:

    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;
        }
    }
    
    // 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>
    
    //MainPage.xaml.cs内部
    public void GetImages(对象发送方,RoutedEventArgs e)
    {
    (DataContext作为ViewModel);
    }
    //inside ViewModel.cs
    public void GetMeSomeImages()
    {
    列表链接=ThisMethodGetsLinks();
    Links.Clear();
    _links=新的observeCollection();
    foreach(链接中的字符串链接)
    {
    _添加(新的BoundImage(link));
    }
    }
    可观测采集链接;
    公共收集链接
    {
    得到
    {
    如果(_links==null)
    _links=新的observeCollection();
    返回链接;
    }
    设置
    {
    _链接=价值;
    }
    }
    
    将XAML更改为钩住绑定到BoundImage的ImageURL属性:

    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;
        }
    }
    
    // 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>
    
    //MainPage.xaml内部
    获取图像
    
    您是如何尝试为列表框中的图像释放内存的?目前,在我的应用程序中,我绑定了这样一个图像,其中MyImage是我创建的位图图像,并存储在视图模型中的可观察集合中,但我需要手动启动后台线程并通过Webclient下载它。然后,一旦我请求新图像,我就“清除”Listbox的ItemSource设置为的ObservableCollection(通过将每个MyImage的URI源设置为null),它似乎可以降低内存,但比Xaml解决方案要详细得多。我一直在互联网上寻找基于Xaml的解决方案,但运气不好。