tags:

views:

171

answers:

3

Hi,

In the application I'm developing , we use the DevExpress XtraGrid control, which has a RowCellStyle event that allows to customize the style of each cell. The event handlers for this event typically look like that :

private gridView1_RowCellStyle(object sender, RowCellStyleEventArgs e)
{
    if (/* Some condition */)
    {
        e.Appearance.Font = new Font(gridView1.Appearance.Font, FontStyle.Bold);
    }
}

This handler is called every time a cell is rendered, so it can create a large number of Font instances. So I'm wondering about the cost of doing that... I did a few experiments, and it seems that a new HFONT handle is created every time. Should I worry about it ? How big is the impact on resource usage ?

If it has a significant performance impact, shouldn't there be a FontCache class or something similar ?

Note : I know how to solve the problem (I just need to create the font once and reuse it every time), my question is really about the cost of creating many HFONT handles

+5  A: 

Test it; I get about double performance by reuse (in release, reuse = 3000ms, recreate = 4900ms)

using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
static class Program
{
    static void Main()
    {
        Button btn1, btn2;
        Form form = new Form
        {
            Controls = {
                (btn1 = new Button { Dock = DockStyle.Bottom, Text = "reuse" }),
                (btn2 = new Button { Dock = DockStyle.Bottom, Text = "recreate"})
            }
        };
        btn1.Click += delegate
        {
            var watch = Stopwatch.StartNew();
            using (var gfx = form.CreateGraphics())
            using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold))
            {                
                gfx.Clear(SystemColors.Control);
                for (int i = 0; i < 10000; i++)
                {
                    gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152);
                }
            }
            watch.Stop();
            form.Text = watch.ElapsedMilliseconds + "ms";
        };
        btn2.Click += delegate
        {
            var watch = Stopwatch.StartNew();
            using (var gfx = form.CreateGraphics())
            {
                gfx.Clear(SystemColors.Control);
                for (int i = 0; i < 10000; i++)
                {
                    using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold))
                    {
                        gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152);
                    }
                }
            }
            watch.Stop();
            form.Text = watch.ElapsedMilliseconds + "ms";
        };
        Application.Run(form);

    }
}
Marc Gravell
Quite convincing argument... thanks ! Is there any existing font caching mechanism in .NET ? I couldn't find anything like that in System.Drawing...
Thomas Levesque
+2  A: 

Font implements IDisposable - you should make sure you call Dispose when you have finished with it.

It is an unmanaged resource so it is possible that the OS will eventually run out of resources to prodide you with more objects.

Check the GDI handle count in TaskManager to see if it continuously increases.

Matt Breckon
Also, if you're going to be using that Font a lot, make it once and reuse it! No need to continually instanciate it.
Sonny Boy
@Sonny Boy - see the question: > Note : I know how to solve the problem (I just need to create the font once and reuse it every time), my question is really about the cost of creating many HFONT handles
Marc Gravell
+2  A: 

Reuse is almost always faster than on-demand, but your capability to reuse or cache Font objects may vary.

If you are reusing Font objects and associating distinct Font objects with every control, you will increase the chance of overrunning the gdi handle limit. This will harm all applications in use by a given user when it occurs.

Ideally, you should cache Font objects at an application/process level and only the minimum set that is necessary (perhaps a dozen or so at most). Also, caching a small shared set of fonts and other GDI objects allows you to do a better job of handling tricky messages like WM_SETTINGCHANGE. In your sample, you are modifying the defaults, and you may encounter drawing bugs when a User changes schemes or the default display font in windows. During WM_SETTINGCHANGE, you would consider releasing your cached copies and initializing or preparing new sets of font objects.

WM_SETTINGCHANGE (Windows) @ MSDN

If you have the need to have hundreds of varied fonts (or other gdi objects) available, you should not cache fonts and only create what you need on demand. This is not performance friendly, but it will help your application co-exist with other applications a user may have open. Holding hundreds of GDI objects will greatly increase the likelyhood of a user encountering the GDI handle limit. However, A reference-count based cache may provide a trade-off here that works best for you, holding hard copies of only the most frequently used variants.

Finally, as mentioned by Matt Breckon, make use of IDisposable for all the GDI objects that have it available. This will help you avoid gdi handle leaks.

meklarian
+1, good point about WM_SETTINGCHANGE if I implement a cache mechanism...
Thomas Levesque