# How can I make this code (that is usually in a loop) faster and efficiently?

When I have a place with limited space, where I entry a text that can be few letters or huge amount of text because it's dynamic, I'm using this code to make the text fit the specific space.

#region TextSizing
string textSizing = databaseTable.Description;
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
{
size = g.MeasureString(textSizing, new System.Drawing.Font("Arial", 14)).Width;
}

if (size < 1500)
lblText.Text = textSizing;
else
lblText.Text = textSizing.Length > 135 ? textSizing.Remove(135) + "..." : textSizing;
#endregion


A friend of mine suggested that I should use cache to store the Font and the Graphics, I didn't understand it very well, but keep thinking about it... How can I make this code (that is usually in a loop) faster and efficiently ?

• Not sure how to put a good title for this question, so please feel free to change it so it can be fit more the Q&A format. thanks. Dec 12 '12 at 12:02
• You should use the unicode character ellipsis …, and not three dots. You might also be interested in gist.github.com/4250125 Dec 12 '12 at 13:25

Cache the instance of Graphics and Font objects so that you don't need to create them each time you measure the length of string. I've created a small test to see which part takes most of the time:

private static void Main()
{
Stopwatch stopwatch = Stopwatch.StartNew();

stopwatch.Restart();
for (int i = 0; i < 1000000; i++)
{
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
{
var size = g.MeasureString("asdasdf", new System.Drawing.Font("Arial", 14)).Width;
}
}
Console.WriteLine("Current solution - create a new Graphics per measure: {0} milliseconds", stopwatch.ElapsedMilliseconds);

stopwatch.Restart();
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
{
for (int i = 0; i < 1000000; i++)
{
var size = g.MeasureString("asdasdf", new System.Drawing.Font("Arial", 14)).Width;
}
}
Console.WriteLine("Measure the string with Graphics cached: {0} milliseconds", stopwatch.ElapsedMilliseconds);

stopwatch.Restart();
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
using (var font = new System.Drawing.Font("Arial", 14))
{
for (int i = 0; i < 1000000; i++)
{
var size = g.MeasureString("asdasdf", font).Width;
}
}
Console.WriteLine("Measure the string with Graphics and font cached: {0} milliseconds", stopwatch.ElapsedMilliseconds);
}


Results on my computer are:

Current solution - create a new Graphics per measure: 26864 milliseconds
Measure the string with Graphics cached: 6588 milliseconds
Measure the string with Graphics and font cached: 803 milliseconds


So by caching Graphics you'll reduce the time consumption by 75%, and by caching both Font and Graphics you get a nice 33.5X performance boost :).

• Thank you very much for your hard work. This help me more than I expected =) Dec 12 '12 at 16:33
• Now I just need to figure out a way to do this as a method. Dec 12 '12 at 16:37
• @MichelAyres see updated answer for better results :) Dec 12 '12 at 17:17