Lazy Grid WTL 实现






4.64/5 (5投票s)
2002年1月15日
1分钟阅读

152202

2676
WTL 中最简单的网格实现。
关于自定义绘制
请参阅 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(懒惰的程序员)的问候。