2012-02-27 127 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>