以下是网上流传自绘CListCtrl控件的方法:不过有几个问题需要注意一下:
以下功能在静态创建时没有问题,动态创建是会出现两个问题
一.控件的表没有自动重绘,没有实现换肤。在CListCtrlCL类中设置断点,进入不了CListCtrlCl::DrawItem(),原因是在动态创建是没有LVS_OWNERDRAWFIXED风格。
解决办法: m_list->Create( LVS_REPORT|LVS_OWNERDRAWFIXED |WS_CHILD|WS_VISIBLE|LBS_NOTIFY|LBS_NOINTEGRALHEIGHT&~WS_BORDER , m_rect, this, 456121)
二.表头也没有完成换肤
查看源代码,调试发现在中CListCtrlCl::PreSubclassWindow()中GetHeaderCtrl();返回值为NULL。
解决办法:重载oncreat()函数 将 CHeaderCtrl *pHeader = GetHeaderCtrl();
m_Header.SubclassWindow(pHeader->GetSafeHwnd());这两行放入oncreat()中。
改正后的源码下载地址:http://download.csdn.net/detail/chenyixin121738/9706922
用CListCtrl来显示数据比较方便,有时候我们需要标注某一列或某一个单元格的背景和字体颜色,或者需要改变一下行高和字体大小,CListCtrl要改变这些并不是很方便。本文将介绍如何派生一个类来改变CListCtrl及其表头的高度、字体大小、列背景颜色、单元格背景颜色、列字体颜色、单元格字体颜色
实现过程:
1.表头修改
新建一个MFC类CHeaderCtrlCl,其基类为CHeaderCtrl,响应OnPaint消息实现自绘,在头文件中定义函数LRESULT OnLayout( WPARAM wParam, LPARAM lParam ),之后手动添加消息响应ON_MESSAGE(HDM_LAYOUT, OnLayout),在消息响应中改变高度。
HeaderCtrlCl.h文件的代码如下:
class CHeaderCtrlCl : public CHeaderCtrl
{
DECLARE_DYNAMIC(CHeaderCtrlCl)
public:
CHeaderCtrlCl();
virtual ~CHeaderCtrlCl();
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
CStringArray m_HChar;
CString m_Format; //表示对齐类型的整型数组,0表示左对齐,1表示中间对齐,2表示右对齐
public:
int m_R;
int m_G;
int m_B;
int m_Gradient; // 画立体背景,渐变系数
float m_Height; //表头高度,这是倍数,
int m_fontHeight; //字体高度
int m_fontWith; //字体宽度
COLORREF m_color;
LRESULT OnLayout( WPARAM wParam, LPARAM lParam );
};
//HeaderCtrlCl.cpp的代码如下
#include "HeaderCtrlCl.h" // CHeaderCtrlCl IMPLEMENT_DYNAMIC(CHeaderCtrlCl, CHeaderCtrl) CHeaderCtrlCl::CHeaderCtrlCl() : m_R(171) , m_G(199) , m_B(235) , m_Gradient(8) { m_Format = ""; m_Height = 1; m_fontHeight = 15; m_fontWith = 0; m_color = RGB(0,0,0); } CHeaderCtrlCl::~CHeaderCtrlCl() { } BEGIN_MESSAGE_MAP(CHeaderCtrlCl, CHeaderCtrl) ON_WM_PAINT() ON_MESSAGE(HDM_LAYOUT, OnLayout) END_MESSAGE_MAP() // CHeaderCtrlCl 消息处理程序 void CHeaderCtrlCl::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 // 不为绘图消息调用 CHeaderCtrl::OnPaint() int nItem; nItem = GetItemCount();//得到有几个单元 for(int i = 0; i<nItem;i ++) { CRect tRect; GetItemRect(i,&tRect);//得到Item的尺寸 int R = m_R,G = m_G,B = m_B; CRect nRect(tRect);//拷贝尺寸到新的容器中 nRect.left++;//留出分割线的地方 //绘制立体背景 for(int j = tRect.top;j<=tRect.bottom;j++) { nRect.bottom = nRect.top+1; CBrush _brush; _brush.CreateSolidBrush(RGB(R, G, B));//创建画刷 dc.FillRect(&nRect,&_brush); //填充背景 _brush.DeleteObject(); //释放画刷 R-=m_Gradient;G-=m_Gradient;B-=m_Gradient; if (R<0)R = 0; if (G<0)G = 0; if (B<0)B= 0; nRect.top = nRect.bottom; } dc.SetBkMode(TRANSPARENT); CFont nFont ,* nOldFont; //dc.SetTextColor(RGB(250,50,50)); dc.SetTextColor(m_color); nFont.CreateFont(m_fontHeight,m_fontWith,0,0,0,FALSE,FALSE,0,0,0,0,0,0,_TEXT("宋体"));//创建字体 nOldFont = dc.SelectObject(&nFont); UINT nFormat = 1; if (m_Format[i]=='0') { nFormat = DT_LEFT; tRect.left+=3; } else if (m_Format[i]=='1') { nFormat = DT_CENTER; } else if (m_Format[i]=='2') { nFormat = DT_RIGHT; tRect.right-=3; } //将文字显示在一个适合的高度位置 TEXTMETRIC metric; dc.GetTextMetrics(&metric); int ofst = 0; ofst = tRect.Height() - metric.tmHeight; tRect.OffsetRect(0,ofst/2); dc.DrawText(m_HChar[i],&tRect,nFormat); dc.SelectObject(nOldFont); nFont.DeleteObject(); //释放字体 } //画头部剩余部分(没有标题的那部分) CRect rtRect; CRect clientRect; GetItemRect(nItem - 1,rtRect); GetClientRect(clientRect); //当前(CHeaderCtrl)控件的大小 rtRect.left = rtRect.right+1; rtRect.right = clientRect.right; int R = m_R,G = m_G,B = m_B; CRect nRect(rtRect); //绘制立体背景 for(int j = rtRect.top;j<=rtRect.bottom;j++) { nRect.bottom = nRect.top+1; CBrush _brush; _brush.CreateSolidBrush(RGB(R,G,B));//创建画刷 dc.FillRect(&nRect,&_brush); //填充背景 _brush.DeleteObject(); //释放画刷 R-=m_Gradient;G-=m_Gradient;B-=m_Gradient; if (R<0)R = 0; if (G<0)G = 0; if (B<0)B= 0; nRect.top = nRect.bottom; } } LRESULT CHeaderCtrlCl::OnLayout( WPARAM wParam, LPARAM lParam ) { LRESULT lResult = CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, lParam); HD_LAYOUT &hdl = *( HD_LAYOUT * ) lParam; RECT *prc = hdl.prc; WINDOWPOS *pwpos = hdl.pwpos; //表头高度为原来1.5倍,如果要动态修改表头高度的话,将1.5设成一个全局变量 int nHeight = (int)(pwpos->cy * m_Height); pwpos->cy = nHeight; prc->top = nHeight; return lResult; }
2. 表的修改
新建一个MFC类CListCtrlCl,其基类为CListCtrl,定义一个CHeaderCtrlCl的成员变量m_Header,重载PreSubclassWindow(),在函数中修改控件类型为自绘模式,然后子类化表头。
这里在设置表头的行高时发送了一个WM_WINDOWPOSCHANGED消息。当我们给 listctrl 发送 WM_WINDOWPOSCHANGED消息时,也就是告诉 listdctrl 控件大小、位置等发生变化,这时候 listctrl 控件就会触发 ON_WM_MEASUREITEM_REFLECT消息,当然这个是反射消息。通过映射这个消息,我们就可以改变 listctrl 控件的行高。
代码中需要手动映射 ON_WM_MEASUREITEM_REFLECT 消息,该消息的响应函数是void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
///ListCtrlCl.h文件的代码如下// CListCtrlCl
// CListCtrlCl class CListCtrlCl : public CListCtrl { DECLARE_DYNAMIC(CListCtrlCl) public: CListCtrlCl(); virtual ~CListCtrlCl(); protected: DECLARE_MESSAGE_MAP() virtual void PreSubclassWindow(); protected: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); protected: void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); protected: CHeaderCtrlCl m_Header; //表头 int m_nRowHeight;// 行高 CPtrList m_ptrListCol; //保存列颜色 CPtrList m_ptrListItem; //保存Item颜色表 CPtrList m_colTextColor; //保存列字体颜色 CPtrList m_ItemTextColor; //保存单元格字体颜色 COLORREF m_color; int m_fontHeight; // 字体高度 int m_fontWith; // 字体宽度 public: // 设置表头高度 void SetHeaderHeight(float Height); // Gradient - 渐变系数,立体背景用,不用渐变设为0 void SetHeaderBKColor(int R, int G, int B, int Gradient); int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1); void SetHeaderFontHW(int nHeight,int nWith); //设置表头字体大小 void SetHeaderTextColor(COLORREF color); void SetRowHeight(int nHeight); //设置行高 bool FindColColor(int col ,COLORREF &color); //查找列颜色 bool FindItemColor(int col,int row,COLORREF &color); bool FindColTextColor(int col,COLORREF &color); //查找列字体颜色 bool FindItemTextColor(int col,int row,COLORREF &color); void SetColColor(int col,COLORREF color); //设置列颜色 void SetItemColor(int col,int row,COLORREF color); //设置Item颜色 void SetColTextColor(int col,COLORREF color); //设置列文本颜色 void SetItemTextColor(int col,int row,COLORREF color); void SetTextColor(COLORREF cr); void SetFontHW(int nHeight,int nWith); //设置字体的高和宽 };
//ListCtrlCl.cpp文件的代码
#include "ListCtrlCl.h" struct stColor { int nRow; int nCol; COLORREF rgb; }; // CListCtrlCl IMPLEMENT_DYNAMIC(CListCtrlCl, CListCtrl) CListCtrlCl::CListCtrlCl() : m_nRowHeight(0) , m_fontHeight(12) , m_fontWith(0) { m_color = RGB(0,0,0); } CListCtrlCl::~CListCtrlCl() { stColor *ptemp = NULL; while (m_ptrListCol.GetCount() > 0) { ptemp = (stColor *)(m_ptrListCol.RemoveHead()); if ( NULL != ptemp ) { delete ptemp; ptemp = NULL; } } while ( m_ptrListItem.GetCount() > 0) { ptemp = (stColor *)(m_ptrListItem.RemoveHead()); if ( NULL != ptemp ) { delete ptemp; ptemp = NULL; } } while ( m_colTextColor.GetCount() > 0) { ptemp = (stColor *)(m_colTextColor.RemoveHead()); if ( NULL != ptemp ) { delete ptemp; ptemp = NULL; } } while ( m_ItemTextColor.GetCount() > 0) { ptemp = (stColor *)(m_ItemTextColor.RemoveHead()); if ( NULL != ptemp ) { delete ptemp; ptemp = NULL; } } } BEGIN_MESSAGE_MAP(CListCtrlCl, CListCtrl) ON_WM_MEASUREITEM_REFLECT() END_MESSAGE_MAP() // CListCtrlCl 消息处理程序
//在此源代码有点问题 //之所以在资源编辑器中静态的拖进去控件不会有问题, 可能是因为拖进去控件时就已经设置其style为LVS_REPORT, 通过实验可以发现, 如果不是LVS_REPORT时, 那么在PreSubclassWindow()函数中GetHeaderCtrl()依然会返回NULL. //唯一让人迷惑不解的是, 静态添加时, 如果不指定其style为LVS_REPORT, 那么虽然PreSubclassWindow()中是不可能的, 但OnCreate()中却一直GetHeaderCtrl()可能正常工作 //具体原因可参照到http://wangqingyun84.blog.163.com/blog/static/7908361720119744913240/ void CListCtrlCl::PreSubclassWindow() { // TODO: 在此添加专用代码和/或调用基类 ModifyStyle(0,LVS_OWNERDRAWFIXED); CListCtrl::PreSubclassWindow(); //这两句在 控件被托到对话框资源时(formview等)没有问题,因为此时资源已经存在,report风格已经在属性里面设置完成 CHeaderCtrl *pHeader = GetHeaderCtrl(); //如果换成动态创建,调试到此处,会发现pHeader一值为NULL,原因是,此时窗口还没创建。 m_Header.SubclassWindow(pHeader->GetSafeHwnd());// } //解决办法 重载Oncreat()函数 将这两句放到Oncreat()函数中void CListCtrlCl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) void CListCtrlCl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: 添加您的代码以绘制指定项 TCHAR lpBuffer[256]; LV_ITEM lvi; lvi.mask = LVIF_TEXT | LVIF_PARAM ; lvi.iItem = lpDrawItemStruct->itemID ; lvi.iSubItem = 0; lvi.pszText = lpBuffer ; lvi.cchTextMax = sizeof(lpBuffer); VERIFY(GetItem(&lvi)); LV_COLUMN lvc, lvcprev ; ::ZeroMemory(&lvc, sizeof(lvc)); ::ZeroMemory(&lvcprev, sizeof(lvcprev)); lvc.mask = LVCF_WIDTH | LVCF_FMT; lvcprev.mask = LVCF_WIDTH | LVCF_FMT; CDC* pDC; pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rtClient; GetClientRect(&rtClient); for ( int nCol=0; GetColumn(nCol, &lvc); nCol++) { if ( nCol > 0 ) { // Get Previous Column Width in order to move the next display item GetColumn(nCol-1, &lvcprev) ; lpDrawItemStruct->rcItem.left += lvcprev.cx ; lpDrawItemStruct->rcItem.right += lpDrawItemStruct->rcItem.left; } CRect rcItem; if (!GetSubItemRect(lpDrawItemStruct->itemID,nCol,LVIR_LABEL,rcItem)) continue; ::ZeroMemory(&lvi, sizeof(lvi)); lvi.iItem = lpDrawItemStruct->itemID; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.iSubItem = nCol; lvi.pszText = lpBuffer; lvi.cchTextMax = sizeof(lpBuffer); VERIFY(GetItem(&lvi)); CRect rcTemp; rcTemp = rcItem; if (nCol==0) { rcTemp.left -=2; } if ( lpDrawItemStruct->itemState & ODS_SELECTED ) { pDC->FillSolidRect(&rcTemp, GetSysColor(COLOR_HIGHLIGHT)) ; pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)) ; } else { COLORREF color; color = GetBkColor(); pDC->FillSolidRect(rcTemp,color); if (FindColColor(nCol,color)) { pDC->FillSolidRect(rcTemp,color); } if (FindItemColor(nCol,lpDrawItemStruct->itemID,color)) { pDC->FillSolidRect(rcTemp,color); } //pDC->SetTextColor(m_color); } pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT)); UINT uFormat = DT_CENTER ; if (m_Header.m_Format[nCol]=='0') { uFormat = DT_LEFT; } else if (m_Header.m_Format[nCol]=='1') { uFormat = DT_CENTER; } else if (m_Header.m_Format[nCol]=='2') { uFormat = DT_RIGHT; } TEXTMETRIC metric; pDC->GetTextMetrics(&metric); int ofst; ofst = rcItem.Height() - metric.tmHeight; rcItem.OffsetRect(0,ofst/2); pDC->SetTextColor(m_color); COLORREF color; if (FindColTextColor(nCol,color)) { pDC->SetTextColor(color); } if (FindItemTextColor(nCol,lpDrawItemStruct->itemID,color)) { pDC->SetTextColor(color); } CFont nFont ,* nOldFont; nFont.CreateFont(m_fontHeight,m_fontWith,0,0,0,FALSE,FALSE,0,0,0,0,0,0,_TEXT("宋体"));//创建字体 nOldFont = pDC->SelectObject(&nFont); DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer), &rcItem, uFormat) ;//工程为unicode 应该为wcslen(lpBuffer) pDC->SelectStockObject(SYSTEM_FONT) ; } } void CListCtrlCl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { if (m_nRowHeight>0) { lpMeasureItemStruct->itemHeight = m_nRowHeight; } } int CListCtrlCl::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat /* = LVCFMT_LEFT */, int nWidth /* = -1 */, int nSubItem /* = -1 */) { m_Header.m_HChar.Add(lpszColumnHeading); if (nFormat==LVCFMT_LEFT) { m_Header.m_Format = m_Header.m_Format + "0"; } else if (nFormat==LVCFMT_CENTER) { m_Header.m_Format = m_Header.m_Format + "1"; } else if (nFormat==LVCFMT_RIGHT) { m_Header.m_Format = m_Header.m_Format + "2"; } else { m_Header.m_Format = m_Header.m_Format + "1"; } return CListCtrl::InsertColumn(nCol,lpszColumnHeading,nFormat,nWidth,nSubItem); } // Gradient - 渐变系数,立体背景用,不用渐变设为0 void CListCtrlCl::SetHeaderBKColor(int R, int G, int B, int Gradient) //设置表头背景色 { m_Header.m_R = R; m_Header.m_G = G; m_Header.m_B = B; m_Header.m_Gradient = Gradient; } // 设置表头高度 void CListCtrlCl::SetHeaderHeight(float Height) //设置表头高度 { m_Header.m_Height = Height; } bool CListCtrlCl::FindColColor(int col,COLORREF &color) //查找列颜色 { int flag = 0; for (POSITION pos = m_ptrListCol.GetHeadPosition();pos!=NULL;) { stColor *pColor = (stColor*)m_ptrListCol.GetNext(pos); if (pColor->nCol==col) { flag = 1; color = pColor->rgb; break; } } if (1==flag) { return true; } return false; } bool CListCtrlCl::FindItemColor(int col,int row,COLORREF &color) //查找颜色 { int flag = 0; for (POSITION pos = m_ptrListItem.GetHeadPosition();pos!=NULL;) { stColor *pColor = (stColor*)m_ptrListItem.GetNext(pos); if (pColor->nCol==col&&pColor->nRow==row) { flag = 1; color = pColor->rgb; break; } } if (1==flag) { return true; } return false; } void CListCtrlCl::SetColColor(int col,COLORREF color) //设置列颜色 { stColor *pColor = new stColor; pColor->nCol = col; pColor->rgb = color; m_ptrListCol.AddTail(pColor); } void CListCtrlCl::SetItemColor(int col,int row,COLORREF color) //设置格子颜色 { stColor *pColor = new stColor; pColor->nCol = col; pColor->nRow = row; pColor->rgb = color; m_ptrListItem.AddTail(pColor); } void CListCtrlCl::SetRowHeight(int nHeight) // 设置行高 { m_nRowHeight = nHeight; CRect rcWin; GetWindowRect(&rcWin); WINDOWPOS wp; wp.hwnd = m_hWnd; wp.cx = rcWin.Width(); wp.cy = rcWin.Height(); wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER; SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp); } void CListCtrlCl::SetHeaderFontHW(int nHeight,int nWith) //设置头部字体宽和高 { m_Header.m_fontHeight = nHeight; m_Header.m_fontWith = nWith; } void CListCtrlCl::SetHeaderTextColor(COLORREF color) //设置头部字体颜色 { m_Header.m_color = color; } void CListCtrlCl::SetTextColor(COLORREF cr) //设置字体颜色 { m_color = cr; } void CListCtrlCl::SetFontHW(int nHeight,int nWith) //设置字体高和宽 { m_fontHeight = nHeight; m_fontWith = nWith; } void CListCtrlCl::SetColTextColor(int col,COLORREF color) { stColor *pColor = new stColor; pColor->nCol = col; pColor->rgb = color; m_colTextColor.AddTail(pColor); } bool CListCtrlCl::FindColTextColor(int col,COLORREF &color) { int flag = 0; for (POSITION pos = m_colTextColor.GetHeadPosition();pos!=NULL;) { stColor *pColor = (stColor*)m_colTextColor.GetNext(pos); if (pColor->nCol==col) { flag = 1; color = pColor->rgb; break; } } if (1==flag) { return true; } return false; } bool CListCtrlCl::FindItemTextColor(int col,int row,COLORREF &color) { int flag = 0; for (POSITION pos = m_ItemTextColor.GetHeadPosition();pos!=NULL;) { stColor *pColor = (stColor*)m_ItemTextColor.GetNext(pos); if (pColor->nCol==col&&pColor->nRow==row) { flag = 1; color = pColor->rgb; break; } } if (1==flag) { return true; } return false; } void CListCtrlCl::SetItemTextColor(int col,int row,COLORREF color) { stColor *pColor = new stColor; pColor->nCol = col; pColor->nRow = row; pColor->rgb = color; m_ItemTextColor.AddTail(pColor); }
测试:在对话框的OnInitDialog()中添加初始化代码:
m_ListCtrl.SetColColor(0,RGB(10,150,20)); //设置列背景色 m_ListCtrl.SetColColor(2,RGB(30,100,90)); //设置列背景色 m_ListCtrl.SetBkColor(RGB(50,10,10)); //设置背景色 m_ListCtrl.SetItemColor(1,1,RGB(100,100,10)); //设置指定单元背景色 m_ListCtrl.SetRowHeight(25); //设置行高度 m_ListCtrl.SetHeaderHeight(1.5); //设置头部高度 m_ListCtrl.SetHeaderFontHW(16,0); //设置头部字体高度,和宽度,0表示缺省,自适应 m_ListCtrl.SetHeaderTextColor(RGB(255,200,100)); //设置头部字体颜色 m_ListCtrl.SetTextColor(RGB(0,255,255)); //设置文本颜色 m_ListCtrl.SetHeaderBKColor(100,255,100,8); //设置头部背景色 m_ListCtrl.SetFontHW(15,0); //设置字体高度,和宽度,0表示缺省宽度 m_ListCtrl.SetColTextColor(2,RGB(255,255,100)); //设置列文本颜色 m_ListCtrl.SetItemTextColor(3,1,RGB(255,0,0)); //设置单元格字体颜色 m_ListCtrl.InsertColumn(0,_T("名字"),LVCFMT_CENTER,55); m_ListCtrl.InsertColumn(1,_T("身高"),LVCFMT_CENTER,60); m_ListCtrl.InsertColumn(2,_T("体重"),LVCFMT_CENTER,60); m_ListCtrl.InsertColumn(3,_T("测量时间"),LVCFMT_CENTER,180); m_ListCtrl.InsertItem(0,"张三"); m_ListCtrl.SetItemText(0,1,"178CM"); m_ListCtrl.SetItemText(0,2,"70KG"); m_ListCtrl.SetItemText(0,3,"2009年1月15日23时40分"); m_ListCtrl.InsertItem(1,"王五"); m_ListCtrl.SetItemText(1,1,"178cm"); m_ListCtrl.SetItemText(1,2,"70kg"); m_ListCtrl.SetItemText(1,3,"2009年1月15日23时40分"); m_ListCtrl.InsertItem(2,"阿花"); m_ListCtrl.SetItemText(2,1,"168cm"); m_ListCtrl.SetItemText(2,2,"60kg"); m_ListCtrl.SetItemText(2,3,"2009年1月15日23时40分"); SetWindowLong(m_ListCtrl.m_hWnd ,GWL_EXSTYLE,WS_EX_CLIENTEDGE); m_ListCtrl.SetExtendedStyle(LVS_EX_GRIDLINES); //设置扩展风格为网格
//::SendMessage(m_ListCtrl.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);