效果图:
1、绘制非item区域可以使用NM_CUSTOMDRAW这个消息。非item区域分为:插入行的下方和插入行的右侧两个区域。
2、对于create的clistctrl控件,在MFC对话框中正常响应NM_CUSTOMDRAW,但对于嵌入到blcokUI后,不响应。我目前的解决方案是:嵌入MFC对话框,然后从资源工具栏拖到对话框内,然后子类化控件。
3、插入行不能使用InsertItem,需要用数组记录每次插入行的信息(我定义了一个结构体,把单元格颜色信息也存取了),然后使用LVN_GETDISPINFO更新列表显示内容。插入行或者更改单元格内容后,使用SetItemCount更新显示。
4、列表控件跟随Block窗口大小变化,方案1:在blcokUI里添加定时器,定时刷新,更改控件窗口位置。方案2:子类化blockUI对话框,并重载onsize和onsizing在里面设置clistctrl控件大小。
5、双击单元格可修改内容,继承Cedit,重写一个类,clistctrl响应双击消息,并create一个edit控件,用来编辑内容。
6、相关参数,可能不是很准确。表头高度23,行高20。表头背景色RGB(240,240,240);表格奇数行背景色RGB(250,250,250),偶数行RGB(255,255,255);网格线颜色RGB(236,236,236);选中高亮色RGB(51,153,255)。
以下为绘制关键代码:
//绘制表格
void NxListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLVCUSTOMDRAW pLVCD = (LPNMLVCUSTOMDRAW)pNMHDR;
*pResult = CDRF_DODEFAULT;
if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage ) //准备开始绘制循环
{
*pResult = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT; //CDRF_NOTIFYITEMDRAW指出项或子项将进行绘制。注意:它下面的值与 CDRF_NOTIFYSUBITEMDRAW 相同。 //CDRF_NOTIFYPOSTPAINT 列表项绘制结束时发送消息
return;
}
else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage ) //准备开始绘制item区域右侧非item区域
{
DrawRightNonItemSpace(pLVCD);
*pResult = CDRF_NOTIFYSUBITEMDRAW; //指出项(或子项)将进行绘制。注意,它上面的值与 CDRF_NOTIFYITEMDRAW 相同。
return;
}
else if ( (CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage ) //准备开始绘制绘制item区域
{
int nRow = static_cast<int>( pLVCD->nmcd.dwItemSpec );//获得子项(行数)
int nCol = pLVCD->iSubItem;//获得子项(列数)
if (nRow >= 0 && nCol >= 0)
{
//是否选中单元格
bool bSelected = false;
if (GetItemState(nRow, LVIS_SELECTED))
{
bSelected = true;
}
//是否list控件聚焦
bool bFocus = false;
CWnd *pWndFocus = GetFocus();
if (IsChild(pWndFocus) || pWndFocus == this)
{
bFocus = true;
}
//获取单元格位置
CRect rSubItem;
GetSubItemRect(nRow, nCol, LVIR_LABEL,rSubItem);
rSubItem.NormalizeRect();
//绘制单元格
CDC dc;
dc.Attach(pLVCD->nmcd.hdc);
DrawSubItem(&dc, nRow, nCol, rSubItem, bSelected, bFocus);
dc.Detach();
}
*pResult = CDRF_SKIPDEFAULT;
return;
}
else if (pLVCD ->nmcd.dwDrawStage == CDDS_POSTPAINT) //绘制循环结束, 绘制item区域下方的非item区域
{
DrawBottomNonItemSpace(pLVCD);
*pResult = CDRF_SKIPDEFAULT;
return;
}
*pResult = CDRF_DODEFAULT;
}
//绘制插入的Item区域
void NxListCtrl::DrawSubItem(CDC *pDC, int nRow, int nCol, CRect &rectSubItem, bool bSelected, bool bFocus)
{
//判断区域是否合理
if (rectSubItem.Width() <= 0 || rectSubItem.Height() <= 0) return;
pDC->SetBkMode(TRANSPARENT);
int nSave = pDC->SaveDC();
bool bParity = (nRow % 2 == 0 ? true : false);
if (bSelected)
{
if (bFocus)
{
CBrush selectBrush;
selectBrush.CreateSolidBrush(RGB(51, 153, 255));
pDC->FillRect(&rectSubItem, &selectBrush);
}
else
{
CBrush selectNoFocusBrush;
selectNoFocusBrush.CreateSolidBrush(RGB(250, 250, 250));
pDC->FillRect(&rectSubItem, &selectNoFocusBrush);
}
}
else if (bParity)
{
CBrush oddBrush;
oddBrush.CreateSolidBrush(m_itemsInfor.at(nRow).at(nCol).bkgClor);
pDC->FillRect(&rectSubItem, &oddBrush);
}
else
{
CBrush normalBrush;
normalBrush.CreateSolidBrush(m_itemsInfor.at(nRow).at(nCol).bkgClor);
pDC->FillRect(&rectSubItem, &normalBrush);
}
// 画竖线
CPen pen;
pen.CreatePen(PS_SOLID, 1, RGB(236, 236, 236));
pDC->SelectObject(&pen);
pDC->MoveTo(rectSubItem.right - 1, rectSubItem.top);
pDC->LineTo(rectSubItem.right - 1, rectSubItem.bottom);
pDC->MoveTo(rectSubItem.left, rectSubItem.bottom - 1);
pDC->LineTo(rectSubItem.right, rectSubItem.bottom - 1);
pDC->RestoreDC(nSave);
//创建字体
CFont font;
font.CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE,0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("宋体"));
pDC->SelectObject(&font);
//设置文字颜色
pDC->SetTextColor(m_itemsInfor.at(nRow).at(nCol).frgClor);
//获取单元格内的字符
CString strText;
strText = GetItemText(nRow, nCol);
//重绘字体
rectSubItem.left = rectSubItem.left + 5;
pDC->DrawText(strText, strText.GetLength(), &rectSubItem, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS); //DT_CENTER DT_RIGHT
}