2
\$\begingroup\$

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 ?

\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 12, 2012 at 12:02
  • 2
    \$\begingroup\$ You should use the unicode character ellipsis , and not three dots. You might also be interested in gist.github.com/4250125 \$\endgroup\$
    – ANeves
    Commented Dec 12, 2012 at 13:25

1 Answer 1

10
\$\begingroup\$

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

\$\endgroup\$
3
  • \$\begingroup\$ Thank you very much for your hard work. This help me more than I expected =) \$\endgroup\$ Commented Dec 12, 2012 at 16:33
  • \$\begingroup\$ Now I just need to figure out a way to do this as a method. \$\endgroup\$ Commented Dec 12, 2012 at 16:37
  • 1
    \$\begingroup\$ @MichelAyres see updated answer for better results :) \$\endgroup\$
    – almaz
    Commented Dec 12, 2012 at 17:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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