65.9K
CodeProject 正在变化。 阅读更多。
Home

Lazy Grid WTL 实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (5投票s)

2002年1月15日

1分钟阅读

viewsIcon

152202

downloadIcon

2676

WTL 中最简单的网格实现。

Sample Image - LazyGrid1.gif

关于自定义绘制

请参阅 Michael Dunn 的 https://codeproject.org.cn/listctrl/lvcustomdraw.asp。所有信息都可以在 https://codeproject.org.cn/ 文章和 MSDN 中找到。

引言

我们需要安装至少 4.71 版本的通用控件(IE 4.0 及更高版本)。我将展示基于自定义绘制机制的最简单的网格实现。使用 4.71 版本,有人可以覆盖列表视图控件的子项。因此,我将此功能添加到 CCustomDraw AtlCtrls.h)中。

第一个更改在 CCustomDraw::OnCustomDraw 中,我在其中为子项预绘制添加了一个额外的条件分支。

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

然后添加了另一个可重写项。

 // Overrideables

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

现在让我们编写最简单的网格实现。它处理项目和子项的预绘制。

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

现在我们拥有外观漂亮的网格,具有选定行和单元格高亮显示,并且没有额外的绘制或填充...

让我们添加导航

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

现在我们的网格对鼠标和导航按钮的点击做出反应。现在有人可能希望编辑选定的单元格。内置编辑控件不能在任何我们想要的时间显示,所以让我们编写自己的编辑控件 - 就像这样。

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

我们的编辑控件在按下 Enter 或 Escape 键后向父网格发送消息,指示编辑完成。此外,我们需要在网格中处理消息 WM_CTLCOLOREDIT 以设置编辑文本、背景的颜色。

最终我们得到了编辑机制

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

目前,我们有一个非常简单、未优化的但功能完善的网格实现,没有进行任何硬编码。在其他作者关于自定义绘制主题的帮助下,可以以任何方式自定义网格。

结论

对我来说,没有奇特功能,简单的网格是一个易于使用和调试的控件。警告 - 我没有花很多时间来测试和调试它。来自 Sokolov Maxim(懒惰的程序员)的问候。

© . All rights reserved.