35

This is not a rounding problem. Difference ~ 5+ pixels.

Test Case String: ""MACD (26,12,9) -0.000016"

e.Graphics.MeasureString("MACD (26,12,9) -0.000016", SystemFonts.DefaultFont).Width)
TextRenderer.MeasureText("MACD (26,12,9) -0.000016", SystemFonts.DefaultFont).Width)

The result is always:

139.3942
134

Why is there so much difference in size? I just need the round of width of string outside paint method. But it should match MeasureString or vice versa.

A G
  • 21,087
  • 11
  • 87
  • 112

2 Answers2

74

TextRenderer uses GDI to render the text, whereas Graphics uses GDI+. The two use a slightly different method for laying out text so the sizes are different.

Which one you should use depends on what will eventually be used to actually draw the text. If you are drawing it with GDI+ Graphics.DrawString, measure using Graphics.MeasureString. If you are drawing using GDI TextRenderer.DrawText, measure using TextRenderer.MeasureText.

If the text will be displayed inside a Windows Forms control, it uses TextRenderer if UseCompatibleTextRendering is set to false (which is the default).

Reading between the lines of your question, you seem to be using TextRenderer because you don't have a Graphics instance outside the Paint event. If that's the case, you can create one yourself to do the measuring:

using( Graphics g = someControl.CreateGraphics() )
{
    SizeF size = g.MeasureString("some text", SystemFonts.DefaultFont);
}

If you don't have access to a control to create the graphics instance you can use this to create one for the screen, which works fine for measurement purposes.

using( Graphics g = Graphics.FromHwnd(IntPtr.Zero) )
{
     SizeF size = g.MeasureString("some text", SystemFonts.DefaultFont);
}
Sven
  • 21,903
  • 4
  • 56
  • 63
  • For all other strings I have tested the size is accurate for both. The question is why is it not accurate in this 'specific' case. Framework does not allow to use someControl.CreateGraphics(). There is not control available. – A G Jul 15 '11 at 09:37
  • +1, see also http://stackoverflow.com/questions/6107280/textrenderer-with-graphics-transform – takrl Jul 15 '11 at 10:37
  • @Aseem I have almost *always* had the two return pretty wildly-differing results in my experience. I have since deleted the code, but at one point I had three measuring methods drawing colored boxes on top of text where it thought the bounds were. All three were different in almost all cases. – Shibumi Jul 15 '11 at 13:06
  • 3
    @Aseem: it's honestly pretty normal for `TextRenderer.MeasureText` and `Graphics.MeasureString` to return different results. And if you have no control instance, you can create a `Graphics` instance for the entire screen; I've updated my answer to that effect. – Sven Jul 15 '11 at 14:37
  • 1
    This blog post explains why .Net by default is using GDI instead of GDI+ for text rendering: http://blogs.msdn.com/b/jfoscoding/archive/2005/10/13/480632.aspx – RenniePet Jun 24 '12 at 02:24
2

I Made the following class to use MeasureString outside the paint event, and it works pretty well.

public interface ITextMeasurer
    {
        SizeF MeasureString(string text, Font font, StringFormat format);
    }

    public class TextMeasurer : ITextMeasurer
    {
        private readonly Image _fakeImage;
        private readonly Graphics _graphics;

        public TextMeasurer()
        {
            _fakeImage = new Bitmap(1, 1);
            _graphics = Graphics.FromImage(_fakeImage);
        }

        public SizeF MeasureString(string text, Font font, StringFormat format)
        {
            return _graphics.MeasureString(text, font, int.MaxValue, format);
        }
    }
A G
  • 21,087
  • 11
  • 87
  • 112
  • 20
    You can create a Graphics instance if you don't have a control using `Graphics.FromHwnd(IntPtr.Zero)`, there is no need to create a bitmap. Also, your class encapsulates two disposable resources but is not itself disposable, which is bad practice. – Sven Jul 15 '11 at 14:39
  • 3
    I'm also wondering what on earth would be the point of that interface. Interfaces are only useful if you expect to make multiple implementations of something. – Nyerguds Nov 24 '15 at 12:24
  • 2
    They are also useful when the interface and the implementation are in different libraries, and you can reference the library with interface, but not with the implementation (e.g. code injection). – Mike Oct 30 '17 at 09:02