using API functions, can i get the text rectangule?

Dani AI

Generated

Good question. The trick is to ask GDI to format the text the same way the control will, then use that formatted rectangle to resize the window. is right about using the CALCRECT path; where people get tripped up is tabs, word-wrapping, prefixes, and overhangs. Use DrawTextEx so you can pass DRAWTEXTPARAMS and the right flags; Windows will return the exact formatted text box without drawing anything. See notes on the flags and defaults in the docs. DrawText. DrawTextEx.

Example (measure, then resize). This handles CR/LF, expands tabs, and avoids mnemonic processing. No magic +2 needed; AdjustWindowRectEx converts client size to window size.

RECT rc = {0, 0, maxWidth /* 0 for single line */, 0};
DRAWTEXTPARAMS dtp{ sizeof(dtp) /* iTabLength uses avg char widths */ };
HDC hdc = GetDC(hwnd);
HFONT hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
HGDIOBJ old = SelectObject(hdc, hFont);

DrawTextEx(hdc, text, -1, &rc,
    DT_CALCRECT | DT_EXPANDTABS | DT_WORDBREAK | DT_EDITCONTROL | DT_NOPREFIX,
    &dtp);

// Optional: add tm.tmOverhang for italic/antialiased fonts.
TEXTMETRIC tm; GetTextMetrics(hdc, &tm); rc.right += tm.tmOverhang;

SelectObject(hdc, old);
ReleaseDC(hwnd, hdc);

RECT wr = rc;
AdjustWindowRectEx(&wr,
    (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE), FALSE,
    (DWORD)GetWindowLongPtr(hwnd, GWL_EXSTYLE));
SetWindowPos(hwnd, nullptr, 0, 0, wr.right - wr.left, wr.bottom - wr.top,
    SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

Why your tabs felt too wide: DT_EXPANDTABS uses 8-character tab stops by default. If you need a different stop, DrawText’s DT_TABSTOP cannot be combined with DT_CALCRECT, so either pre-expand the tabs yourself or measure with GetTabbedTextExtent/TabbedTextOut using your desired stops before calling DrawText for height. DrawText flags. GetTabbedTextExtent. TabbedTextOut.

Two final tips:

  • Do not delete a font you did not create; if you create and set a font with WM_SETFONT, delete it when you no longer need it. WM_SETFONT.
  • For push buttons, let the OS size it: send BCM_GETIDEALSIZE. BCM_GETIDEALSIZE.

Recommended Answers

All 11 Replies

Not that I know of, but you can get the text itself. The size of the rectangle would depend on the font being used.

Not sure exactly what you want, but I guess you can get the text displayed within the window and the size of the window easily enough. This should give you enough information to call DrawText. If you include DT_CALCRECT in the uFormat parameter this won't actually draw the text but instead will calculate the size of the drawn text. I think that is what you need.

imagine the text is more big than control. i want resize the control until see the text. i use the:
- GetWindowText() for get the text;
- SendMessage() for get the font and then select the object;
- GetTextExtentPoint32() for get the font lengh in pixels;
- GetWindowRect() for get the window position(and size);
- GetWindowLongPtr() for get the Styles and Extended Styles;
- AdjustWindowRectEx() for get the control size with borders;
- SetWindowPos() for change the window size.
these code works fine, but imagine that the text have '\t' or '\n', by some reason these code didn't work for the right size. but if is just a single text line and without '\t', works fine.
what you can advice me, please?

It sounds exactly what DrawText with DT_CALCRECT is made for. From MSDN:

Determines the width and height of the rectangle. If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the rectangle to bound the last line of text. If the largest word is wider than the rectangle, the width is expanded. If the text is less than the width of the rectangle, the width is reduced. If there is only one line of text, DrawText modifies the right side of the rectangle so that it bounds the last character in the line. In either case, DrawText returns the height of the formatted text but does not draw the text.
It does mean that you should get the width roughly right first. To do that you might use GetTextExtentPoint32 first to find the width of drawn all on 1 line. If that is too much to fit on 1 line, set the control width to the maximum you can accept and then use DrawText to calculate the required control height.

If you need to be more creative with the way you adjust the control size, such as maintaining aspect ratio, then I guess you need an iterative approach to home in on the optimum size. An initial calculation based on text area using the height & width from GetTextExtentPoint32 might get you close.

sorry SalmiSoft, but i'm very confuse:(
because i only know that the DrawText() is for show us a text on control\hdc.
so please give me more information

i did a new search i did these code:

void setAutoSize(bool autosize)
    {
        if (autosize==true)
        {
            char a[256];
            GetWindowText(this->hwnd,a,256);
            RECT c = { 0, 0, 0, 0 };            
            DrawText(GetDC(hwnd), a, strlen(a), &c, DT_CALCRECT);
            LONG s=GetWindowLongPtr(hwnd,GWL_EXSTYLE);
            LONG g=GetWindowLongPtr(hwnd,GWL_STYLE);
            AdjustWindowRectEx (&c,g,FALSE,s );
            SetWindowPos(hwnd, 0, 0, 0, c.right+2, c.bottom+2,
            SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|
            SWP_DRAWFRAME | SWP_FRAMECHANGED);
        }
    }

but i see 2 problems:
1 - the "\t" is ignored;
2 - i must add '+2'... why? for show the last character correctly.

what you can tell me about these?

The "\t" is ignored because you haven't specified that tabs should be expanded. You will need DT_EXPANDTABS as well as DT_CALCRECT.
I think the +2 is needed because AdjustWindowRectEx sets the window size including the border (and caption bar and menu bar). Does your window have a border width of 1 (so 2 for 2 sides)?

thanks for all.
but can i avoid the '+2'???(i think, depending on fontsize or even anotherthing, the '+2' can be less than we need)

You will have to add something, but of course it is good to avoid hard-coding "+2" and as you say the amount to add could vary. I think this will do the trick:

    void setAutoSize(bool autosize)
        {
            if (autosize==true)
            {
                char a[256];
                GetWindowText(this->hwnd,a,256);
                RECT c = { 0, 0, 0, 0 };            
                DrawText(GetDC(hwnd), a, strlen(a), &c, DT_CALCRECT);
                LONG s=GetWindowLongPtr(hwnd,GWL_EXSTYLE);
                LONG g=GetWindowLongPtr(hwnd,GWL_STYLE);
                AdjustWindowRectEx (&c,g,FALSE,s );
                SetWindowPos(hwnd, 0, 0, 0, c.right+2*GetSystemMetrics(SM_CXSIZEFRAME), c.bottom+2*GetSystemMetrics(SM_CYSIZEFRAME),
                SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|
                SWP_DRAWFRAME | SWP_FRAMECHANGED);
            }
        }

anotherthing: if we use DT_EXPANDTABS with DT_CALCRECT, the rectangle size include the tab size too:

DrawText(GetDC(hwnd), a, strlen(a), &c, DT_CALCRECT | DT_EXPANDTABS);

sorry.. it's give me much more big than i need :(

heres the code corrected for give us the text rectangule and more 2(for we never lose 1 or 2 pixles of the letter):

//Getting the DC Font amd select it
            HDC hdc = GetDC(hwnd);
            HFONT hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
            HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);


            //Getting the
            RECT c = { 0, 0, 0, 0 };

            //Getting the text rectangle from actual caption
            char a[256];
            GetWindowText(this->hwnd,a,256);
            DrawText(hdc, a, strlen(a), &c, DT_CALCRECT | DT_EXPANDTABS);

            //add 2 to the size
            TEXTMETRIC v;
            GetTextMetrics (hdc,&v);
            c.right=c.right+(v.tmAveCharWidth / 2);
            c.bottom=c.bottom+(v.tmAveCharWidth / 2);

            //Clean the Font DC
            ReleaseDC(hwnd,hdc);
            DeleteFont(hOldFont);
            DeleteFont(hFont);

            //Getting the actual styles
            LONG s=GetWindowLongPtr(hwnd,GWL_EXSTYLE);
            LONG g=GetWindowLongPtr(hwnd,GWL_STYLE);

            //change the rectangle size for borders
            AdjustWindowRectEx (&c,g,FALSE,s );

            //Update the control            
            intWidth=c.right-intLeft;
            intHeight=c.bottom-intTop;            
            SetWindowPos(hwnd, 0, 0, 0, c.right, c.bottom,
                SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE |SWP_NOCOPYBITS);

these code is excelent for do autosize of the control... very cool

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.