Click here to Skip to main content
15,867,453 members
Articles / Multimedia / GDI+

Diposer for GDI+ resources (Pen, Brush, ...)

Rate me:
Please Sign up or sign in to vote.
3.17/5 (18 votes)
23 Oct 2009CPOL7 min read 62.2K   22   30
Helper class to call Dispose() of created GDI+ resources.

Introduction

The draw resource classes found inside the System.Drawing namespace (Pen, SolidBrush, ...) are actually wrapping the underling native Windows GDI resources. Like all system resources, their amount is limited, and so they should be returned to the Operating System as soon as possible.

That's why .NET documentation tells you to call the Dispose() method on Pens, Brushes etc., as soon as they are not used anymore.

Background

But .NET is a managed environment and has a garbage collector, then why do we have to manually release resources? Consider the following usual paint code:

C#
protected override void OnPaint(PaintEventArgs e)
{
    Pen myPen1 = new Pen(Color.Black, 3);
    Pen myPen2 = new Pen(Color.Lime, 3);
    SolidBrush myBrush1 = new SolidBrush(Color.Red);
    SolidBrush myBrush2 = new SolidBrush(Color.Lime);

    // ....
    // use myPenX and myBrushX for drawing
    // ...
}

Pen and SolidBrush are internally holding unmanaged native Win32 GDI resources (HBRUSH, HPEN etc.). These are precious Windows system resources and should be released as soon as possible. When OnPaint() exits, the only references to myPenX and myBrushX are gone, so the .NET garbage collector will release the instances sometime later. This will, of course, also release the underlying native handles. But the point is, sometime later. For the time span between the last reference gone and the garbage collector releasing the unused instances, the underlying native resources are occupied although not in use anymore. This can be a problem on heavy system load situations. And, like all resources, we should use them not more than necessary, and return them as soon as we do not need them anymore.

So, we have to call Dispose() on all Pens, Brushes, etc.:

C#
protected override void OnPaint(PaintEventArgs e)
{
    Pen myPen1 = new Pen(Color.Black, 3);
    Pen myPen2 = new Pen(Color.Lime, 3);
    SolidBrush myBrush1 = new SolidBrush(Color.Red);
    SolidBrush myBrush2 = new SolidBrush(Color.Lime);

    // ....
    // use myPen and myBrush for drawing
    // ...

    myPen1.Dispose();
    myPen2.Dispose();
    myBrush1.Dispose();
    myBrush2.Dispose();
}

If you have a complicated drawing routine incorporating many pens and brushes, calling Dispose() on all instances can be a tedious and error prone task. You easily add a new brush in the middle of your code and forget to call Dispose() on it before method exit. And be aware: if an exception is thrown somewhere in your drawing code, the control flow can jump out of OnPaint() without running to its end. So, the Dispose() of your instances will not be called at all.

C# provides the using statement for this situation:

C#
protected override void OnPaint(PaintEventArgs e)
{
    using (Pen myPen1 = new Pen(Color.Black, 3))
    using (Pen myPen2 = new Pen(Color.Lime, 3))
    using (SolidBrush myBrush1 = new SolidBrush(Color.Red))
    using (SolidBrush myBrush2 = new SolidBrush(Color.Lime))
    {
        // use myPenX and myBrushX for drawing
    }
    // here all resources created inside using() are diposed,
    // also in case of an exception or return statement
}

This solves the problem perfectly for simple OnPaint() methods. But if there is more complicated painting involving different paint resources depending on the control's state, we have to create all possibly used resources before painting:

C#
protected override void OnPaint(PaintEventArgs e)
{
    using (Pen myPen1 = new Pen(Color.Black, 3))
    using (Pen myPen2 = new Pen(Color.Lime, 3))
    using (SolidBrush myBrush1 = new SolidBrush(Color.Red))
    using (SolidBrush myBrush2 = new SolidBrush(Color.Lime))
    {
        if ( some condition )
        {
            // use myPen, myBrush1 and myBrush2, myPen2 is not used
            if ( some other condition )
            {
                // use myPen2
            }
        }
        else
        {
            // use myPen1 and myBrush1 for drawing,
            // myPen2 and myBrush2 are never used
        }
    }
    // here all resources created inside using()
    // are diposed, also in case of an exception or return
}

Of course, this is not a big deal if only one brush or pen is unnecessarily created. I do visualizations for industrial processes, and there we use sophisticated user controls involving a lot of complicated painting using many different resources. And, there are two big 1600x1080 screens showing many of those controls on the same time. All the needless draw resources created have a measurable impact on the system performance. To avoid this, we would end with something like this:

C#
protected override void OnPaint(PaintEventArgs e)
{
    using (Pen myPen1 = new Pen(Color.Black, 3))
    using (SolidBrush myBrush1 = new SolidBrush(Color.Red))
    {
        if ( some condition )
        {
            using (SolidBrush myBrush2 = new SolidBrush(Color.Lime))
            {
                // use myPen1, myBrush1 and myBrush2
                if ( some other condition )
                {
                    using (Pen myPen2 = new Pen(Color.Lime, 3))
                    {
                        // use myPen2
                    }
                    // here myPen2 is disposed
                 }
            }
            // here myBrush2 is disposed
        }
        else
        {
            // use myPen1 and myBrush1 for drawing, myPen2 and myBrush2 are never used
        }
    }
    // here myPen1 and myBrush1 are disposed
}

So far, so good. But imagine the code we are ending up with if there are 6 conditions and 12 draw resources, some of them used in all control paths, some in only one, some in three of them, and so on. The resulting construct of nested using statements is hard to read and even harder to maintain. If the drawing code changes and a resource used before only in one control path is newly used in another control path too, we have to rearrange the whole code.

Helper class to dispose drawing resources on method exit

I have a strong C++ background, and in C++, it is a best practice to encapsulate resources in objects to get them released as soon as they go out of scope. This is especially important to make code exception safe. So, I ported the C++ approach to C#: have a helper class releasing resources.

C#
using System;
using System.Collections.Generic;

class Disposer : IDisposable
{
    private List<IDisposable> m_disposableList = 
            new List<IDisposable>();
    private bool m_bDisposed = false;

    // default ctor
    public Disposer()
    {
    }

    public void Add(IDisposable disposable)
    {
        if (m_bDisposed)
        {
            // its not allowed to add additional items
            // to dispose if Dispose() already called
            throw new InvalidOperationException(
              "Disposer: tried to add items after call to Disposer.Dispose()");
        }
        m_disposableList.Add(disposable);
    }

    #region IDisposable members
    public void Dispose()
    {
        if (!m_bDisposed)
        {
            foreach (IDisposable disposable in m_disposableList)
            {
                disposable.Dispose();
            } 
            m_disposableList.Clear(); 
            m_bDisposed = true;
        }
    } 
    #endregion
}

All drawing resource classes in the System.Drawing namespace implement the IDisposable interface. It declares a method, Dispose(). For details, see the MSDN documentation on IDisposable. We add all object instances to be disposed to an instance of Disposer, which iterates the internal list and calls Dispose() on all entries. Be aware that this code is not thread safe. For this, m_disposableList and m_bDisposed would need to be protected with a lock(). The intended use case for this helper class is inside user controls, where all members have to be accessed from the thread which initially created the control anyway.

Usage of the Disposer helper class

Now, the OnPaint() method is changed to use the Disposer helper class:

C#
protected override void OnPaint(PaintEventArgs e)
{
    using (Disposer drawResDisposer = new Disposer())
    {
        Pen myPen1 = new Pen(Color.Black, 3);
        drawResDisposer.Add(myPen1);
        SolidBrush myBrush1 = new SolidBrush(Color.Red);
        drawResDisposer.Add(myBrush1);

        if ( some condition )
        {
            SolidBrush myBrush2 = new SolidBrush(Color.Red);
            drawResDisposer.Add(myBrush2);
            // use myPen1, myBrush1 and myBrush2
            if ( some other condition )
            {
                Pen myPen2 = new Pen(Color.Yellow, 3);
                drawResDisposer.Add(myPen2);
                // draw using myPen2
            }
        }
        else
        {
            // use myPen1 amyBrush1 for drawing,
            // myPen2 and myBrush2 is never used
        }
        // here all resources added to drawResDisposer
        // in any control path are disposed
    }

Just add every instantiated drawing object to the Disposer, drawResDisposer, and on exit of the code block surrounded by using {}, drawResDisposer.Dispose() is called automagically, which calls the Dispose() of all the instances added to it. No need to know which of the draw resources where actually needed and created. Just add them to the disposer, use them, and forget about them. I think this is a much cleaner and easier to maintain code structure.

The Disposer class can also be used for every other class encapsulating native and unmanaged resources. As long as the class implements the IDisposable interface.

I am a fan of one single kind of programmer laziness only: Don't do things manually which can be done by the compiler or the runtime automatically.

Improving the Disposer with WeakReference

In advanced user controls, we should not create all drawing resources on each call of OnPaint(). Painting occurs quite frequently in situations like resizing. It is a waste of resources to create a pen every time it is used and then hand it over to the garbage collector for destruction directly. Each drawing resource should be created on first use, and disposed inside the control's Dispose() method. In other words, we create a singleton for each different drawing resource type used for painting. This way, the resources are held by the control during its lifetime, but this is still better than constructing and destroying the same drawing resources over and over again. But, this approach makes the problem even worse: the resources are now created and destroyed in different methods of the control class, so it is much harder to get the Dispose() of each resource called inside the control's Dispose() method. If we fail to dispose the disposer instance when the control gets destroyed, the internally existing references to the IDisposable instances stop the garbage collector from freeing them. Less than better...

To make the disposer helper class more useful in such situations, it is changed to use the .NET System.WeakReference class. It allows the garbage collector to free an object although there is still a reference, a weak one. See the MSDN documentation for details. This class is made for such kind of situations: a reference to an object which can be used to dispose it but does not count itself as a reference for the garbage collector.

C#
using System;
using System.Collections.Generic;

class Disposer : IDisposable
{
    private List<WeakReference> m_disposableList = 
            new List<WeakReference>();
    private bool m_bDisposed = false;

    // default ctor
    public Disposer()
    {
    }

    public void Add(IDisposable disposable)
    {
        if (m_bDisposed)
        {
            // its not allowed to add additional items
            // to dispose if Dispose() already called
            throw new InvalidOperationException(
              "Disposer: tried to add items after call to Disposer.Dispose()");
        }
        m_disposableList.Add(new WeakReference(disposable));
    }

    #region IDisposable members
    public void Dispose()
    {
        if (!m_bDisposed)
        {
            foreach (WeakReference weakRef in m_disposableList)
            {
                try
                {
                    if (weakRef.IsAlive)
                    {
                        // sadly there is no generic version of WeakReference
                        // WeakReference<IDisposable> would be nice...
                        IDisposable strongRef = (IDisposable)weakRef.Target;
                        // strongRef is null if weakRef.Target already disposed
                        if (strongRef != null)
                        {
                            strongRef.Dispose();
                        }
                    }
                } 
                catch (System.InvalidOperationException ex)
                {
                    // weakRef.Target already finalized
                }
            }
        } 
        m_disposableList.Clear(); 
        m_bDisposed = true;
    }
    #endregion
}

Here is the usage of the improved class inside a control:

C#
class MyControl : UserControl
{
    private Disposer m_Disposer = new Disposer();

    private Pen m_Pen1 = null;
    private Pen1
    {
        get
        {
            if (m_Pen1 == null)
            {
                m_Pen1 = new Pen(Color.Lime, 5);
                m_Disposer.Add(m_Pen1);
            }
            return m_Pen1;
        }
    }

    private SolidBrush m_Brush1 = null;
    private Brush1
    {
        get
        {
            if (m_Brush1 == null)
            {
                m_Brush1 = new SolidBrush(Color.Red);
                m_Disposer.Add(m_Brush1);
            }
            return m_Brush1;
        }
    }

    //
    // ... many other pens, brushes, fonts, we draw a copmlicated control
    //

    protected override void OnPaint(PaintEventArgs e)
    {
        // ...
        // use the same singleton on every call
        e.Graphics.DrawLine(this.Pen1, point1, point2);
        e.Graphics.FillRectangle(this.Brush1, rect);
        // ...
    }

    #region IDisposable members
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (components != null)
            {
                components.Dispose();
            }
            m_Disposer.Dispose()
            base.Dispose(disposing);
        }
    }
    #endregion

    //
    // ...
    //
}

All you have to is add the lines shown in bold. If you forget the call to m_Disposer.Dispose(), the garbage collector will take care of the draw resources. The references held inside the disposer class do not count for the garbage collector anymore. This is exactly what we want.

Points of interest

This possible usage of the using keyword is often overseen:

C#
// myType is any type implementing IDisposable
using (myType myVar = new myType())
{

    // ...
    // code here making use of myVar
    // ...
}

If the code block following using() is left on any path, including an exception, a return in the middle etc., myVar.Dispose() is called implicitly. The needed calls are inserted by the C# compiler. The only restriction for types used in using() is that they implement IDisposable.

The using statement is actually just syntactic sugar which is extended by the C# compiler to the following code. You can see this if you inspect your binary assembly with Reflector:

C#
try
{
    // myType is any type implementing IDisposable
    myType myVar = new myType();

    // ...
    // code here making use of myVar
    // ...

}
catch (System.Exception ex)
{

}
finally
{
    myVar.Dispose();
}

Conclusion

Although no resources will really be leaked by not calling Dispose() on GDI wrapper classes, it's a good habit to do so. This way, they are released when they are not needed anymore, and not sometime later when the garbage collector is run the next time.

Better idea?

I'm aware of the fact that this is a C++ approach. I'm relatively new to C#, but did C and C++ since more than a decade before. So, if you have a more "C#-ish" way to do this, please tell me.

History

  • 07/07/2009 - Initial version.
  • 10/13/2009 - WeakReference version added, some minor text additions.
  • 10/22/2009 - Reworked code samples to better show the intended use case.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Earnesto Developez15-Dec-11 11:00
Earnesto Developez15-Dec-11 11:00 
GeneralMy vote of 1 Pin
dregsor2-Dec-09 7:25
dregsor2-Dec-09 7:25 
GeneralRe: My vote of 1 Pin
Joerg Blattner8-Dec-09 7:08
Joerg Blattner8-Dec-09 7:08 
GeneralMy vote of 1 Pin
winSharp9320-Oct-09 5:00
winSharp9320-Oct-09 5:00 
General*MY* vote of 1 Pin
Ilíon24-Oct-09 14:15
Ilíon24-Oct-09 14:15 
GeneralMy vote of 1 Pin
Przemyslaw Wlodarczak19-Oct-09 23:12
Przemyslaw Wlodarczak19-Oct-09 23:12 
General*MY* vote of 1 Pin
Ilíon24-Oct-09 14:15
Ilíon24-Oct-09 14:15 
Generalstandard colors Pin
Luc Pattyn15-Oct-09 2:02
sitebuilderLuc Pattyn15-Oct-09 2:02 
GeneralRe: standard colors Pin
supercat916-Oct-09 7:29
supercat916-Oct-09 7:29 
Luc Pattyn wrote:
Hence, the article really should use different examples, say Fonts and pens with a width larger than 1.


Or better yet, code which may (depending upon what's being drawn) either use a stock object or a newly-created one. Which is nicer (bearing in mind that one Disposer object can handle many brush, font, pen, and other objects:
Using Dispos as New Disposer
  Dim BR1 as Brush,BR2 as Brush

  If Color1=Color.Black then
    BR1 = Brushes.Black
  Else
    BR1 = New SolidBrush(Color1)
    Dispos.Add(BR1)
  End If
  If Color2=Color.Black then
    BR2 = Brushes.Black
  Else
    BR2 = New SolidBrush(Color2)
    Dispos.Add(BR2)
  End If
  .. Now do stuff with BR1 and BR2
End Using

or, if not using Disposer:
Dim DisposBR1 = False
Dim DisposBR2 = False
Try
  If Color1=Color.Black then
    BR1 = Brushes.Black
  Else
    BR1 = New SolidBrush(Color1)
    DisposBR1 = True
  End If
  If Color2=Color.Black then
    BR2 = Brushes.Black
  Else
    BR2 = New SolidBrush(Color2)
    DisposBR2 = True
  End If
  .. Now do stuff with BR1 and BR2
Finally
  If DisposBR1 Then BR1.Dispose
  If DisposBR2 Then BR2.Dispose
End Try

I would think approach #1 would be far less error-prone, though it might be improved by adding a generic function to Disposer which would add its argument and return it (allowing something like:
BR1 = Dipos.Add(New SolidBrush(Color1))

GeneralMy vote of 1 Pin
cpw999cn14-Oct-09 17:46
cpw999cn14-Oct-09 17:46 
GeneralRe: My vote of 1 Pin
supercat916-Oct-09 13:17
supercat916-Oct-09 13:17 
GeneralMy vote of 1 Pin
seeblunt14-Oct-09 13:31
seeblunt14-Oct-09 13:31 
GeneralIgnorance is dangerous. Pin
seeblunt14-Oct-09 13:31
seeblunt14-Oct-09 13:31 
GeneralRe: Ignorance is dangerous. Pin
Joerg Blattner14-Oct-09 14:50
Joerg Blattner14-Oct-09 14:50 
GeneralRe: Ignorance is dangerous. Pin
seeblunt14-Oct-09 15:55
seeblunt14-Oct-09 15:55 
GeneralMy vote of 1 Pin
Heywood14-Oct-09 11:29
Heywood14-Oct-09 11:29 
GeneralMy vote of 2 Pin
occcy17-Aug-09 20:50
occcy17-Aug-09 20:50 
GeneralRe: My vote of 2 Pin
Joerg Blattner27-Aug-09 2:23
Joerg Blattner27-Aug-09 2:23 
GeneralRe: My vote of 2 Pin
Heywood14-Oct-09 11:26
Heywood14-Oct-09 11:26 
GeneralWhy it's useful Pin
supercat915-Oct-09 7:58
supercat915-Oct-09 7:58 
GeneralUse WeakReference Pin
Niel M.Thomas13-Jul-09 21:12
professionalNiel M.Thomas13-Jul-09 21:12 
GeneralRe: Use WeakReference Pin
Joerg Blattner23-Jul-09 9:39
Joerg Blattner23-Jul-09 9:39 
GeneralMore concisely and less error prone would be to just put a 'using' on every IDisposable unmanaged resource that you create ... Pin
HightechRider7-Jul-09 21:11
HightechRider7-Jul-09 21:11 
GeneralRe: More concisely and less error prone would be to just put a 'using' on every IDisposable unmanaged resource that you create ... Pin
Joerg Blattner7-Jul-09 21:56
Joerg Blattner7-Jul-09 21:56 
GeneralRe: More concisely and less error prone would be to just put a 'using' on every IDisposable unmanaged resource that you create ... Pin
supercat915-Oct-09 8:14
supercat915-Oct-09 8:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.