1

I have a usercontrol in WPF,it contains a Image and it's Source binding with ImgSource:

    public ImageWithWatingCtl()
    {
        this.DataContext = this;
        InitializeComponent();
        story = (base.Resources["waiting"] as Storyboard);
        StartLoading();
    }
    private ImageSource imgSource;

    public ImageSource ImgSource
    {
        get { return imgSource; }
        set
        {
            if (imgSource != null)
            { 
                imgSource = null;
            }
            imgSource = value;
            this.Dispatcher.BeginInvoke(new Action(() => { StopLoading(); }));
            OnPropertyChanged("ImgSource");
        }
    }


    public string Source
    {
        get { return (string)GetValue(SourceProperty); }
        set { SetValue(SourceProperty, value); }
    }

    public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", typeof(string), typeof(ImageWithWatingCtl), new FrameworkPropertyMetadata(new PropertyChangedCallback(CallBack)));

    private static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ImageWithWatingCtl imgCtl = d as ImageWithWatingCtl;
        imgCtl.path = e.NewValue != null ? e.NewValue.ToString() : "";
        imgCtl.GetImage();
    }

    private void GetImage()
    {
        if (!string.IsNullOrEmpty(path))
        {

            service.OnComplate += service_OnComplate;
            service.EnQueue(path);

        }
    }

    void service_OnComplate(string url, BitmapImage bitmap)
    {
        if (url == path)
        {

            if (bitmap != null)
            {
                if (ImgSource == null)
                    ImgSource = bitmap;

            }

        }
    }

    private void StartLoading()
    {
        this.story.Begin();
        imageLoading.Visibility = Visibility.Visible;
    }
    private void StopLoading()
    {
        this.story.Stop();
        imageLoading.Visibility = Visibility.Collapsed;
    }

This control gets a url and send it to GetImageService:

    public delegate void ComplateDelegate(string url, BitmapImage bitmap);
    public event ComplateDelegate OnComplate;
    private List<string> LstImageInfo { get; set; }
    object lstlock = new object();

    private static GetImageService instance = null;
    private readonly static object instance_lock = new object();

    public static GetImageService GetInstance()
    {
        if (instance == null)
        {
            lock (instance_lock)
            {
                if (instance == null)
                {
                    instance = new GetImageService();
                }
            }
        }
        return instance;
    }

    private GetImageService()
    {
        LstImageInfo = new List<string>();
        Thread getTread = new Thread(new ThreadStart(GetImage));
        getTread.IsBackground = true;
        getTread.Start();
    }

    public void EnQueue(string info)
    {
        lock (lstlock)
        {
            if (!LstImageInfo.Contains(info))
                LstImageInfo.Add(info);
        }
    }

    private void GetImage()
    {
        while (true)
        {
            lock (lstlock)
            {
                if (LstImageInfo.Count > 0)
                {
                    Console.WriteLine(LstImageInfo.Count);
                    BitmapImage bitmap = null;
                    var info = LstImageInfo[0];
                    if (info.StartsWith("http:"))
                    {
                        bitmap = ShareClass.GetBitemapUrl(info);
                    }
                    else
                    {
                        bitmap = ShareClass.GetBitmapImage(info);
                    }
                    if (bitmap.CanFreeze)
                        bitmap.Freeze();
                    if (OnComplate != null)
                        OnComplate(info, bitmap);
                    LstImageInfo.RemoveAt(0);
                }
            }
            Thread.Sleep(100);
        }
    }

the share class used to get image:

    public static BitmapImage GetBitmapImage(string path, int imageWidth = 0)
    {
        BitmapImage bitmap;
        if (!File.Exists(path))
        {
            path = Path.Combine(GetAssemblyPath(), path);
        }
        if (File.Exists(path))
        {
            using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(path)))
            {
                bitmap = new BitmapImage();
                bitmap.BeginInit();

                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                if (imageWidth > 0)
                {
                    using (System.Drawing.Image drawingImage = System.Drawing.Image.FromStream(ms))
                    {
                        int old_w = drawingImage.Width;
                        int old_h = drawingImage.Height;
                        int imageHeight = (int)(old_h * (imageWidth * 1.0 / old_w));
                        using (System.Drawing.Image thumImage = drawingImage.GetThumbnailImage(imageWidth, imageHeight, () => { return true; }, IntPtr.Zero))
                        {
                            MemoryStream ms_thum = new MemoryStream();
                            thumImage.Save(ms_thum, System.Drawing.Imaging.ImageFormat.Png);
                            bitmap.StreamSource = ms_thum;
                        }
                    }
                }
                else
                {
                    bitmap.StreamSource = ms;
                }
                bitmap.EndInit();
                bitmap.Freeze();
            }

            return bitmap;
        }
        else
            return null;
    }
     public static BitmapImage GetBitemapUrl(string url)
    {
        try
        {
            if (string.IsNullOrEmpty(url))
            {
                return null;
            }
            else
            {
                BitmapImage bitmap = new BitmapImage();
                WebClient wc = new WebClient();
                using (var ms = new MemoryStream(wc.DownloadData(url)))
                {
                    bitmap = new BitmapImage();
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.StreamSource = ms;
                    bitmap.EndInit();
                    if (bitmap.CanFreeze)
                        bitmap.Freeze();
                }

                return bitmap;
            }
        }
        catch (Exception ex)
        {
            return null;
        }
    }

I've tryed many ways,set ImgSource =null, use GC, but unuseful, so how to free memory or there is another way to realize my demand?

By the way, actually pictures in GetImageService are all 20~30kb,but if I set ImgSource = bitmap,I see memory increase higher than 10mb.

Rang
  • 1,362
  • 7
  • 17
  • 30
  • just out of curiosity, are you implementing some kind of animation effect? – kennyzx Nov 20 '14 at 02:02
  • @kennyzx yes,in my image usercontrol, I create a Storyboard to show loading effect and `in service_OnComplate() ` I stop it : `this.Dispatcher.BeginInvoke(new Action(() => { StopLoading(); }));` – Rang Nov 20 '14 at 02:17

2 Answers2

0

First thing you should make sure the UI Control which using the ImageSource is removed. GC.Collect should works very well, if GC.Collect failed to free the memory, there should be one reference to your ImageSource at least.

WPF UI is like a tree, In your situation.

UserControl -> BitmapImage -> ImageSource

both UserControl and BitmapImage have Childen, VisualChildren and LogicChildren, all may reference to your ImageSource.

Yang Yuan
  • 176
  • 1
  • 4
0

There are many SO threads on this topic, some solutions to this issue are

1. Freeze the BitmapImage

Cause: [copied from the link]

This leak is triggered because under the covers WPF keeps a strong reference between the static BitmapImage and the Image.

The fix/workaround is freezing the BitmapImage.

BitmapImage bitmap = ShareClass.GetBitemapUrl(info);
bitmap.Freeze(); 
OnComplate(info, bitmap);

2. Load image from stream instead of Uri.

you could verify if any of them can fix the memory issue.

Community
  • 1
  • 1
kennyzx
  • 12,845
  • 6
  • 39
  • 83
  • I've freeze bitmapimage in ShareClass,in addition I freeze it agin before OnComplate,but no use. pls see my update – Rang Nov 20 '14 at 03:08