Formulation of Problem and Methods of its Decision
I do not know if it is possible to explain such a fact that header of CHeaderCtrl
/ SysHeader32
of standard CListCtrl
/ SysListView32
list of the Windows appears with some distortion that strikes the eyes at once. It does for the impression from the programs which use these controls. Usually the lines of header fall behind on 1 pixel from the lines of list and header for some reason on 2 pixels shorter, then would follow. To hide this effect, often the lines of list are taken away. It is good evidently in fig. 1-3.
Fig. 1. Typical CListCtrl’s descendant with data and gridlines.
Fig. 2. The same CListCtrl’s descendant without the gridlines.
Fig. 3. Enlarged CListCtrl’s descendant with data and gridlines.
Besides the decision of problem of the header «deformation» it will be interestingly to us arbitrarily to change its height and height of lines of list control, regardless of sizes of current font and image icon present in columns or rows of the list. Thus, we wish to decide these questions in the most elegant method. We also wish to use preset fonts and text formatting in the header and list cells. Except for it, demonstration example, for a typical list, we will fill with data, quite ordinarily via InsertItem
/ SetItem
, and for the modified list will use the virtual mode (LVS_OWNERDATA
style), with the help of the SetItemCount
function and LVN_GETDISPINFO
message.
A job performance is presented in fig. 4. We will notice that although we wished for typical CListCtrl
’s descendant to even its first column «Ind. No.» on a right edge, this control ignored our «order» and equalized a column on the left edge (fig. 1-3). And only for the modified control we were able to obtain the necessary alignment (fig. 4). In addition, we showed that it is possible to change data formatting.
Fig. 4. Modified CHeaderCtrl’s / CListCtrl’s descendants with data and gridlines.
If we change the height of header due to an enlargement font or image icons, indicated «deformation» nevertheless, saved. Obviously, that it is «property» of not MFC’s CListCtrl
class but of the SysListView32
Windows’ control.
To liquidate «deformation» of header, it is needed additionally to displace it to the right on a 1 pixel and to increase its height on 2 pixels, desirably without the obvious bringing in of such functions as MoveWindow
/ SetWindowPos
, and also without the necessity to do the own drawing of CHeaderCtrl
’s / CListCtrl
’s descendants in functions like OnPaint
. Except for it, we do not want to use the SetFont
function not on purpose (only for an header enlargement, with the subsequent resize of his font). The same touches image icons which we wish to use exceptionally on purpose too, but not for a change only of sizes of header or lines (say, due to the use of thin icons of decision height and not distinctive from a background). There is yet a variant recreate header on a base CHeaderCtrl::Create
function, which assumes the obvious pointing of sizes and his location, but this variant also does not arrange as too bulky.
Thus, we have one variant for the change of height and locality of header (with a help HDM_LAYOUT
message) and one variant of change of height of lines of list actually (with a help WM_MEASUREITEM
message). A method with HDM_LAYOUT
is interesting and because very often programmers use it incorrectly, that sometimes results in a surplus code or incorrect work of a program. We will show that it is possible to format a text in the cells of list and his header.
HDM_LAYOUT Message
To get and process the HDM_LAYOUT
message of a table header, it is necessary to define the function:
afx_msg LRESULT OnLayout(WPARAM wparam, LPARAM lparam)
in the HeaderCtrlEx.h header file. Then in the proper HeaderCtrlEx.cpp file to specify a macro in the messages map:
ON_MESSAGE(HDM_LAYOUT, OnLayout)
and to define the proper OnLayout
function:
LRESULT CHeaderCtrlEx::OnLayout(WPARAM, LPARAM lParam) {
LPHDLAYOUT pHL = reinterpret_cast<LPHDLAYOUT>(lParam);
RECT *pRect = pHL->prc;
WINDOWPOS *pWPos = pHL->pwpos;
int nRet = CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, lParam);
pWPos->x += m_nHdrWidthDefect;
pWPos->cy = m_nHdrHeight + m_nHdrHeightDefect;
pRect->top = m_nHdrHeight;
return nRet;
}
Actually, here are two variants of change of sizes and locality of header of CHeaderCtrlEx
class without evident using
functions of kind of SetFont
/ MoveWindow
/ SetWindowPos
/ CHeaderCtrlEx::Create
or using pseudo image icons (for achievement of the same aims). Not looking on seeming simplicity of our CHeaderCtrlEx::OnLayout
function, many programmers assume errors in processing HDM_LAYOUT
message. Possibly, it is caused from not enough clear description of LPHDLAYOUT
structure in MSDN.
WM_MEASUREITEM Message
Like the previous one, we determine function:
afx_msg void MeasureItem(LPMEASUREITEMSTRUCT pMIS)
in the ListCtrlEx.h header file. Then in the proper ListCtrlEx.cpp file, we specify a macro in the messages map:
ON_WM_MEASUREITEM_REFLECT()
and determine the proper MeasureItem
function:
void CListCtrlEx::MeasureItem(LPMEASUREITEMSTRUCT pMIS) {
pMIS->itemHeight = m_nListHeight;
}
Text Formatting
It’s a little more difficult to determine the text formatting at our discretion. Here’s the CHeaderCtrlEx::DrawItem
code:
void CHeaderCtrlEx::DrawItem(LPDRAWITEMSTRUCT pDIS) {
HDITEM hDI;
TCHAR szBuf[MAXITEMTEXT];
hDI.mask = HDI_TEXT;
hDI.pszText = szBuf;
hDI.cchTextMax = MAXITEMTEXT;
GetItem(pDIS->itemID, &hDI);
CDC *pDC;
HDC hDC = pDIS->hDC; pDC = CDC::FromHandle(hDC);
pDC->SelectObject(m_pFont);
int x = 0; int y = 0;
UINT nOptions = 0; RECT *pIRect = NULL;
pIRect = &pDIS->rcItem;
SIZE Size = {0};
if(!GetTextExtentPoint(hDC, szBuf, wcslen(szBuf), &Size)) {
_M("Failed to call GetTextExtentPoint for table header!");
return;
}
x = (pIRect->left + pIRect->right - Size.cx)/2 - 1;
x = (x < pIRect->left + 2) ? pIRect->left + 2 : x;
y = (pIRect->bottom - pIRect->top - Size.cy)/2 - 1;
nOptions |= ETO_CLIPPED;
pIRect->right -= 4;
pDC->ExtTextOut(x, y, nOptions, pIRect, szBuf, wcslen(szBuf), NULL);
pDC->SelectStockObject(SYSTEM_FONT);
}
And here’s the most substantial code for the list text formatting:
void CListCtrlEx::SetColItemText(CDC *pDC, CString& stColText, CRect& TextRect,
UINT nJustify)
{
int x = 0; int y = 0; UINT nOptions = 0;
int nTextLen = stColText.GetLength();
HDC hDC = pDC->m_hDC;
SIZE Size = {0};
if(!GetTextExtentPoint(hDC, stColText, nTextLen, &Size)) {
_M("Failed to call GetTextExtentPoint for table list!");
return;
}
CRect TmpRect(TextRect);
x = (TextRect.left + TextRect.right - Size.cx)/2 - 1;
x = (x < TextRect.left + 2) ? TextRect.left + 2 : x;
y = (TextRect.bottom - TextRect.top - Size.cy)/2 - 1;
nOptions |= ETO_OPAQUE;
pDC->ExtTextOut(TextRect.left, TextRect.top, nOptions, TextRect, NULL, 0, NULL);
TmpRect.left++; TmpRect.top += y; TmpRect.InflateRect(-3, 0);
UINT nFormat = 0;
switch(nJustify & LVCFMT_JUSTIFYMASK) {
case LVCFMT_LEFT:
nFormat = DT_LEFT;
break;
case LVCFMT_RIGHT:
nFormat = DT_RIGHT;
break;
case LVCFMT_CENTER:
nFormat = DT_CENTER;
break;
default:
_M("CListCtrlEx: Error of the text formatting!");
return;
}
::DrawText(hDC, stColText, nTextLen, TmpRect, nFormat);
}
The examples of realization of the virtual mode of list are known well, therefore I will not expand on them.
History
- 22nd June, 2009: Initial post