2012-12-11 11 views
26

मैं अपनी कस्टम गैलरी में विंडोज फोन 8 फोटो फ़ोल्डर में संग्रहीत सभी छवियों को प्रदर्शित करना चाहता हूं जो छवियों को प्रदर्शित करने के लिए 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> 
निम्नलिखित कनवर्टर के साथ

:

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(); 
    } 
} 

छवियाँ एक कस्टम कक्षा में जमा हो जाती है:

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; 
     } 
    } 
} 

निम्नलिखित कोड प्रयोग किया जाता है

ListBox कोड इस प्रकार है ListBox डेटा स्रोत सेट करने के लिए:

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; 
    } 
} 

यह सब ठीक काम करता है लेकिन कोड में कुछ छवियों के बाद एक OutOfMemoryException साथ दुर्घटनाओं (खासकर जब तेजी से स्क्रॉल):

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; 
}; 

अंत में यहाँ "सफाई" कोड है। विधि VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1 को विनियामक कहा जाता है (उदा। प्रत्येक 2 या 3 सूची बॉक्स प्रविष्टियां) जब ListBox स्क्रॉल किया जाता है।

इस नमूना कोड के साथ क्या गलत है?

स्मृति क्यों मुक्त नहीं है (पर्याप्त तेज़)?

+0

'चित्र 'क्या है और' GetImage() 'विधि क्या करती है? आपने केवल '_bitmap' फ़ील्ड को' शून्य 'पर सेट किया है, लेकिन' _ चित्र 'फ़ील्ड अकेला छोड़ा गया है, क्या यह वह ऑब्जेक्ट हो सकता है जिसमें कुछ डेटा हो? साथ ही, सार्वजनिक रूप से फ़ील्ड का पर्दाफाश करना एक अच्छा अभ्यास नहीं है। 'PreviewImageItem' में' IDISposable' लागू करें और अपने 'VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1' विधि में 'निपटान()' को कॉल करें ... – khellang

+0

सफाई में, आपको' _ चित्र 'संपत्ति को –

+0

चित्र को समाप्त करना चाहिए "चित्र। माइक्रोसॉफ्ट.एक्सना। फ्रेमवर्क। मीडिया। चित्र "और अधिक स्मृति की आवश्यकता नहीं है। अधिकांश स्मृति का उपयोग बिटमैप इमेज द्वारा किया जाता है जो चित्र वस्तुओं द्वारा प्रदान की गई धाराओं से बनाए जाते हैं। – Hyndrix

उत्तर

22

ओह, मैंने हाल ही में यह काम करने के लिए पूरे दिन मारा!

तो समाधान है:

अपनी छवि नियंत्रण मुक्त संसाधन बनाएं। तो

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

जैसा कि पहले उल्लेख किया गया था सेट करें।

सुनिश्चित करें कि आप सूची के प्रत्येक आइटम पर _bitmap वर्चुअलाइज़ करें। आपको इसे मांग पर लोड करना चाहिए (LongListSelector.Realized विधि) और आपको इसे नष्ट करना होगा! यह स्वचालित रूप से एकत्र नहीं होगा और जीसी। चयन भी काम नहीं करता है। अशक्त संदर्भ भी :( काम कर रहा है नहीं लेकिन यहाँ विधि है:। 1x1 पिक्सेल फ़ाइल बनाने विधानसभा में इसे कॉपी और इसके साथ 1x1 पिक्सेल खाली आपकी छवियों निपटान के लिए से संसाधन धारा बनाने कस्टम LongListSelector.UnRealized घटना के लिए विधि निपटान के लिए बाध्य। 1000 400 चौड़ाई प्रत्येक के साथ LongListSelector में मेरे लिए काम कर (e.Container अपने सूची आइटम संभालती है)।

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 { } 
} 

आप डेटा संग्रह के साथ 2 कदम चूक जाते हैं तो आप अच्छे परिणाम देख सकते हैं लेकिन 100-200 आइटम स्क्रॉल किए जाने के बाद मेमोरी बहती है।

+0

मैं फिर से स्मृति समस्या के पार आया। और इसका एकमात्र साधन जो हल हो गया था वह आपके "डिस्प्ले इमेज" विधि का उपयोग कर रहा था! – Hyndrix

+1

खुशी है कि यह आपकी मदद करता है। मुझे लगता है कि यह WP8 मंच में एक बग है। –

+0

मुझे एक ही समस्या का सामना करना पड़ रहा है। समाधान के लिए धन्यवाद। –

13

आपके पास बस स्क्रीन पर उपयोगकर्ता की मीडिया लाइब्रेरी "चित्र" फ़ोल्डर में सभी तस्वीरों के साथ विंडोज फोन था। यह अविश्वसनीय स्मृति गहन है और WP8 ऐप्स पर 150 एमबी सीमा पर विचार करने से कोई आश्चर्य नहीं कि आपको ओओएम अपवाद मिल रहे हैं।

कुछ बातें आप को जोड़ने पर विचार करना चाहिए:

1) सेट स्रोत और SourceUri गुण दृश्य से बाहर listboxitem स्क्रॉल जब शून्य पर। देखें स्टीफन के लेख में "कैशिंग छवियाँ" @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; 

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; 

(here से कोड नमूना)

3) तुम क्यों Picture.GetImage() के बजाय Picture.GetThumbnail उपयोग कर रहे हैं()? क्या आपको पूरी स्क्रीन लेने के लिए वास्तव में छवि की आवश्यकता है?

4) यदि यह एक WP8 अनन्य ऐप है तो ListBox से LongListSelector पर जाने पर विचार करें। एलएलएस में सूची बॉक्स में बहुत अधिक वर्चुअलाइजेशन है। अपने कोड नमूना को देखते हुए यह आपके लिए XAML ListBox तत्व को LongListSelector तत्व में बदलने के लिए पर्याप्त हो सकता है।

+0

डिकोडिंग रिज़ॉल्यूशन को सीमित करने का संकेत बहुत अच्छा है (मैं इसे पूरी तरह से भूल गया हूं हालांकि यह बहुत स्पष्ट है)। थंबनेल स्ट्रीम रास्ता बहुत कम गुणवत्ता है। एक चीज जो भी महत्वपूर्ण है, सिस्टम स्क्रॉलिंग के मामले में सिस्टमिंग के बाद सिस्टम.जीसी.कोलेक्ट() को कॉल करना है। – Hyndrix

+0

मैं कुछ हद तक परेशान हूं, मेरी लिस्ट व्यू डाटाबेसिंग के माध्यम से अपना डेटा प्राप्त करती है, इस प्रकार मुझे स्क्रॉलिंग पर कोई प्रत्यक्ष प्रभाव नहीं पड़ता है। तकनीक का उपयोग 1. मैं छवियों को अनलोड कर सकता हूं, लेकिन यदि उपयोगकर्ता वापस स्क्रॉल करता है, तो छवियां अब काले हैं और फ्रेमवर्क द्वारा फिर से लोड नहीं होती हैं ... कोई विचार? –

+0

टिम, यदि आप WP8 पर हैं तो आपको ItemRealized और ItemUnrealized ईवेंट के साथ LongListSelector का उपयोग करना चाहिए। – JustinAngel

संबंधित मुद्दे