ListControl常用操作汇总


本文根据本人在项目中的应用,来谈谈CListCtrl的部分用法及技巧。当初学习时,查了很多资料,零零碎碎的作了些记录,现在主要是来做个总结,方便以后查阅。主要包括以下十三点内容:基本操作、获取选中行的行号、复选框操作、动态设置选中行的字体颜色、设置选中行的背景颜色、禁止拖动表头、让第一列居中显示、设置行高与字体、虚拟列表技术、点击表头时进行归类、向上与向下移动、动态调整大小问题、避免闪烁问题。

      分为两篇来进行总结。本篇重点总结:基本操作获取选中行的行号复选框操作动态设置选中行的字体颜色设置选中行的背景颜色


view plaincopy
  1. POSITION pos = pList->GetFirstSelectedItemPosition();  
  2. if (pos == NULL)  
  3.    TRACE0("No items were selected!\n");  
  4. else  
  5. {  
  6.    while (pos)  
  7.    {  
  8.       int nItem = pList->GetNextSelectedItem(pos);  
  9.       TRACE1("Item %d was selected!\n", nItem);  
  10.       // you could do your own processing on nItem here  
  11.    }  
  12. }  

        第二种方法,该示例来自我的项目,可作修改后使用。

[cpp] view plaincopy
  1. //获取单击所在的行号  
  2. //找出鼠标位置  
  3. DWORD dwPos = GetMessagePos();  
  4. CPoint point( LOWORD(dwPos), HIWORD(dwPos) );  
  5. m_listCtrl.ScreenToClient(&point);  
  6.   
  7.  //定义结构体  
  8. LVHITTESTINFO lvinfo;  
  9. lvinfo.pt = point;  
  10.   
  11.  //获取行号信息  
  12. int nItem = m_listCtrl.HitTest(&lvinfo);  
  13. if(nItem != -1)  
  14.     m_itemSel = lvinfo.iItem;   //当前行号  

      对于LVHITTESTINFO 结构体,其有四个成员,在上述HitTest调用中,其第一个成员作为输入,另外三个作为输出。具体变量含义可查看MSDN。

[cpp] view plaincopy
  1. typedef struct _LVHITTESTINFO {  
  2.     POINT pt;  
  3.     UINT flags;  
  4.     int iItem;  
  5.     int iSubItem;  
  6. } LVHITTESTINFO, *LPLVHITTESTINFO;  

view plaincopy
  1. void CXXXX::OnNMClickXXXX(NMHDR *pNMHDR, LRESULT *pResult)  
  2. {  
  3.     //获取单击所在的行号  
  4.     //找出鼠标位置  
  5.     DWORD dwPos = GetMessagePos();  
  6.     CPoint point( LOWORD(dwPos), HIWORD(dwPos) );  
  7.     m_listCtrl.ScreenToClient(&point);  
  8.     //定义结构体  
  9.     LVHITTESTINFO lvinfo;  
  10.     lvinfo.pt = point;  
  11.     //获取行号信息  
  12.     int nItem = m_listCtrl.HitTest(&lvinfo);  
  13.     if(nItem != -1)  
  14.     m_itemSel = lvinfo.iItem;   //当前行号  
  15.   
  16.      //判断是否点击在CheckBox上  
  17.      if(lvinfo.flags == LVHT_ONITEMSTATEICON)  
  18.           m_bHit = TRUE;  
  19.   
  20.      *pResult = 0;  
  21. }  
  22.   
  23. void CXXXX::OnLvnItemchangedXXXX(NMHDR *pNMHDR, LRESULT *pResult)  
  24. {  
  25.     LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR);  
  26.     //判断m_bHit,即是否点击了CheckBox  
  27.     if(m_bHit)  
  28.     {  
  29.             m_bHit = FALSE;     //复位  
  30.   
  31.         if(m_listCtrl.GetCheck(m_itemSel))  
  32.         {       //CheckBox被选中  
  33.             //do your own processing   
  34.         }  
  35.         else  
  36.         {      //CheckBox取消选择  
  37.             //do your own processing   
  38.         }  
  39.     }  
  40.   
  41.     *pResult = 0;  
  42. }  


http://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra    

         然后,来谈谈我的方法,这里主要谈对选中行的字体颜色进行动态修改,当然也是我通过上面文章和自己实践结合得出的。

        我们需要搞清楚以下几点(可以结合下面修改某一行的字体颜色的方法来看):

① 当控件绘制时,会发送NM_CUSTOMDRAW 消息,该消息的消息响应函数为

[cpp] view plaincopy
  1. void CXXXX::OnNMCustomdrawXXXX(NMHDR *pNMHDR, LRESULT *pResult)  
  2. {  
  3.     LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast(pNMHDR);  
  4.     // TODO: Add your control notification handler code here  
  5.     *pResult = CDRF_DODEFAULT;  
  6.        //………………  
  7.  }  

②其中,pNMHDR为输入参数,其指向NMLVCUSTOMDRAW结构体,该结构包含了很多信息,包括字体颜色、背景等等,特别是第一个成员,为NMCUSTOMDRAW结构体变量,其包含了Current drawing stage(不知道怎么编译比较好),其可能的值如下图(截自MSDN)所示


pResult为输出参数,该参数决定了接下来向windows发送什么消息(与绘制有关的),通过发送该消息我们可以进入下一步需要的处理阶段。具体输出哪个值取决于Current drawing stage,其可能的值如下图(截自MSDN)所示


④ 有一点必须注意(英文的,我觉得看起来比翻译过来更精确):

     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.

        下面我们来看看如何修改某一行的字体颜色:

①  首先,我们应该明白要修改字体颜色,应该在pre-paint 阶段来完成

② 因此,在消息响应函数中,我们首先判断是否处于pre-paint stage(即pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT),然后通过修改输出值pResult 的值来通知windows我们需要处理每个item的消息(即设置 *pResult = CDRF_NOTIFYITEMDRAW)。

③ 再次进入消息响应函数时,我们判断是否处于Item的pre-paint stage(即pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT),如果是则进行相关处理,即修改字体颜色等等。

④ 处理完了后重新设置 *pResult = CDRF_DODEFAULT,表示我们不再需要其他特殊的消息了,默认执行即可。

         示例如下:

[cpp] view plaincopy
  1. void CXXXX::OnNMCustomdrawXXXX(NMHDR *pNMHDR, LRESULT *pResult)  
  2. {  
  3.     LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast(pNMHDR);  
  4.     *pResult = CDRF_DODEFAULT;  
  5.   
  6.     // First thing - check the draw stage. If it's the control's pre-paint stage,   
  7.     // then tell Windows we want messages for every item.  
  8.     if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )  
  9.     {  
  10.         *pResult = CDRF_NOTIFYITEMDRAW;  
  11.     }  
  12.     else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )  
  13.     {  
  14.         // This is the notification message for an item.   
  15.     //处理,将item改变背景颜色  
  16.         if/*符合条件*/ )  
  17.         pLVCD->clrText = RGB(255,0,255);  
  18.           
  19.            *pResult = CDRF_DODEFAULT;  
  20.     }  
  21. }  

        上面谈的方法主要用于设置静态字体颜色,当然,如果你的列表的信息在不断变化(即用SetItemText不断修改),那么也就实现了动态改变了,否则需要在合适的地方调用重绘函数:

                         BOOL RedrawItems( int nFirst, int nLast )

表示在nFirst和nLast之间的行需要进行重绘。


view plaincopy
    1. void CXXXX::OnNMClickXXXX(NMHDR *pNMHDR, LRESULT *pResult)  
    2. {  
    3.     //…………  
    4.   
    5.     //重绘item,更改背景颜色  
    6.     int nFirst = min(m_itemSel,m_itemForeSel);  
    7.     int nLast = max(m_itemSel,m_itemForeSel);  
    8.     m_listCtrl.RedrawItems(nFirst, nLast);  //在前一次选中的item和当前选中的Item之间进行重绘  
    9.   
    10.     *pResult = 0;  
    11. }  
    12. void CXXXX::OnNMCustomdrawXXXX(NMHDR *pNMHDR, LRESULT *pResult)  
    13. {  
    14.     LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast(pNMHDR);  
    15.     *pResult = CDRF_DODEFAULT;  
    16.   
    17.     // First thing - check the draw stage. If it's the control's prepaint  
    18.         // stage, then tell Windows we want messages for every item.  
    19.     if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )  
    20.     {  
    21.              *pResult = CDRF_NOTIFYITEMDRAW;  
    22.     }  
    23.         else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )  
    24.     {  
    25.                 // This is the notification message for an item.   
    26.         //处理,将item改变背景颜色  
    27.         if(m_itemSel == pLVCD->nmcd.dwItemSpec)        
    28.         {   //当前选中的item  
    29.             pLVCD->clrTextBk = RGB(255,0,0);  
    30.         }  
    31.         else if(m_itemForeSel == pLVCD->nmcd.dwItemSpec)  
    32.         {   //前一次选中的item,恢复为白色  
    33.             pLVCD->clrTextBk = RGB(255,255,255);  
    34.         }  
    35.   
    36.                 *pResult = CDRF_DODEFAULT;  
    37.     }