Click here to Skip to main content
15,891,828 members
Articles / Desktop Programming / MFC
Article

A Multiline Header Control Inside a CListCtrl

Rate me:
Please Sign up or sign in to vote.
4.93/5 (13 votes)
16 May 2000 430.5K   7.5K   74   67
How to make the CListCtrl's header Multiline
  • Download source files - 18.8 Kb
  • Download demo project - 8 Kb

    Sample Image - HeaderCtrlEx.jpg

    First of all I have to mention that Alon Peleg helped me find the solution to the problem so I feel it is only fair that his name will be mentioned as an author.

    On a recent project I did I had to make the header control of a CListCtrl multiline. This small project show how to do it by subclassing the CHeaderCtrl of the CListCtrl.

    If you want to use this code just the HeaderCtrlExt.h and HeaderCtrlExt.cpp files into your source code.

    In addition on your CListView or CListCtrl derived class, add a member variable of type CHeaderCtrlEx and a member variable of type CFont.

    If you are using a CListCtrl without a view then put the following code in the end of the OnCreate handler of the CListCtrl:

    ///////////////////////SET UP THE MULTILINE HEADER CONTROL
    
    //m_NewHeaderFont is of type CFont
    m_NewHeaderFont.CreatePointFont(190,"MS Serif"); 
    
    CHeaderCtrl* pHeader = NULL;
    pHeader=GetHeaderCtrl();
    
    if(pHeader==NULL)
    	return;
    	
    VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));	
    
    //A BIGGER FONT MAKES THE CONTROL BIGGER
    m_HeaderCtrl.SetFont(&m_NewHeaderFont);
    
    HDITEM hdItem;
    
    hdItem.mask = HDI_FORMAT;
    
    for(i=0; i < m_HeaderCtrl.GetItemCount(); i++)
    {
    	m_HeaderCtrl.GetItem(i,&hdItem);
    
    	hdItem.fmt|= HDF_OWNERDRAW;
    		
    	m_HeaderCtrl.SetItem(i,&hdItem);
    }

    If you are using a CListView or any class derived by it then add the following code in the OnInitialUpdate override of the CListView:

    ///////////////////////SET UP THE MULTILINE HEADER CONTROL
    //m_NewHeaderFont is of type CFont
    m_NewHeaderFont.CreatePointFont(190,"MS Serif"); 
    
    CListCtrl& ListCtrl = GetListCtrl();
    
    CHeaderCtrl* pHeader = NULL;
    pHeader=ListCtrl.GetHeaderCtrl();
    
    if(pHeader==NULL)
    	return;
    	
    VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));	
    
    //A BIGGER FONT MAKES THE CONTROL BIGGER
    m_HeaderCtrl.SetFont(&m_NewHeaderFont);
    
    HDITEM hdItem;
    
    hdItem.mask = HDI_FORMAT;
    
    for(i=0; i < m_HeaderCtrl.GetItemCount(); i++)
    {
    	m_HeaderCtrl.GetItem(i,&hdItem);
    	hdItem.fmt|= HDF_OWNERDRAW;
    		
    	m_HeaderCtrl.SetItem(i,&hdItem);
    }

    The only difference between the two parts of code is way we get a pointer to the Header control.

    Thats it. Enjoy!

  • 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


    Written By
    Team Leader www.unitronics.com
    Israel Israel
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    Questionhow about in FormView? Pin
    TiianyMike3-Jul-11 23:34
    TiianyMike3-Jul-11 23:34 
    GeneralI want both multiline and dynamical update on header. [modified] Pin
    necoyam7-Jul-09 15:38
    necoyam7-Jul-09 15:38 
    GeneralDT_VCENTER not working Pin
    Erakis17-Jul-08 4:03
    Erakis17-Jul-08 4:03 
    Generalmanifest problem Pin
    marsiancba4-Apr-07 3:41
    marsiancba4-Apr-07 3:41 
    GeneralWindows XP theme Pin
    Farid Z13-Dec-06 14:17
    Farid Z13-Dec-06 14:17 
    GeneralRe: Windows XP theme Pin
    byung chul cho12-Jun-08 20:32
    byung chul cho12-Jun-08 20:32 
    Generalleft aligning Pin
    pedroo28-Oct-06 22:32
    pedroo28-Oct-06 22:32 
    QuestionCode in VB.NET Pin
    Rudis_DC25-Aug-06 0:41
    Rudis_DC25-Aug-06 0:41 
    AnswerRe: Code in VB.NET Pin
    Alberto Bar-Noy26-Aug-06 23:27
    Alberto Bar-Noy26-Aug-06 23:27 
    GeneralRe: Code in VB.NET Pin
    Rudis_DC17-Oct-06 1:20
    Rudis_DC17-Oct-06 1:20 
    GeneralRe: Code in VB.NET Pin
    Alberto Bar-Noy17-Oct-06 1:37
    Alberto Bar-Noy17-Oct-06 1:37 
    GeneralRe: Code in VB.NET Pin
    Rudis_DC17-Oct-06 1:43
    Rudis_DC17-Oct-06 1:43 
    Generalit is possible to have multiline column in row not header using win32 api Pin
    kcselvaraj17-Jul-06 4:31
    kcselvaraj17-Jul-06 4:31 
    GeneralRe: it is possible to have multiline column in row not header using win32 api Pin
    PurnimaKC9-Aug-06 20:16
    PurnimaKC9-Aug-06 20:16 
    Generalproblem with delete Pin
    cpicass30-Sep-05 4:45
    cpicass30-Sep-05 4:45 
    GeneralSeem does not work with SDI application Pin
    Member 104710930-Apr-04 3:37
    Member 104710930-Apr-04 3:37 
    GeneralRe: Seem does not work with SDI application Pin
    stephane.boeuf8-Jul-04 4:55
    stephane.boeuf8-Jul-04 4:55 
    GeneralRe: Seem does not work with SDI application Pin
    Rod Stone25-Jan-05 23:57
    Rod Stone25-Jan-05 23:57 
    GeneralThank you! Pin
    Jörgen Sigvardsson27-Feb-06 4:00
    Jörgen Sigvardsson27-Feb-06 4:00 
    QuestionCan be done in plain C ? Pin
    ednilsom16-Feb-04 3:07
    ednilsom16-Feb-04 3:07 
    QuestionIt doesn't work in a dialog? Pin
    wjlcqjy24-Jul-03 15:54
    wjlcqjy24-Jul-03 15:54 
    AnswerRe: It doesn't work in a dialog? Pin
    Jian Ruan29-Aug-03 15:41
    Jian Ruan29-Aug-03 15:41 
    GeneralRe: It doesn't work in a dialog? Pin
    Anonymous15-Oct-03 11:32
    Anonymous15-Oct-03 11:32 
    GeneralRe: It doesn't work in a dialog? Pin
    llllskywalker20-Oct-05 10:50
    llllskywalker20-Oct-05 10:50 
    AnswerRe: It doesn't work in a dialog? Pin
    llllskywalker20-Oct-05 13:09
    llllskywalker20-Oct-05 13:09 
    Good news... I figured out a workaround for getting multiline headers to work in a CListCtrl object. Unfortunately, with the author's code, you can't use OnCreate() or PreSubClassWindow(). However, you can try the following approach, which in some ways makes your list class more usable because you can turn on/off multiline whenever you feel like it.

    In your derived list control class (Example: CMyListCtrl) you need these four member variables in MyListCtrl.h:

    bool m_bInit;
    CFont m_FontSingle;
    CFont m_FontMultiple;
    CCtrlListHeaderEx m_HeaderCtrl; // multiline header control


    Two of these need to be initialized, so in the constructor of your list class (CMyListCtrl), place the following:

    m_bInit = false;
    m_FontMultiple.CreatePointFont(190,"MS Serif"); // 190 for two rows of text


    You will need the CCtrlListHeaderEx class, which is pretty much a tiny class with only one real function, so i just inline it and put it above the list class in MyListCtrl.h:

    // A simple extension to the header class to allow multi-line headers
    class CCtrlListHeaderEx : public CHeaderCtrl
    {
    public:
    CCtrlListHeaderEx() { m_bMultiline = false; }
    virtual ~CCtrlListHeaderEx() { }
    bool m_bMultiline;
    protected:
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) // override
    {
    ASSERT(lpDrawItemStruct->CtlType == ODT_HEADER);
    HDITEM hdi;
    TCHAR lpBuffer[256];
    hdi.mask = HDI_TEXT;
    hdi.pszText = lpBuffer;
    hdi.cchTextMax = 256;
    GetItem(lpDrawItemStruct->itemID, &hdi);
    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT)); // this font is only for drawing as long as we don't do a SetFont()
    ::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH); // Draw the button frame.
    UINT uFormat;
    if (m_bMultiline) { uFormat = DT_CENTER; } else { uFormat = DT_SINGLELINE | DT_CENTER; }
    ::DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer), &lpDrawItemStruct->rcItem, uFormat); // Draw the text
    pDC->SelectStockObject(SYSTEM_FONT);
    }
    };


    It's basically the same class that the author used, except that I added a m_bMultiline variable and some code to switch between one and the other.

    Then, override PreSubclassWindow() for CMyListCtrl. The function should contain the following:

    void CMyListCtrl::PreSubclassWindow()
    {
    CHeaderCtrl* pHeader = GetHeaderCtrl();
    if (pHeader==NULL) { return; }
    VERIFY(m_HeaderCtrl.SubclassWindow(pHeader->m_hWnd));
    CListCtrl::PreSubclassWindow();
    }


    Then create a function for CMyListCtrl called SetHeaderMultiline() which looks like:

    void CMyListCtrl::SetHeaderMultiline(bool bMulti)
    {
    // Set up multiline headers
    InsertItem(0,"DELETE!"); // Used to trick the column bar to refresh

    // the following code will only ever get called once in the class's lifetime
    if (!m_bInit) {
    m_bInit = true;
    // get the current font so we can revert back to singleline later if needed
    CFont* pOldFont = m_HeaderCtrl.GetFont();
    LOGFONT LogFont; pOldFont->GetLogFont(&LogFont);
    m_FontSingle.CreateFontIndirect(&LogFont);
    // set ownerdraw to true (unfortunately we have to do this here because
    // there doesn't seem to be an init method that we can override
    // since OnCreate() doesn't get called for CListCtrl derived classes
    // and GetItemCount() returns 0 within PreSubclassWindow() Wink | ;-)
    HDITEM hdItem; hdItem.mask = HDI_FORMAT;
    for (int i = 0; i < m_HeaderCtrl.GetItemCount(); i++) {
    m_HeaderCtrl.GetItem(i,&hdItem);
    hdItem.fmt |= HDF_OWNERDRAW;
    m_HeaderCtrl.SetItem(i,&hdItem);
    }
    }

    //bool bFlipOwnerDraw = false;
    if (bMulti && !m_HeaderCtrl.m_bMultiline) {
    m_HeaderCtrl.SetFont(&m_FontMultiple);
    m_HeaderCtrl.m_bMultiline = true;
    } else if (m_HeaderCtrl.m_bMultiline) {
    m_HeaderCtrl.SetFont(&m_FontSingle);
    m_HeaderCtrl.m_bMultiline = false;
    }

    DeleteItem(0); // Completing the trick requires removing the grabage!
    }


    At this point your derived CMyListCtrl class should have a nifty new function called
    SetHeaderMultiline(...); Now, all you have to do is call that function from in the class which contains the list. A good example might be to call this from OnInitDialog() in a dialog box.

    m_List.SetHeaderMultiline(true);

    It sounds more complicated than it is... just add those 4 variables, initialize two of them in the list's constructor, define the CCtrlListHeaderEx class, and paste in the SetHeaderMultiline() function -- and your derived list control class should be displaying multiline headers in no time.

    questions/comments: lclemens@gmail.com

    --luke

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.