C# Idiosyncrasy #1: PictureBox and Dispose

C# has several idiosyncrasies that I have come across now. So, why not make a loose series (loose as in how frequently I update this blog anyway) about it?

Recently I started a little project to develop a Desktop Client in .Net 4.0 for trakt.tv with its amazing API. In there I implemented the similar shows function from said API and created dynamically a PictureBox for each show and displayed the cover. Whenever one clickes on such a cover the shows information, including seasons and again similar shows for the "new" show are getting displayed. To hold these boxes a single panel is used for all similar shows.

Now, when there are new covers to display in there the program obviously has to delete the old PictureBoxes as the amount can vary, reusing is therefore not a choice. And here we've got the problem, in a very early version I just ended up using panel.Clear(), assuming it might lead to problems and left it for future improvment. After some clicking around in the similar shows panel and loading some of those covers that are resized to the nearest size of 120x120 by keeping the ratio of height:width intact and saved locally for later use the program used easily more than 210MB of RAM shortly after start whereupon around 30MB would be normal and the Garbage Collector didn't free any of those resources.

After finding the culprit the improvement to that was apparently to use Dispose() on each Control in that panel correctly instead of just Clear(). Since Dispose() is described in the Docs as "freeing all used resources" you might think it's enough to free the used Image within the PictureBox. Especially when you have no reference or connection to those PictureBoxes anymore after disposing. Turns out that's wrong. You have to set the PictureBox's Image to null beforehand. Otherwise it won't free the resources used by the image.

PictureBox pb = new PicturBox();
pb.Image = (Image) resource;

// some other code, work with the PB etc.
//Now removing the prior created PBs, wrong:
pb.Dispose();  //resource stays in RAM, GC doesn't free it

//correct:
pb.Image = null;  // first setting the Image to null manually
pb.Dispose();     //now resource will be freed when GC makes its next turn