Click here to Skip to main content
Click here to Skip to main content

Lazy Grid WTL implementation

, 28 Jan 2002
Rate this:
Please Sign up or sign in to vote.
Simplest grid implementation in WTL

Sample Image - LazyGrid1.gif

About Custom Draw

See http://www.codeproject.com/listctrl/lvcustomdraw.asp by Michael Dunn. All information can be found from http://www.codeproject.com/ articles and MSDN.

Introduction

We need at least version 4.71 of the common controls installed (IE 4.0 and higher). I'll show the most simple grid implementation based on custom draw mechanism. With version 4.71 someone have possibility to overwrite subitem for list-view control. So I added this functionality to CCustomDraw (AtlCtrls.h).

First change is in CCustomDraw::OnCustomDraw where I made one more conditional branch for subitem prepainting

LRESULT OnCustomDraw(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) 
{ 
    ...
#if(_WIN32_IE >= 0x0400)
    LPNMLVCUSTOMDRAW lpNMLVCustomDraw = 
         reinterpret_cast<LPNMLVCUSTOMDRAW> (lpNMCustomDraw);
    if ( lpNMLVCustomDraw->nmcd.dwDrawStage == 
             (CDDS_SUBITEM | CDDS_ITEMPREPAINT) ) 
    {
        return pT->OnSubItemPrePaint(idCtrl, lpNMLVCustomDraw);
    }
#endif
    switch(lpNMCustomDraw->dwDrawStage)
    { 
    ...
    return dwRet;
}

Then one more overrideable was added.

 // Overrideables
#if (_WIN32_IE >= 0x0400)
DWORD OnSubItemPrePaint (int idCtrl, LPNMLVCUSTOMDRAW lpNMLVCustomDraw)
{
    return CDRF_DODEFAULT;
}
#endif

Let's now write simplest grid implementation. It handles both item and subitem prepainting.

class CGrid : public CWindowImpl<CGrid, CListViewCtrl>, CCustomDraw<CGrid>
{
public:
    long curRow, curCol;        // current row, column  
    COLORREF clrCell, clrCellBk;      // selected cell text, background  
    COLORREF clrRow, clrRowBk;    // selected row text, background
    COLORREF clrGrid, clrGridBk, clrBk; // unselected cell text, background
 
    BEGIN_MSG_MAP(CGrid)
        // Other handlers for customizing colors, holding cell edit, ...
            CHAIN_MSG_MAP ( CCustomDraw<CGrid>)
    END_MSG_MAP()
    
    DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
    {
        return CDRF_NOTIFYITEMDRAW;
    } 
    
    DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW pCD)
    {
        NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pCD );
        // asking subitem repainting for selected||focused row
        if ( pCD->uItemState == 0x201 || pCD->uItemState == 0x211 ) 
             return CDRF_NOTIFYSUBITEMDRAW;
        pLVCD->clrTextBk = clrGridBk;
        pLVCD->clrText = clrGrid;
        return CDRF_DODEFAULT;
    }
DWORD OnSubItemPrePaint (int /*idCtrl*/, LPNMLVCUSTOMDRAW pLVCD)
    {
        if ( pLVCD->nmcd.uItemState == 0x201 ||
                     pLVCD->nmcd.uItemState == 0x211 )
        {
            curRow = pLVCD->nmcd.dwItemSpec;
            
            // ATTENTION here we simply unselect row to 
            // avoid standard selection 
            // painting with ugly colors and leaves our nice colors 
            // for selected row (and cell also)
            pLVCD->nmcd.uItemState = 0x200; 
 

            if ( pLVCD->iSubItem == curCol )
            {
                pLVCD->clrText = clrCell;
                pLVCD->clrTextBk = clrCellBk;
            }
            else
            {
                pLVCD->clrText = clrRow;
                pLVCD->clrTextBk = clrRowBk;
            }
        }
        return CDRF_DODEFAULT;
    }
...
};

From now we have pretty looking grid with selected row and cell highlighting and without any additional painting, filling...

Let's add navigation

BEGIN_MSG_MAP(CGrid)

...
    MESSAGE_HANDLER ( WM_LBUTTONDOWN, OnMouse )
    MESSAGE_HANDLER ( WM_KEYDOWN, OnKey )
...
END_MSG_MAP()
 
LRESULT OnMouse(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    switch(wParam)
    {
        case MK_LBUTTON:
        {
            LVHITTESTINFO info;
            info.pt.x = LOWORD(lParam);
            info.pt.y = HIWORD(lParam);
            info.iItem = 0;
            info.iSubItem = 0;
            int nRes = SubItemHitTest (&info);
            if ( info.iItem == curRow )
            {
                curCol = info.iSubItem;
                SendNotifyMessage(CDRF_NOTIFYSUBITEMDRAW);
            }
            else
            {
                curCol = info.iSubItem;
                curRow = info.iItem;
                SendNotifyMessage(CDRF_NOTIFYITEMDRAW);
            }
            break;
    }
    bHandled = FALSE;
    return 0;
}

 

LRESULT OnKey(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    switch (wParam)
    {
    case VK_RIGHT:
        if ( curCol >= Cols-1) curCol = 0;
        else curCol++;
        SendNotifyMessage(CDRF_NOTIFYSUBITEMDRAW);
        break;
    case VK_LEFT:
        if ( curCol <= 0) curCol = Cols-1;
        else curCol--;
        SendNotifyMessage(CDRF_NOTIFYSUBITEMDRAW);
        break;
    }
    bHandled = FALSE;
    return 0;
}

Now our grid reacts at mouse and navigating buttons clicks. Now someone might like editing selected cells. Built-in edit control cannot be shown at any time we want, so let's write our own edit - like this. 

class CGridEdit : public CWindowImpl<CGridEdit, CEdit>
{
    CHAR m_text[256];    
public:
    operator LPTSTR() { return m_text; }
    operator LPCTSTR() const { return m_text; }
 
    BEGIN_MSG_MAP(CGridEdit)
        MESSAGE_HANDLER ( WM_KEYDOWN, OnKey )
    END_MSG_MAP()
 
    LRESULT OnKey(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        switch (wParam)
        {
        case VK_RETURN:
        {    LV_DISPINFO info;
            GetWindowText(m_text, 256);
            info.item.pszText = m_text;
            info.item.iItem = 0;
            info.item.cchTextMax = 256;
            info.hdr.code = LVN_ENDLABELEDIT;
            info.hdr.hwndFrom = m_hWnd;
            ::SendMessage ( GetParent(), WM_NOTIFY, NULL, (LPARAM)&info );
            break;    }

        case VK_ESCAPE:
        {    LV_DISPINFO info;
            info.item.pszText = 0;
            info.item.iItem = -1;
            info.item.cchTextMax = 0;
            info.hdr.code = LVN_ENDLABELEDIT;
            info.hdr.hwndFrom = m_hWnd;
            ::SendMessage ( GetParent(), WM_NOTIFY, NULL, (LPARAM)&info );
            break;    }
        case VK_LEFT:
        {
            WORD start = HIWORD(GetSel());
            start--;
            SetSel(start, start);
            break;    }
        case VK_RIGHT:
        {
            WORD start = HIWORD(GetSel());
            start++;
            SetSel(start, start);
            break;    }
        case VK_END:    SetSel(LineLength(),LineLength()); break;
        case VK_HOME:    SetSel(0,0); break;
        }    
        bHandled = TRUE;
        return 1;
    }
};

Our edit sends message to parent grid about finishing editing after Enter or Escape pressed. Also at grid we need to handle message WM_CTLCOLOREDIT to set colors to edit text, background.

Finally we get editing mechanism

class CGrid : public CWindowImpl<CGrid, CListViewCtrl>, CCustomDraw<CGrid>

{


public:
    bool editing;
    CGridEdit m_edit;
    ...
    BEGIN_MSG_MAP(CGrid)
        ...
        NOTIFY_CODE_HANDLER ( LVN_ENDLABELEDIT , OnEndEdit)
        NOTIFY_CODE_HANDLER ( LVN_BEGINLABELEDIT , OnBeginEdit)
        MESSAGE_HANDLER ( WM_CTLCOLOREDIT, OnColor )
        ...
    END_MSG_MAP()
 
    LRESULT OnBeginEdit(int /*wParam*/, LPNMHDR lParam, BOOL& bHandled)
    {
        RECT rc;
        GetSubItemRect(curRow, curCol, LVIR_BOUNDS, &rc);
        if ( Cols > 0   && curCol == 0  ) // correction for first column
        {
            GetSubItemRect(curRow, 1, LVIR_BOUNDS, &rc);
            rc.right = rc.left;
            rc.left = 0;
        }
        rc.left++; rc.bottom--; // some rect correction

        GetItemText(curRow, curCol, m_edit, 256);
        m_edit.MoveWindow (&rc, TRUE);
        m_edit.SetFont (GetFont());
        if ( curCol ) m_edit.SetMargins (5,5); // some margins correction

        else m_edit.SetMargins (3,5);
        m_edit.SetWindowText (m_edit);
        m_edit.ShowWindow(SW_SHOW);
        m_edit.SetFocus();
        m_edit.SetSel (0,0);
        m_edit.UpdateWindow ();
        editing = true;
        bHandled = TRUE;
        return 1;
    }
    
    LRESULT OnEndEdit(int /*wParam*/, LPNMHDR lParam, BOOL& bHandled)
    {
        LV_DISPINFO * plvdi = (LV_DISPINFO *)lParam;
        m_edit.ShowWindow(SW_HIDE);
        if((plvdi->item.iItem != -1  ) && 
              (plvdi->item.pszText != NULL)) 
            SetItemText ( curRow, curCol, plvdi->item.pszText );
        editing = false;
        bHandled = FALSE;
        return 0;
    }
    
    LRESULT OnColor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, 
         BOOL& bHandled)
    {
        HBRUSH brush = CreateSolidBrush (clrGridBk);
        ::SetBkColor ((HDC)wParam, clrGridBk);
        ::SetTextColor ((HDC)wParam, clrGrid);
        bHandled = TRUE;
        return (DWORD)brush;
    }
    ...
};

For now we have very simple, not optimized, but functional grid implementation without any hard coding. With the help of other authors on custom draw theme someone can customize grid in any way.

Conclusion

Without any exotic features lazy grid for me is easy to use and debug control. Warning - I haven't spent a lot of time for testing and debugging it. Best regards from Sokolov Maxim (lazy programmer).

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

Share

About the Author

Maxxx

Canada Canada
russian

Comments and Discussions

 
QuestionOnBeginEdit does not call [modified] Pinmemberuzerman1-Nov-12 7:01 
Questionabout all of these Customdraw functions Pinmemberpxdbxq12-Aug-12 19:14 
AnswerRe: about all of these Customdraw functions Pinmemberpxdbxq14-Aug-12 21:57 
Generalv c++ 7.0 = assertion failure Pinmemberericb_summit2-Jul-04 10:44 
GeneralRe: v c++ 7.0 = assertion failure PinmemberTcherepanov Serguei15-Mar-06 0:59 
GeneralProblem with compiling PinmemberHamlet_h17-Mar-04 5:43 
GeneralClistViewCtrl with different colors for rows for wince PinmemberBilal Ahmad1-Dec-03 18:56 
Generalno compile PinmemberAlex_Black18-Aug-03 1:32 
GeneralRe: no compile Pinsusssixth_voice1-Oct-03 3:48 
GeneralSelection is not updates when navigating to left of right PinmemberRamon Smits21-Jan-02 7:48 
General2 problems Pinmemberlimax15-Jan-02 10:57 
GeneralRe: 2 problems Pinmembergok21-Jan-02 8:57 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150327.1 | Last Updated 29 Jan 2002
Article Copyright 2002 by Maxxx
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid