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!



Relevant Pages

  • Re: Fast text insert into textbox
    ... red-lines my CPU and, even if I limit the TextBox to just 30KB of text, ... Or, rather, why is updating the control /so/ slow, compared to hacking ... Dim iLen As Integer = .Text.Length + sText.Length ... strings which ends with VBCRLF (probably automaticly because you retrieve ...
    (microsoft.public.dotnet.languages.vb)
  • Re: Find The Text :-)
    ... personally I would suggest writing your own "wrap" code anyway because you will get much more control that way and much more flexibility, including the ability to do things that the DrawText API cannot do. ... If you don't fancy writing the "wrap" code yourself then a very simple alternative would be to use a borderless standard VB TextBox Control and set its size to the same as your required "wrap rectangle" and just dump your string into its Text property. ... You can then send it an EM_GETLINECOUNT to count the number of wrapped lines and EM_GETLINE messages to get those lines into a VB array of Strings. ... Private Declare Function SendMessage Lib "user32" _ ...
    (microsoft.public.vb.general.discussion)
  • Re: DragDrop bei Textbox
    ... Zeichen Position innerhalb eines Strings anhand von Koordinaten zu bestimmen ... Die Position innerhalb des Strings in der Textbox kannst du durch Senden der ...
    (microsoft.public.de.german.entwickler.dotnet.csharp)
  • RE: textbox insertion point.
    ... 'Put the contents of the selected cell in the Pinyin form textbox. ... In this example "Pinyin Input" is the title of my userform, ... After the code runs to insert the predefined strings, ...
    (microsoft.public.excel.programming)
  • Re: TextBox - Character under Mouse
    ... talkin about is measuring the character width for a particular character and ... which represent the characters underneath the textbox for display & select ... and use the textbox for edit, on change the label sets alters? ... I was thinking about aproaching it by> measuring the string and working it out that way. ...
    (microsoft.public.dotnet.languages.vb)