Re: Measuring Text in TextBox - MeasureString "falls short"!

From: Craig A. (anonymous_at_devdex.com)
Date: 09/29/04


Date: Tue, 28 Sep 2004 19:32:51 -0700


Hi Craig,

This thread has been very helpful! I too was struggling with creating
an auto-resize TextBox. I attempted to use your sample program and
quickly ran into problems related to accurately measuring strings
destined for the dreaded TextBox.

I thought I would reply with a solution I discovered. I realize you may
have moved on to more exciting things, but here goes anyways...

Turns out you can't use .Net to accurately measure strings for controls
that use GDI to do there own drawing (such as the Textbox). While
MeasureCharacterRanges does offer a better "estimate" of the string
length than MeasureString, it does not measure strings the same as it is
measured internally to the TextBox control. This is because .Net goes
through great pains to be resolution independent (see
http://windowsforms.net/articles/gdiptext.aspx).

I finally concluded that I must call the old win32 GDI functions in
order to make things work. To do this, define the following somewhere
in your AutoHeightControl:

[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
        public int cx;
        public int cy;
}

private const int EM_SETMARGINS = 0xD3;
private const int EM_GETMARGINS = 0xD4;
private const int EC_LEFTMARGIN = 0X01;
private const int EC_RIGHTMARGIN = 0X02;

[DllImport("USER32.DLL", EntryPoint= "SendMessage")]
public static extern int SendMessage(
                                 IntPtr hwnd,
                                 int msg,
                                 int wParam,
                                 int lParam);

[DllImport("gdi32.dll",EntryPoint= "GetTextExtentPoint32")]
public static extern int GetTextExtentPoint32(
                                  IntPtr hdc,
                                  string text,
                                  int textLength,
                                  ref SIZE size);
                        
[DllImport("gdi32.dll", EntryPoint= "SelectObject")]
public static extern IntPtr SelectObject(
                                 IntPtr hdc, IntPtr hgdiobj);

I'm importing SendMessage because I also wanted to auto-size the width
of the control and I needed a way to zero-out the margins. To zero the
left and right margins place the following in your OnTextChanged
handler:

AutoSizeTextBox.SendMessage( this.Handle,
                      AutoSizeTextBox.EM_SETMARGINS,
                      3, // EC_LEFTMARGIN | EC_RIGHTMARGIN
                      0);

Here is my routine that measures the contents of the TextBox using the
native GDI routines:

protected Size MeasureDisplayString(
                         Graphics graphics,
                         Font font)
{
        SIZE gdiSize = new SIZE();
        int width = 0;
        int height = 0;

        /*
         * Measure each line - keeping track of the widest
         * line and accumulating the overall height
         */
        foreach (string line in this.Lines)
        {
                string text = line;

                if (text == string.Empty)
                {
                   text = " ";
                }

                gdiSize.cx = 0;
                gdiSize.cy = 0;
                IntPtr hdc = graphics.GetHdc();
                IntPtr hOldFont = SelectObject(hdc,
                                          font.ToHfont());
                GetTextExtentPoint32(hdc,
                                     text,
                                     text.Length,
                                     ref gdiSize);

                SelectObject(hdc, hOldFont);
                graphics.ReleaseHdc(hdc);

                if (gdiSize.cx > width)
                {
                        width = gdiSize.cx;
                }
                height += gdiSize.cy;
        }

        return new Size(width, height);
}

Well, thats about it. It would be interesting to use this same approach
with the RichTextBox control - especially, with RTF strings - multiple
fonts, bullets, etc.

Good luck,

Craig Anderson

*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!