1

I am trying to load pictures with the code below:

void LoadPictures(int s)
{
    grdScene.Children.Clear();
    TabControl tab = new TabControl();
    for (int i = s; i <= s+3; i++)
    {
        System.Drawing.Bitmap bmp = new System.Drawing.Bitmap("C:\\img\\" + i + ".png");
        TabItem tabItem = new TabItem();
        tabItem.Header = "Scene " + i;
        var bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(),
                                                                    IntPtr.Zero,
                                                                    Int32Rect.Empty,
                                                                    BitmapSizeOptions.FromEmptyOptions());
        Image image = new Image();
        image.Source = bitmapSource;
        tabItem.Content = image;
        tab.Items.Add(tabItem);
    }
    grdScene.Children.Add(tab);//grdScene is Grid
}

it works fine but every time I run this method the memory is going up. Then I tried to set all the items to null, but no success. Here is the code:

foreach (var child in grdScene.Children)
{
    TabControl tab1 = (TabControl)child;
    foreach (TabItem item in tab1.Items)
    {
        Image img = (Image)item.Content;
        img = null;
    }
    tab1 = null;
}

I tried GC.Collect() but nothing changes. What should I do to solve this problem?

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Dilshod
  • 3,189
  • 3
  • 36
  • 67

2 Answers2

2

Why are you doing this via code-behind?

From the look of your code the tab items are populated entirely by the image.

You could just bind a tabcontrol to the image path collection and use a style to describe the look you're after, it'd be a lot simpler and the WPF core would then be taking care of the image loading and memory management itself.


If that's not usable for you then your issue is coming from how you're loading the image, you're doing it via an unsafe interop method that doesn't release the memory for you, you have to dispose of the bitmap youreself .Dispose()

You'd be better doing: loading the source of a BitmapImage in WPF?

Using BitmapImage is likely the better way to go if you want to remain doing it in the code-behind rather than the Interop methods.

SO:

    void LoadPictures(int s)
    {
        grdScene.Children.Clear();
        TabControl tab = new TabControl();
        for (int i = s; i <= s + 3; i++)
        {
            BitmapImage bmp = new BitmapImage(new Uri("C:\\img\\" + i + ".png"));
            TabItem tabItem = new TabItem();
            tabItem.Header = "Scene " + i;
            tabItem.Content = new Image(){Source = bmp};
            tab.Items.Add(tabItem);
        }
        grdScene.Children.Add(tab);
    }

Should do the trick.

Community
  • 1
  • 1
Clint
  • 6,133
  • 2
  • 27
  • 48
  • 1
    the reason I was using Bitmap is my actual pictures are not in filesystem. It is stored in database. In my example to make it easier I just loaded it from filesystem. I was thinking converting binary picture to Bitmap and convert Bitmap to BitmapSource. I guess I can convert directly from byte[] to Windows.Controls.Image. I didn't know that before. So that solves the problem. Thank you for helping! – Dilshod Apr 04 '13 at 21:55
  • 1
    @Dilshod no problem, yes you can convert across that way, but anytime you're using System.Drawing classes or the WPF interop systems be sure to call `.Dispose` on everything, they are evil classes and you will get memory leaking all over the place. – Clint Apr 04 '13 at 21:56
2

Try to call Dispose on all IDisposables, such as System.Drawing.Bitmap or better use using..

using(var bmp = new System.Drawing.Bitmap("C:\\img\\" + i + ".png"))
{
     .....
}
I4V
  • 34,891
  • 6
  • 67
  • 79
  • Note, WPF bitmaps are not disposable as Forms ones : http://stackoverflow.com/questions/8352787/how-to-free-the-memory-after-the-bitmapimage-is-no-longer-needed – aybe Apr 05 '13 at 01:04