Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Change of Height and Location of CHeaderCrtrl’s and Heights of Lines of CListCtrl’s Descendants with a Help HDM_LAYOUT and WM_MEASUREITEM Messages

0.00/5 (No votes)
22 Jun 2009 1  
The decision of problem of the header «deformation» and its locality, height of lines of list control and their text formatting.

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.

image001.jpg

Fig. 1. Typical CListCtrl’s descendant with data and gridlines.

image003.jpg

Fig. 2. The same CListCtrl’s descendant without the gridlines.

image005.jpg

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.

image007.jpg

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:

/////////////////////////////////////////////////////////
// OnLayout
/////////////////////////////////////////////////////////
LRESULT CHeaderCtrlEx::OnLayout(WPARAM, LPARAM lParam) {
    LPHDLAYOUT pHL = reinterpret_cast<LPHDLAYOUT>(lParam);

    //*** The table list rectangle
    RECT *pRect = pHL->prc;

    //*** The table header rectangle
    WINDOWPOS *pWPos = pHL->pwpos;

    //*** Here's equivalent code for the code which follows after
    /*
    pWPos->hwndInsertAfter = NULL;

    //*** Moves the table header to the right
    pWPos->x = pRect->left + m_nHdrWidthDefect;
    pWPos->y = pRect->top;
    pWPos->cx = pRect->right - pRect->left;

    //*** New table header height
    pWPos->cy = m_nHdrHeight + m_nHdrHeightDefect;

    pWPos->flags = SWP_NOACTIVATE|SWP_NOZORDER;

    //*** Decreases the table list height on the table header height
    pRect->top += m_nHdrHeight;

    return TRUE;
    */
    //*** Sends HDM_LAYOUT message to the base class

    int nRet = CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, lParam);

    //*** Moves the table header to the right
    pWPos->x += m_nHdrWidthDefect;

    //*** New table header height
    pWPos->cy = m_nHdrHeight + m_nHdrHeightDefect;

    //*** Decreases the table list height on the table header height
    pRect->top = m_nHdrHeight;

    return nRet;
}  // OnLayout

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:

/////////////////////////////////////////////////////////////////////////////
// MeasureItem
/////////////////////////////////////////////////////////////////////////////
void CListCtrlEx::MeasureItem(LPMEASUREITEMSTRUCT pMIS) {
            //*** The table list height
            pMIS->itemHeight = m_nListHeight;
}  // MeasureItem

Text Formatting

It’s a little more difficult to determine the text formatting at our discretion. Here’s the CHeaderCtrlEx::DrawItem code:

///////////////////////////////////////////////////////////////////////////// 
// DrawItem 
///////////////////////////////////////////////////////////////////////////// 

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;  // Handle to device context 
    pDC = CDC::FromHandle(hDC); 

    //*** Selects necessary font 
    pDC->SelectObject(m_pFont); 
    //pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT)); 

    int x = 0;  // x-coordinate of reference point 
    int y = 0;  // y-coordinate of reference point 

    UINT nOptions = 0;  // Text-output options ETO_CLIPPED|ETO_OPAQUE 
    RECT *pIRect = NULL;  // Optional clipping and/or opaquing rectangle 

    pIRect = &pDIS->rcItem; 
    SIZE Size = {0}; 

    //*** Gets the header cell sizes 
    if(!GetTextExtentPoint(hDC, szBuf, wcslen(szBuf), &Size)) { 
        _M("Failed to call GetTextExtentPoint for table header!"); 
        return; 
    } 

    // x-coordinate of reference point 
    x = (pIRect->left + pIRect->right - Size.cx)/2 - 1; 
    x = (x < pIRect->left + 2) ? pIRect->left + 2 : x; 

    // y-coordinate of reference point 
    y = (pIRect->bottom - pIRect->top - Size.cy)/2 - 1; 

    // Specifies that the current background color fills the rectangle pIRect 

    nOptions |= ETO_CLIPPED; 

    //*** Decreases the text border to the right 
    pIRect->right -= 4; 

    //*** Writes the text in the (x, y) - coordinates 
    pDC->ExtTextOut(x, y, nOptions, pIRect, szBuf, wcslen(szBuf), NULL); 

    //*** Restores system font 
    pDC->SelectStockObject(SYSTEM_FONT); 
}  // DrawItem

And here’s the most substantial code for the list text formatting:

///////////////////////////////////////////////////////////////////////////// 
// SetColItemText 
///////////////////////////////////////////////////////////////////////////// 
void CListCtrlEx::SetColItemText(CDC *pDC, CString& stColText,  CRect& TextRect,
    UINT nJustify) 
{
    int x = 0;  // x-coordinate of reference point 
    int y = 0;  // y-coordinate of reference point 
    UINT nOptions = 0;  // Text-output options ETO_CLIPPED|ETO_OPAQUE 

    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; 
    } 

    // Align the text in the whole table 
    CRect TmpRect(TextRect); 

    // x-coordinate of reference point 
    x = (TextRect.left + TextRect.right - Size.cx)/2 - 1; 
    x = (x < TextRect.left + 2) ? TextRect.left + 2 : x; 

    // y-coordinate of reference point 
    y = (TextRect.bottom - TextRect.top - Size.cy)/2 - 1; 

    //*** Specifies that the current background color fills the rectangle 
    nOptions |= ETO_OPAQUE; 

    //*** Draw the background fast 
    pDC->ExtTextOut(TextRect.left, TextRect.top, nOptions, TextRect, NULL, 0, NULL); 

    TmpRect.left++;  // Cosmetic 
    TmpRect.top += y;      // y-coordinate of reference point 
    TmpRect.InflateRect(-3, 0);   // Text does not touch borders 

    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; 
    } 

    //*** Writes the text in the TmpRect 
    ::DrawText(hDC, stColText, nTextLen, TmpRect, nFormat); 
}  // SetColItemText

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here