列表分为列表头和列表项两个部分。
从列表头开始自绘。
class CWzdListHeader : public CHeaderCtrl
{
DECLARE_DYNAMIC(CWzdListHeader)
public:
CWzdListHeader();
virtual ~CWzdListHeader();
// 添加字段
void AddColumn(int nCol, LPCTSTR sName, int nFormat);
protected:
afx_msg void OnPaint();
LRESULT OnLayout(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
可以看到列表头自绘很简单,OnPaint里进行绘画,其中列表头的各个字段的显示属性在AddColumn函数中保存到一个multimap中,以便于绘画的时候一一对应。
AddColumn函数会在下面的InsertColumn中用到。
void CWzdListHeader::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect itemRect;
CBrush colomnBrush; // Column画刷
CPen linePen; // 分隔线画刷
CPen* pOldPen;
colomnBrush.CreateSolidBrush(COLOR_LISTHEAR);
linePen.CreatePen(PS_SOLID, 1, COLOR_WHITE);
pOldPen = dc.SelectObject(&linePen);
// 画出所有字段
int nItem = GetItemCount(); // 得到单元数量
int i = 0;
std::multimap<int, std::pair<LPCTSTR, int>>::iterator itor = g_columeMap.begin();
while (itor != g_columeMap.end() && i < nItem)
{
// 画分隔线,填充背景
GetItemRect(i, &itemRect);
if (0 != i)
{
dc.MoveTo(CPoint(itemRect.left, itemRect.top));
dc.LineTo(CPoint(itemRect.left, itemRect.bottom));
itemRect.left += 1;
}
dc.FillRect(&itemRect,&colomnBrush);
// 创建字体
CFont textFont;
CFont* pOldFont;
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(RGB(0,0,0));
textFont.CreateFont(20,0,0,0,0,FALSE,FALSE,0,0,0,0,0,0, DEFAULT_TEXTFACE);
pOldFont = dc.SelectObject(&textFont);
// 画字
TEXTMETRIC metric;
int ofst = 0;
dc.GetTextMetrics(&metric);
ofst = itemRect.Height() - metric.tmHeight;
itemRect.OffsetRect(0, ofst / 2);
dc.DrawText(itor->second.first, &itemRect, itor->second.second);
dc.SelectObject(pOldFont);
textFont.DeleteObject();
itor++;
i++;
}
//画头部剩余部分
CRect lastRect;
GetClientRect(&lastRect);
lastRect.left = itemRect.right + 1;
dc.MoveTo(CPoint(itemRect.right, lastRect.top));
dc.LineTo(CPoint(itemRect.right, itemRect.bottom));
dc.FillRect(&lastRect, &colomnBrush);
colomnBrush.DeleteObject();
linePen.DeleteObject();
dc.SelectObject(pOldPen);
}
然后就是列表项的自绘
class CWzdList : public CListCtrl
{
DECLARE_DYNAMIC(CWzdList)
public:
CWzdList();
virtual ~CWzdList();
int InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1);
protected:
CWzdListHeader m_listHeader;
virtual void PreSubclassWindow();
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
PreSubclassWindow和OnCreate都是用来在创建之前改变列表的风格,前者是绑定的时候调用,后者是动态创建的时候调用。
void CWzdList::PreSubclassWindow()
{
SetWindowLong(this->m_hWnd, GWL_STYLE, GetStyle() | LVS_OWNERDRAWFIXED | LVS_REPORT);
SetExtendedStyle(GetExtendedStyle()| LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
CHeaderCtrl *pHeader = GetHeaderCtrl();
m_listHeader.SubclassWindow(pHeader->GetSafeHwnd());
CListCtrl::PreSubclassWindow();
}
int CWzdList::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
SetWindowLong(this->m_hWnd, GWL_STYLE, GetStyle() | LVS_OWNERDRAWFIXED | LVS_REPORT);
SetExtendedStyle(GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
CHeaderCtrl *pHeader = GetHeaderCtrl();
m_listHeader.SubclassWindow(pHeader->GetSafeHwnd());
return 0;
}
同样添加了LVS_EX_FULLROWSELECT 属性后,可以在DrawItem种进行自绘。
void CWzdList::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
LV_COLUMN lvColumn, lvColumnPre ;
ZeroMemory(&lvColumn, sizeof(lvColumn));
ZeroMemory(&lvColumnPre, sizeof(lvColumnPre));
lvColumn.mask = LVCF_WIDTH | LVCF_FMT;
lvColumnPre.mask = LVCF_WIDTH | LVCF_FMT;
// 获取DC和列表尺寸
CDC* pDC;
CRect rtClient;
pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
GetClientRect(&rtClient);
std::multimap<int, std::pair<LPCTSTR, int>>::iterator itor = g_columeMap.begin();
for (int nCol = 0; GetColumn(nCol, &lvColumn); nCol++, itor++)
{
// 获取列表项尺寸,全部LVIR_BOUNDS
CRect rcItem;
GetSubItemRect(lpDrawItemStruct->itemID, nCol, LVIR_BOUNDS, rcItem);
// 初始化列表项, 获取列表项文字
LV_ITEM lvItem;
WCHAR lpBuffer[256];
ZeroMemory(&lvItem, sizeof(lvItem));
lvItem.iItem = lpDrawItemStruct->itemID;
lvItem.mask = LVIF_TEXT | LVIF_PARAM;
lvItem.iSubItem = nCol;
lvItem.pszText = lpBuffer;
lvItem.cchTextMax = sizeof(lpBuffer);
VERIFY(GetItem(&lvItem));
// 画选中和不选中的Item
if (lpDrawItemStruct->itemState & ODS_SELECTED)
{
pDC->FillSolidRect(&rcItem, COLOR_LISTITEM_SELECT);
}
else
{
pDC->FillSolidRect(rcItem, COLOR_LISTITEM_NORMAL);
}
// 创建字体
CFont nFont;
CFont* pOldFont;
nFont.CreateFont(17,0,0,0,0,FALSE,FALSE,0,0,0,0,0,0, DEFAULT_TEXTFACE);
pOldFont = pDC->SelectObject(&nFont);
// 获取列表项尺寸,文字部分LVIR_LABEL
GetSubItemRect(lpDrawItemStruct->itemID, nCol, LVIR_LABEL, rcItem);
// 画字
CString strText(lpBuffer);
int uFormat = itor->second.second;
pDC->SetTextColor(COLOR_BLACK);
pDC->DrawText(strText, strText.GetLength(), &rcItem, uFormat);
}
}
最后重写InsertColumn来储存插入的列表头关键字属性
int CWzdList::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat /*= LVCFMT_LEFT*/, int nWidth /*= -1*/, int nSubItem /*= -1*/)
{
m_listHeader.AddColumn(nCol, lpszColumnHeading, nFormat);
return CListCtrl::InsertColumn(nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);
}