Monday, April 27, 2009

Setting a Win32 Control's Font and Sizing It to Fit the Text

Copyright © 2009, Steven E. Houchin
In my latest application, I have a Static control that I use as a label at the top of a rectangular box. I wanted to change the font, then resize the control to fit the text exactly. First, I needed to set the font. This is done by initializing a LOGFONT structure for the desired font family and size. The code below speicifies the minimum LOGFONT fields necessary:
LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 12;
_tcscpy(lf.lfFaceName, _T("MS Sans Serif"));
Now that we have a LOGFONT, a Win32 font object needs to be created and associated with the control:
// the LOGFONT must have some valid data in it
HFONT hFont = CreateFontIndirect(&lf);
if ((HFONT)0 != hFont)
{
     SetWindowFont(hCtrl, hFont, TRUE);
}
The SetWindowFont call is really a macro that expands into a call to SendMessage that specifies the WM_SETFONT message. At this point, when the control is redrawn, it will use the new font.
Now, we need to resize the control so it fits exactly the text it contains. Let's assume the variable lpszText points to our null-terminated text string that the Static control already displays. We must call the Win32 GetTextExtentPoint32 function to get the dimensions of the text. This function requires the device context (DC) of the control. And, to make sure we're getting the proper dimensions, our new font must be selected into the device context.
HDC hdc = GetDC(hCtrl); // get the static control's DC
HFONT hfOld = reinterpret_cast<hfont>(SelectObject(hdc, hFont));
DeleteObject(hfOld);    // free the old font handle

// measure the text height and width
size_t iLen = _tcslen(lpszText);
SIZE s;
if (GetTextExtentPoint32(hdc, m_pszLabelText, static_cast<int>(iLen), &s))
{
     // control's x,y position set by CreateWindow
     POINT p = this->ptControlOrigin;
     MoveWindow(hCtrl, p.x, p.y, s.cx, s.cy,
     FALSE /* no repaint now */);
}
ReleaseDC(hCtrl, hdc); // free the DC
That completes the task. We've changed the control's font to 12 point "MS Sans Serif" and have resized the control to fit the control's text string exactly. If the control's text string is changed, then the calls to GetTextExtentPoint32 and MoveWindow must be repeated for the new string's length.