主要知识点就是mfc CListCtrl的LVN_GETDISPINFO和NM_CUSTOMDRAW消息处理
虚拟列表简单理解:我们维护自己想要展示的数据,系统根据展示情况来取数据,我们只要专注维护数据就可以了.
先放个效果图:
继承CMFCButton和CListCtrl
class CButtonEx : public CMFCButton
{
DECLARE_DYNAMIC(CButtonEx)
public:
CButtonEx(int iItem, int iSubItem, CRect rect);
virtual ~CButtonEx();
int m_iItem;
protected:
DECLARE_MESSAGE_MAP()
public:
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
};
class CVirualListCtrl : public CListCtrl
{
private:
CString m_strKey;
vector<CString> m_vtrData;
int m_iItemCounts;
int m_iSelLine;
UINT m_uId;
map<int, CButtonEx*> m_btnMap;
public:
CVirualListCtrl();
~CVirualListCtrl();
DECLARE_MESSAGE_MAP()
public:
BOOL setStringKey(CString);//设置一个特定内容,凡有是这个内容的行显示为红色
BOOL setVtrData(vector<CString>& vtrData);
afx_msg void OnLvnGetdispinfoList(NMHDR *pNMHDR, LRESULT *pResult);//处理
afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);//处理自绘事件
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
//添加在item中按钮
BOOL CreateItemButton(int iSubItem);
void UpdateListCtrlButtonText(int iItem);
void ReleaseItemButton();
};
源文件:
//
//ListCtrl中的按钮
IMPLEMENT_DYNAMIC(CButtonEx, CMFCButton)
CButtonEx::CButtonEx(int iItem, int iSubItem, CRect rect)
{
m_iItem = iItem;
}
CButtonEx::~CButtonEx()
{
}
BEGIN_MESSAGE_MAP(CButtonEx, CMFCButton)
END_MESSAGE_MAP()
LRESULT CButtonEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: 在此添加专用代码和/或调用基类
switch (message)
{
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
return TRUE;
case WM_LBUTTONUP:
{
CString strBuff;
strBuff.Format("当前行:%d",m_iItem);
MessageBox(strBuff, "提示", MB_OK);
}
return TRUE;
}
return CMFCButton::WindowProc(message, wParam, lParam);
}
//
//
//CVirualListCtrl
CVirualListCtrl::CVirualListCtrl()
{
m_iItemCounts = 23;
m_uId = WM_USER + 1000;
}
CVirualListCtrl::~CVirualListCtrl()
{
ReleaseItemButton();
}
BEGIN_MESSAGE_MAP(CVirualListCtrl, CListCtrl)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, &CVirualListCtrl::OnLvnGetdispinfoList)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CVirualListCtrl::OnNMCustomdraw)
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
BOOL CVirualListCtrl::setStringKey(CString strKey)
{
if (strKey.IsEmpty())
{
return FALSE;
}
m_strKey = strKey;
return TRUE;
}
BOOL CVirualListCtrl::setVtrData(vector<CString>& vtrData)
{
if (vtrData.empty())
{
return FALSE;
}
m_vtrData = vtrData;
SetItemCountEx(m_vtrData.size());
return TRUE;
}
void CVirualListCtrl::OnLvnGetdispinfoList(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
LV_ITEM* pItem = &(pDispInfo)->item;
if (pItem->mask & LVIF_TEXT)
{
//pItem->iSubItem为列号
switch (pItem->iSubItem)
{
case 0:
{
//pItem->iItem为行号
lstrcpyn(pItem->pszText, m_vtrData[pItem->iItem], pItem->cchTextMax);
}
break;
case 1:
{
if (0 == pItem->iItem)
{
lstrcpyn(pItem->pszText, "污源子的小故事", pItem->cchTextMax);
}
else if (5 == pItem->iItem)
{
lstrcpyn(pItem->pszText, "污源子的小故事", pItem->cchTextMax);
}
else
{
lstrcpyn(pItem->pszText, "污源子", pItem->cchTextMax);
}
}
break;
case 2:
{
lstrcpyn(pItem->pszText, "虚拟列表", pItem->cchTextMax);
}
break;
case 3:
{
UpdateListCtrlButtonText(pItem->iItem);
}
break;
default:
break;
}
}
}
void CVirualListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
*pResult = CDRF_DODEFAULT;
NMLVCUSTOMDRAW * lplvdr = (NMLVCUSTOMDRAW*)pNMHDR;
NMCUSTOMDRAW &nmcd = lplvdr->nmcd;
switch (lplvdr->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
{
//设置自定义绘制
*pResult = CDRF_NOTIFYITEMDRAW;
}
break;
case CDDS_ITEMPREPAINT:
{
//绘制item
//item的text判断是否是需要设置使用红色绘制
if (m_strKey == GetItemText((int)nmcd.dwItemSpec, 1))
{
lplvdr->clrText = RGB(0xff, 0, 0);
*pResult = CDRF_DODEFAULT;
}
}
break;
default:
break;
}
}
void CVirualListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
switch (nChar)
{
case VK_DOWN:
{
POSITION pos = GetFirstSelectedItemPosition();
int iItem = GetNextSelectedItem(pos) + 1;
//可以根据上下移动发送消息处理其他界面
//::PostMessage(GetParent()->GetSafeHwnd(), WM_KEY_DOWN, (WPARAM)iItem, 0);
}
break;
case VK_UP:
{
POSITION pos = GetFirstSelectedItemPosition();
int iItem = GetNextSelectedItem(pos) - 1;
//::PostMessage(GetParent()->GetSafeHwnd(), WM_KEY_UP, (WPARAM)iItem, 0);
}
break;
}
CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}
BOOL CVirualListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
//限制表头拖动
NMHEADER* pNMHeader = (NMHEADER*)lParam;
if (((pNMHeader->hdr.code == HDN_BEGINTRACKW) |
(pNMHeader->hdr.code == HDN_DIVIDERDBLCLICKW)))
{
*pResult = TRUE;
return TRUE;
}
return CListCtrl::OnNotify(wParam, lParam, pResult);
}
BOOL CVirualListCtrl::CreateItemButton(int iSubItem)
{
for (int i = 0; i < m_iItemCounts; i++)
{
//根据item设置按钮大小
CRect rect;
GetSubItemRect(i, iSubItem, LVIR_BOUNDS, rect);
rect.bottom = rect.top + 18;
//创建按钮
CButtonEx* pButton = new CButtonEx(i, iSubItem, rect);
pButton->Create(nullptr, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, rect, this, m_uId);
//pButton->SetFont(&m_btnFont);
m_uId++;
//垂直创建
m_btnMap.insert(make_pair(i, pButton));
}
return TRUE;
}
void CVirualListCtrl::UpdateListCtrlButtonText(int iItem)
{
int nItem = iItem - GetTopIndex();
if (nItem >= m_btnMap.size() || nItem < 0)
{
return;
}
CString strText;
strText.Format(_T("%d"), iItem);
m_btnMap[nItem]->m_iItem = iItem;
m_btnMap[nItem]->SetWindowText(strText);
}
void CVirualListCtrl::ReleaseItemButton()
{
map<int, CButtonEx*>::iterator iter = m_btnMap.begin();
while (iter != m_btnMap.end())
{
delete iter->second;
iter->second = nullptr;
iter++;
}
m_btnMap.clear();
}
//
调用代码:
CString strNumber;
for (int i = 0;i < 3000;i++)
{
strNumber.Format("%d", i);
m_vtrStrNumber.push_back(strNumber);
}
initVirualtListCtrl();
initVirualtListCtrl函数
void CMFCTestDlg::initVirualtListCtrl()
{
CRect rectWin, rectList;
GetClientRect(&rectWin);
rectList = rectWin;
rectList.bottom -= 40;
//样式设置为report,使用虚拟列表一定要设置LVS_OWNERDATA属性
m_listCtrlTwo.Create(WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_OWNERDATA, rectList, this, IDC_LIST2);
//插入列和设置列宽
int iColOneWidth = 40;
m_listCtrlTwo.InsertColumn(0, "行号", LVCFMT_RIGHT, iColOneWidth);
m_listCtrlTwo.InsertColumn(1, "第一列", LVCFMT_CENTER, (rectList.Width() - iColOneWidth) / 3);
m_listCtrlTwo.InsertColumn(2, "第二列", LVCFMT_CENTER, (rectList.Width() - iColOneWidth) / 3);
m_listCtrlTwo.InsertColumn(3, "第三列", LVCFMT_CENTER, (rectList.Width() - iColOneWidth) / 3 - 10);
//设置列表的显示样式
m_listCtrlTwo.SetExtendedStyle(m_listCtrlTwo.GetExStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
// 设置自定义属性
m_listCtrlTwo.setStringKey("污源子的小故事");
m_listCtrlTwo.CreateItemButton(3);
m_listCtrlTwo.setVtrData(m_vtrStrNumber);
}