Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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 ?

share|improve this question
    
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. –  Michel Ayres Dec 12 '12 at 12:02
2  
You should use the unicode character ellipsis , and not three dots. You might also be interested in gist.github.com/4250125 –  ANeves Dec 12 '12 at 13:25
add comment

1 Answer

up vote 8 down vote accepted

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 :).

share|improve this answer
    
Thank you very much for your hard work. This help me more than I expected =) –  Michel Ayres Dec 12 '12 at 16:33
    
Now I just need to figure out a way to do this as a method. –  Michel Ayres Dec 12 '12 at 16:37
1  
@MichelAyres see updated answer for better results :) –  almaz Dec 12 '12 at 17:17
    
Damn, I can't +1 you again =P Thanks =D –  Michel Ayres Dec 12 '12 at 17:20
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.