完美的CListCtrl控件自绘

来源:互联网 发布:淘宝宝贝排名靠前技巧 编辑:程序博客网 时间:2024/05/22 09:19

自绘一个item我相信大部分人都在10分钟内能搞定

但是绘制非item部分的区域,同学们就傻眼了,不知道如何下手

CListCtrl的绘制机制在windows里做了一个优化,就是一个绘制循环通知过程

之所以不在OnPaint中画是考虑效率上的原因,好了我们看下这个绘制循环:

 

 

CDDS_PREERASE 准备开始擦除循环 

CDDS_POSTERASE 擦除循环结束 

CDDS_PREPAINT 准备开始绘制循环 

CDDS_POSTPAINT 绘制循环结束 

CDDS_ITEM 指定dwItemSpec, uItemState, lItemlParam参数有效 

CDDS_ITEMPREERASE 准备开始列表项擦除 

CDDS_ITEMPREPAINT 准备开始列表项绘制 

CDDS_SUBITEM 指定列表子项 

CDDS_ITEMPOSTERASE 列表项擦除结束 

CDDS_ITEMPOSTPAINT 列表项绘制结束 

我们这里主要是响应NM_CUSTOMDRAW来实现绘制的

在CDDS_POSTPAINT绘制循环结束时我们可以开始绘制非item的部分,如何计算非item的空间请看下面代码:

 

 

//SkinListCtrl.h

[cpp] view plaincopy
  1. #pragma once  
  2.   
  3. // CSkinListCtrl  
  4. class CSkinListCtrl : public CListCtrl  
  5. {  
  6.     DECLARE_DYNAMIC(CSkinListCtrl)  
  7. public:  
  8.     unsigned int LIST_ITEM_HEIGHT;  
  9. public:  
  10.     CSkinListCtrl();  
  11.     virtual ~CSkinListCtrl();  
  12. protected:  
  13.     DECLARE_MESSAGE_MAP()  
  14. public:  
  15.     afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);  
  16. protected:  
  17.     void Init();  
  18.     virtual void DrawSubItem(CDC *pDC, int nItem, int nSubItem, CRect &rSubItem, bool bSelected, bool bFocus);  
  19.     virtual void DrawRemainSpace(LPNMLVCUSTOMDRAW lpnmcd);  
  20.     virtual void draw_row_bg(CDC *pDC, RECT rc, bool bSelected, bool bFocus,int nRow);  
  21.     virtual void draw_row_bg(CDC *pDC, RECT rc, bool bSelected, bool bFocus, bool bOdd);  
  22.     void InvalidateItem(int nItem);  
  23. public:  
  24.     afx_msg void OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult);  
  25.     virtual void PreSubclassWindow();  
  26.     virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);  
  27. };  
 

 

//SkinListCtrl.cpp

 

[cpp] view plaincopy
  1. // SkinListCtrl.cpp : implementation file  
  2. //  
  3. #include "stdafx.h"  
  4. #include "SkinListCtrl.h"  
  5. // CSkinListCtrl  
  6. IMPLEMENT_DYNAMIC(CSkinListCtrl, CListCtrl)  
  7. CSkinListCtrl::CSkinListCtrl()  
  8. {  
  9.    LIST_ITEM_HEIGHT = 20;  
  10. }  
  11. CSkinListCtrl::~CSkinListCtrl()  
  12. {  
  13. }  
  14.   
  15. BEGIN_MESSAGE_MAP(CSkinListCtrl, CListCtrl)  
  16.     ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CSkinListCtrl::OnNMCustomdraw)  
  17.     //ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, &CSkinListCtrl::OnLvnItemchanged)  
  18. END_MESSAGE_MAP()  
  19.   
  20. // CSkinListCtrl message handlers  
  21. void CSkinListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)  
  22. {  
  23.     LPNMLVCUSTOMDRAW lpnmcd = (LPNMLVCUSTOMDRAW) pNMHDR;  
  24.     if (lpnmcd ->nmcd.dwDrawStage == CDDS_PREPAINT)  
  25.     {  
  26.         *pResult =  CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;  
  27.         return;  
  28.     }  
  29.     else if (lpnmcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)  
  30.     {  
  31.         /*CRect rSubItem, rectClient; 
  32.         GetItemRect(lpnmcd->nmcd.dwItemSpec, &rSubItem, LVIR_LABEL); 
  33.         GetClientRect(&rectClient);  
  34.         rSubItem.left = 0; 
  35.         rSubItem.right = rectClient.right; 
  36.         rSubItem.NormalizeRect(); 
  37.         bool bSelected = false; 
  38.         if (GetItemState(lpnmcd->nmcd.dwItemSpec, LVIS_SELECTED)) 
  39.         { 
  40.             bSelected = true; 
  41.         } 
  42.         bool bFocus = false; 
  43.         HWND hWndFocus = ::GetFocus(); 
  44.         if (::IsChild(m_hWnd,hWndFocus) || m_hWnd == hWndFocus) 
  45.         { 
  46.             bFocus = true; 
  47.         } 
  48.         CDC dc; 
  49.         dc.Attach(lpnmcd->nmcd.hdc); 
  50.         draw_row_bg(&dc, rSubItem, bSelected , bFocus, (int) lpnmcd->nmcd.dwItemSpec); 
  51.         dc.Detach();*/  
  52.         *pResult =  CDRF_NOTIFYSUBITEMDRAW;  
  53.         return;  
  54.     }  
  55.     else if (lpnmcd ->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT))  
  56.     {  
  57.         int iItem = lpnmcd->nmcd.dwItemSpec;  
  58.         int iSubItem = lpnmcd->iSubItem;  
  59.         if (iItem >= 0 && iSubItem >= 0)  
  60.         {  
  61.             CRect rSubItem;  
  62.             HDC hDC = lpnmcd->nmcd.hdc;  
  63.             GetSubItemRect(iItem, iSubItem, LVIR_LABEL,rSubItem);  
  64.             if (iSubItem == 0)  
  65.             {  
  66.                 rSubItem.left = 0;  
  67.             }  
  68.               
  69.             bool bSelected = false;  
  70.             if (GetItemState(iItem, LVIS_SELECTED))  
  71.             {  
  72.                 bSelected = true;  
  73.             }  
  74.             bool bFocus = false;  
  75.             CWnd *pWndFocus = GetFocus();  
  76.             if (IsChild(pWndFocus) || pWndFocus == this)  
  77.             {  
  78.                 bFocus = true;  
  79.             }  
  80.             rSubItem.NormalizeRect();  
  81.             CDC dc;  
  82.             dc.Attach(lpnmcd->nmcd.hdc);  
  83.             DrawSubItem(&dc,iItem,iSubItem,rSubItem,bSelected,bFocus);  
  84.             dc.Detach();  
  85.             *pResult =  CDRF_SKIPDEFAULT;  
  86.             return;  
  87.         }  
  88.     }  
  89.     else if (lpnmcd ->nmcd.dwDrawStage == CDDS_POSTPAINT)  
  90.     {  
  91.         DrawRemainSpace(lpnmcd);  
  92.         *pResult =  CDRF_SKIPDEFAULT;  
  93.         return;  
  94.     }  
  95.        
  96.     *pResult = 0;  
  97. }  
  98. // overwrite:  
  99. void CSkinListCtrl::DrawSubItem(CDC *pDC, int nItem, int nSubItem, CRect &rSubItem, bool bSelected, bool bFocus)  
  100. {  
  101.   
  102.     pDC->SetBkMode(TRANSPARENT);  
  103.     pDC->SetTextColor(RGB(0, 0, 0));  
  104.     CFont font;  
  105.     font.CreateFont(12,   // nHeight  
  106.         0,                         // nWidth  
  107.         0,                         // nEscapement  
  108.         0,                         // nOrientation  
  109.         FW_NORMAL,                 // nWeight  
  110.         FALSE,                     // bItalic  
  111.         FALSE,                     // bUnderline  
  112.         0,                         // cStrikeOut  
  113.         ANSI_CHARSET,              // nCharSet  
  114.         OUT_DEFAULT_PRECIS,        // nOutPrecision  
  115.         CLIP_DEFAULT_PRECIS,       // nClipPrecision  
  116.         DEFAULT_QUALITY,           // nQuality  
  117.         DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily  
  118.         _T("宋体"));  
  119.     pDC->SelectObject(&font);  
  120.     CString strText;  
  121.     strText = GetItemText(nItem, nSubItem);  
  122.     draw_row_bg(pDC, rSubItem, bSelected, bFocus, nItem);  
  123.     pDC->DrawText(strText, strText.GetLength(), &rSubItem, DT_SINGLELINE | DT_RIGHT | DT_VCENTER | DT_END_ELLIPSIS);  
  124.     
  125. }  
  126. // 画剩余部分  
  127. void CSkinListCtrl::DrawRemainSpace(LPNMLVCUSTOMDRAW lpnmcd)  
  128. {  
  129.     int nTop = lpnmcd->nmcd.rc.top;  
  130.     int nCount = GetItemCount();  
  131.     if (nCount > 0)  
  132.     {  
  133.         CRect rcItem;  
  134.         GetItemRect(nCount - 1, &rcItem, LVIR_LABEL);  
  135.         nTop = rcItem.bottom;  
  136.     }  
  137.     CRect rectClient;  
  138.     GetClientRect(&rectClient);  
  139.     if (nTop < lpnmcd->nmcd.rc.bottom) // 有剩余  
  140.     {  
  141.         CRect rcRemain = lpnmcd->nmcd.rc;  
  142.         rcRemain.top = nTop;  
  143.         rcRemain.right = rectClient.right;  
  144.         int nRemainItem = rcRemain.Height() / LIST_ITEM_HEIGHT;  
  145.         if (rcRemain.Height() % LIST_ITEM_HEIGHT)  
  146.         {  
  147.             nRemainItem++;  
  148.         }  
  149.         int pos = GetScrollPos(SB_HORZ);  
  150.         CDC dc;  
  151.         dc.Attach(lpnmcd->nmcd.hdc);  
  152.         for (int i = 0; i < nRemainItem; ++i)  
  153.         {  
  154.             CRect rcItem;  
  155.             rcItem.top = rcRemain.top + i * LIST_ITEM_HEIGHT;  
  156.             rcItem.left = rcRemain.left;  
  157.             rcItem.right = rcRemain.right;  
  158.             rcItem.bottom = rcItem.top + LIST_ITEM_HEIGHT;  
  159.             int nColumnCount = GetHeaderCtrl()->GetItemCount();  
  160.             CRect  rcSubItem;  
  161.             for (int j = 0; j < nColumnCount; ++j)  
  162.             {  
  163.                 GetHeaderCtrl()->GetItemRect(j, &rcSubItem);  
  164.                 rcSubItem.top = rcItem.top;  
  165.                 rcSubItem.bottom = rcItem.bottom;  
  166.                 rcSubItem.OffsetRect(-pos, 0);  
  167.                 if(rcSubItem.right < rcRemain.left || rcSubItem.left > rcRemain.right)  
  168.                     continue;  
  169.                 draw_row_bg(&dc, rcSubItem, falsefalse, i + nCount);            
  170.             }  
  171.             /*if (rcSubItem.right<rectClient.right) 
  172.             { 
  173.                 rcSubItem.left=rcSubItem.right; 
  174.                 rcSubItem.right=rectClient.right; 
  175.                 draw_row_bg(&dc, rcSubItem, false, false, i+nCount);     
  176.             }*/  
  177.         }  
  178.         dc.Detach();  
  179.     }  
  180. }  
  181. void CSkinListCtrl::draw_row_bg(CDC *pDC, RECT rc, bool bSelected, bool bFocus,int nRow)  
  182. {   
  183.     bool bOdd = (nRow % 2 == 0 ? true : false);  
  184.     CRect rect = rc;  
  185.     if (rect.Width() == 0)  
  186.     {  
  187.         return;  
  188.     }  
  189.        
  190.     draw_row_bg(pDC, rc, bSelected,bFocus,bOdd);  
  191. }  
  192. void CSkinListCtrl::draw_row_bg(CDC *pDC, RECT rc, bool bSelected, bool bFocus, bool bOdd)  
  193. {  
  194.     CRect rect = rc;  
  195.     if (rect.Width() == 0)  
  196.     {  
  197.         return;  
  198.     }  
  199.     int nSave = pDC->SaveDC();  
  200.     if (bSelected)  
  201.     {  
  202.         if (bFocus)  
  203.         {  
  204.             CBrush selectBrush;  
  205.             selectBrush.CreateSolidBrush(RGB(203, 223, 239));  
  206.             pDC->FillRect(&rc, &selectBrush);  
  207.         }  
  208.         else  
  209.         {  
  210.             CBrush selectNoFocusBrush;  
  211.             selectNoFocusBrush.CreateSolidBrush(RGB(206, 206, 206));  
  212.             pDC->FillRect(&rc, &selectNoFocusBrush);  
  213.         }  
  214.     }  
  215.     else if (bOdd)  
  216.     {  
  217.         CBrush oddBrush;  
  218.         oddBrush.CreateSolidBrush(RGB(255, 255, 255));  
  219.         pDC->FillRect(&rc, &oddBrush);  
  220.     }  
  221.     else  
  222.     {  
  223.         CBrush normalBrush;  
  224.         normalBrush.CreateSolidBrush(RGB(243, 243, 243));  
  225.         pDC->FillRect(&rc, &normalBrush);  
  226.     }  
  227.   
  228.     // 画竖线  
  229.     CPen pen;  
  230.     pen.CreatePen(PS_SOLID, 1, RGB(218, 218, 218));  
  231.     pDC->SelectObject(&pen);  
  232.     pDC->MoveTo(rc.right - 1, rc.top);  
  233.     pDC->LineTo(rc.right - 1, rc.bottom);  
  234.     // 画选中的底部分割线  
  235.     if (bSelected)  
  236.     {  
  237.         CPen bottomPen;  
  238.         bottomPen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));  
  239.         pDC->SelectObject(&bottomPen);  
  240.         pDC->MoveTo(rc.left, rc.bottom - 1);  
  241.         pDC->LineTo(rc.right, rc.bottom - 1);  
  242.     }  
  243.     pDC->RestoreDC(nSave);  
  244. }  
  245. void CSkinListCtrl::Init()  
  246. {  
  247.     LOGFONT logfont;  
  248.     memset(&logfont, 0, sizeof(logfont));  
  249.     logfont.lfWeight = FW_NORMAL;  
  250.     logfont.lfCharSet = GB2312_CHARSET;  
  251.     _tcscpy_s(logfont.lfFaceName, LF_FACESIZE, _T("宋体"));  
  252.     logfont.lfHeight = -(LIST_ITEM_HEIGHT-1);  
  253.     CFont font;  
  254.     font.CreateFontIndirect(&logfont);  
  255.     SetFont(&font);  
  256.     font.Detach();  
  257. }  
  258. void CSkinListCtrl::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)  
  259. {  
  260.     LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);  
  261.     if (pNMLV->uChanged & LVIF_STATE)  
  262.     {  
  263.         if (((pNMLV->uOldState & LVIS_SELECTED) != (pNMLV->uNewState & LVIS_SELECTED))   
  264.             || ((pNMLV->uOldState & LVIS_STATEIMAGEMASK) != (pNMLV->uNewState & LVIS_STATEIMAGEMASK)))  
  265.         {  
  266.             InvalidateItem(pNMLV->iItem);  
  267.         }  
  268.     }  
  269.     *pResult = 0;  
  270. }  
  271. void CSkinListCtrl::InvalidateItem(int nItem)  
  272. {  
  273.     CRect rcClient;  
  274.     GetClientRect(&rcClient);  
  275.     CRect rcItem;  
  276.     GetItemRect(nItem, &rcItem, LVIR_BOUNDS);  
  277.     rcItem.left = rcClient.left;  
  278.     rcItem.right = rcClient.right;  
  279.     InvalidateRect(&rcItem,FALSE);  
  280. }  
  281. void CSkinListCtrl::PreSubclassWindow()  
  282. {  
  283.     Init();  
  284.     CListCtrl::PreSubclassWindow();  
  285. }  
  286. void CSkinListCtrl::DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)  
  287. {  
  288. }  
 

 

看效果图:

 

看到了吧 ,非item部分我们也绘制出来了,右边问号部分我这里没做绘制,这部分也可以绘制的,可以把斑马线顶到最右边

创建CListCtrl时样式建议如下:

 

 

[cpp] view plaincopy
  1. DWORD dwExtentStyle = m_list.GetExtendedStyle();  
  2. m_list.SetExtendedStyle(dwExtentStyle|LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER);  
 

 

这里已经画有网格线进去了,样式里就不要加这个网格线样式了,就是不用加LVS_EX_GRIDLINES 样式

OK,后面我们再来画下headerctrl就很好看了,下一篇中给出源码,并结合到CSkinListCtrl中

0 0
原创粉丝点击