For some of my winforms applications I need to create a whole bunch of GDI+ objects (brushes, pens, fonts, etc) and use them over and over again. I created a ghetto caching singleton to accomplish what I need, but the code smell is overwhelming...
public sealed class GraphicsPalette
{
public static readonly GraphicsPalette Instance = new GraphicsPalette();
static GraphicsPalette()
{
}
private Dictionary<Color, Brush> solidBrushes;
//multithreading
private object brushLock;
private GraphicsPalette()
{
solidBrushes = new Dictionary<Color, Brush>();
brushLock = new object();
}
public Brush GetSolidBrush(Color color, int alpha)
{
return GetSolidBrush(Color.FromArgb(alpha, color));
}
public Brush GetSolidBrush(Color color)
{
if (!solidBrushes.ContainsKey(color))
{
lock (brushLock)
{
if (!solidBrushes.ContainsKey(color))
{
Brush brush = new SolidBrush(color);
solidBrushes.Add(color, brush);
return brush;
}
}
}
return solidBrushes[color];
}
}
OnPaint()
etc gets called?Brush
object get called which will in turn release any unmanaged resources?I apologize if this is a repeat, but I didn't find any similar questions.
There will not be memory leak but it’s better to release GDI+ objects when it makes sense for you. There are a limited amount of them in the operating system, so you might cause rendering issues in your and others applications. Another thing to be mentioned is inability of GDI+ objects (fonts, etc.) to be used by 2+ threads the same time (some difficult to reproduce exceptions might be thrown). You might be interested in some measurements of actual GDI+ objects creation time vs. possible exclusive locking delays. "premature optimization is the root of all evil" © Donald Knuth
Actually it works for me to do some GDI+ objects caching: per painting cycle. Client code might look like this:
class Visual
{
public void Draw()
{
using (new GraphicsPalette()) {
DrawHeader();
DrawFooter();
}
}
private void DrawHeader() {
var brush = GraphicsPalette.GetSolidBrush(Color.Green);
...
}
public void DrawFooter() {
using (new GraphicsPalette()) { // ensures palette existence; does nothing if there is a palette on the stack
var brush = GraphicsPalette.GetSolidBrush(Color.Green); // returns the same brush as in DrawHeader
...
}
}
}
So we need GraphicsPalette to ignore nested construction and return the same brush for a given thread. The suggested solution:
public class GraphicsPalette : IDisposable
{
[ThreadStatic]
private static GraphicsPalette _current = null;
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
public GraphicsPalette()
{
if (_current == null)
_current = this;
}
public void Dispose()
{
if (_current == this)
_current = null;
foreach (var solidBrush in _solidBrushes.Values)
solidBrush.Dispose();
}
public static SolidBrush GetSolidBrush(Color color)
{
if (!_current._solidBrushes.ContainsKey(color))
_current._solidBrushes[color] = new SolidBrush(color);
return _current._solidBrushes[color];
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With