设置CListCtrl选中行
m_list.SetItemState(nIndex, LVIS_FOCUSED | LVIS_SELECTED,LVIS_FOCUSED | LVIS_SELECTED);
m_list.SetSelectionMark(nIndexs);
取消CListCtrl选中行
m_list.SetItemState(nIndex, 0, LVIS_SELECTED);
m_list.SetSelectionMark(-1);
CListCtrl选中行改变响应
响应LVN_ITEMCHANGED消息,改变选中行时会有好几个LVN_ITEMCHANGED消息,加入判断即可:
void CMyDlg::OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
if(pNMListView->uChanged == LVIF_STATE)
{
if(pNMListView->uNewState)
{
int nIndex = pNMListView->iItem;
//要进行的操作
}
}
*pResult = 0;
}
设置CListCtrl显示表格边框、整行选中
m_listInfo.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
设置行高
CImageList imgList; //为ClistCtrl设置一个图像列表,以设置行高
imgList.Create(IDB_BITMAP4, 16, 1, RGB(255, 255, 255)); // IDB_BITMAP4 是 16*16的 所以行高16像素
CListCtrl &listCtrl=(CListCtrl&)GetListCtrl();
listCtrl.SetImageList(&imgList,LVSIL_SMALL);
CListCtrl获取选中行的行号
POSITION pos = pList->GetFirstSelectedItemPosition();
if (pos == NULL)
TRACE0("No items were selected!\n");
else
{
while (pos)
{
int nItem = pList->GetNextSelectedItem(pos);
TRACE1("Item %d was selected!\n", nItem);
// you could do your own processing on nItem here
}
}
CListCtrl复选框操作
有时需要在item前面添加一个CheckBox,供用户选择,然后对所有选中项进行处理。
这里涉及到两个问题:第一个,如何添加CheckBox风格;第二个,如何判断某一行的CheckBox状态是否发生改变。
对于第一个问题,在基本操作里已经有所阐述了,即通过SetExtendedStyle函数添加LVS_EX_CHECKBOXES扩展风格。
这里重点探讨第二个问题,首先,操作复选框状态的有两个函数:
BOOL GetCheck(int nItem)-------获取复选框状态
BOOL SetCheck( int nItem, BOOL fCheck = TRUE )-------设置复选框状态
其次,我们要搞清楚以下四点:
① 当列表的项item改变时,控件会向父窗口发送LVN_ITEMCHANGED消息,因此可以在LVN_ITEMCHANGED消息的响应函数中对复选框的状态进行处理(查询或设置)。
② 鼠标点击CheckBox时,消息的顺序是 NM_CLICK -> LVN_ITEMCHANGED,即CheckBox的状态是在 NM_CLICK消息函数结束后才会发生变化,在NM_CLICK中使用GetCheck无效。
③ 鼠标点击Item(非CheckBox区域)时,消息的顺序是 LVN_ITEMCHANGED -> NM_CLICK。
④ 调用InsertItem 函数时,也会产生LVN_ITEMCHANGED消息。鉴于此,通常会自定义一个BOOL型变量m_bHit 来判断是点击操作还是插入操作,该变量初始赋FALSE,当有鼠标点击item时赋TRUE, 检测完是否有CheckBox被点击后重新复位为FALSE。
void CXXXX::OnNMClickXXXX(NMHDR *pNMHDR, LRESULT *pResult)
{
//获取单击所在的行号
//找出鼠标位置
DWORD dwPos = GetMessagePos();
CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
m_listCtrl.ScreenToClient(&point);
//定义结构体
LVHITTESTINFO lvinfo;
lvinfo.pt = point;
//获取行号信息
int nItem = m_listCtrl.HitTest(&lvinfo);
if(nItem != -1)
m_itemSel = lvinfo.iItem; //当前行号
//判断是否点击在CheckBox上
if(lvinfo.flags == LVHT_ONITEMSTATEICON)
m_bHit = TRUE;
*pResult = 0;
}
void CXXXX::OnLvnItemchangedXXXX(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
//判断m_bHit,即是否点击了CheckBox
if(m_bHit)
{
m_bHit = FALSE; //复位
if(m_listCtrl.GetCheck(m_itemSel))
{ //CheckBox被选中
//do your own processing
}
else
{ //CheckBox取消选择
//do your own processing
}
}
*pResult = 0;
}
方法二
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LS_DO, OnItemchangedLsDo)
void CSysPage5::OnItemchangedLsDo(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if((pNMListView->uOldState & INDEXTOSTATEIMAGEMASK(1)) /* old state : unchecked */
&& (pNMListView->uNewState & INDEXTOSTATEIMAGEMASK(2)) /* new state : checked */
)
{
//Item被选中(Check)(pNMListView->iItem)
g_nc.OutBitOn(pNMListView->iItem);
}
else if((pNMListView->uOldState & INDEXTOSTATEIMAGEMASK(2)) /* old state : checked */
&& (pNMListView->uNewState & INDEXTOSTATEIMAGEMASK(1)) /* new state : unchecked */
)
{
//Item未被选中(UnCheck)(pNMListView->iItem)
g_nc.OutBitOff(pNMListView->iItem);
}
else
{
//未改变选中状态(pNMListView->iItem)
}
*pResult = 0;
}
动态设置选中行的字体颜色
有时可能需要设置某行的文字为特殊颜色,以表示某种特殊含义,比如正在下载的信息用绿色,暂停下载的用灰色。
① 当控件绘制时,会发送NM_CUSTOMDRAW 消息,该消息的消息响应函数为.
②其中,pNMHDR为输入参数,其指向NMLVCUSTOMDRAW结构体,该结构包含了很多信息,包括字体颜色、背景等等,特别是第一个成员,为NMCUSTOMDRAW结构体变量,其包含了Current drawing stage
③ pResult为输出参数,该参数决定了接下来向windows发送什么消息(与绘制有关的),通过发送该消息我们可以进入下一步需要的处理阶段。具体输出哪个值取决于Current drawing stage
④ 有一点必须注意(英文的,我觉得看起来比翻译过来更精确):
One thing to keep in mind is you must always check the draw stage before doing anything else, because your handler will receive many messages, and the draw stage determines what action your code takes.
下面我们来看看如何修改某一行的字体颜色:
1) 首先,我们应该明白要修改字体颜色,应该在pre-paint 阶段来完成
2) 因此,在消息响应函数中,我们首先判断是否处于pre-paint stage(即pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT),然后通过修改输出值pResult 的值来通知windows我们需要处理每个item的消息(即设置 *pResult = CDRF_NOTIFYITEMDRAW)。
3) 再次进入消息响应函数时,我们判断是否处于Item的pre-paint stage(即pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT),如果是则进行相关处理,即修改字体颜色等等。
4) 处理完了后重新设置 *pResult = CDRF_DODEFAULT,表示我们不再需要其他特殊的消息了,默认执行即可。
void CXXXX::OnNMCustomdrawXXXX(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's pre-paint stage,
// then tell Windows we want messages for every item.
if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
{
// This is the notification message for an item.
//处理,将item改变背景颜色
if( /*符合条件*/ )
pLVCD->clrText = RGB(255,0,255);
*pResult = CDRF_DODEFAULT;
}
}
如果你的列表的信息在不断变化(即用SetItemText不断修改),那么也就实现了动态改变了,否则需要在合适的地方调用重绘函数:
BOOL RedrawItems( int nFirst, int nLast )
表示在nFirst和nLast之间的行需要进行重绘。
设置选中行的背景颜色
设置选中行的背景颜色,可以将选中行以特殊颜色显示,容易明白当前处理的是哪一行。尽管有高亮,但是高亮是基于焦点的,如果你选中了某一行,然后焦点转移了,这是就无法判断你选的是哪一行了。
设置选中行的背景颜色的方法和第四节中讲的修改字体颜色的方法是相似的,都是利用Custom Draw。这里涉及到设置当前选中行为特殊颜色,同时要恢复前一次选中行的颜色,否则就乱了。因此需要记录前一次选中行、当前选中行的行号,相信通过前面的总结,这点并不难实现。然后在当前选中行和前一次选中行之间进行重绘即可。
void CXXXX::OnNMClickXXXX(NMHDR *pNMHDR, LRESULT *pResult)
{
//…………
//重绘item,更改背景颜色
int nFirst = min(m_itemSel,m_itemForeSel);
int nLast = max(m_itemSel,m_itemForeSel);
m_listCtrl.RedrawItems(nFirst, nLast); //在前一次选中的item和当前选中的Item之间进行重绘
*pResult = 0;
}
void CXXXX::OnNMCustomdrawXXXX(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
{
// This is the notification message for an item.
//处理,将item改变背景颜色
if(m_itemSel == pLVCD->nmcd.dwItemSpec)
{ //当前选中的item
pLVCD->clrTextBk = RGB(255,0,0);
}
else if(m_itemForeSel == pLVCD->nmcd.dwItemSpec)
{ //前一次选中的item,恢复为白色
pLVCD->clrTextBk = RGB(255,255,255);
}
*pResult = CDRF_DODEFAULT;
}
}