zoukankan      html  css  js  c++  java
  • MFC总结之CListCtrl用法及技巧(二)

           续第一篇:MFC总结之CListCtrl用法及技巧(一) http://blog.csdn.net/zwgdft/article/details/7560592

            本篇重点介绍:止拖动表头、让第一列居中显示、设置行高与字体、虚拟列表技术、点击表头时进行归类、向上与向下移动、动态调整大小问题、避免闪烁问题

     6、禁止拖动表头

           重载OnNotify消息响应函数,屏蔽两个消息通知码:HDN_BEGINTRACKW 和HDN_DIVIDERDBLCLICKW。示例如下:

    [cpp] view plaincopy
     
    1. BOOL CXXXX::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)  
    2. {  
    3.     // TODO: Add your specialized code here and/or call the base class  
    4.     //屏蔽两个消息通知码,使得禁止拖动List表头  
    5.     NMHEADER* pNMHeader = (NMHEADER*)lParam;  
    6.     if(((pNMHeader->hdr.code == HDN_BEGINTRACKW) |   
    7.              (pNMHeader->hdr.code == HDN_DIVIDERDBLCLICKW)))  
    8.     {  
    9.         *pResult = TRUE;  
    10.         return TRUE;  
    11.     }  
    12.   
    13.     return CDialog::OnNotify(wParam, lParam, pResult);  
    14. }  

     7、让第一列居中显示

            在插入列时,我们可以通过参数nFormat来设置文本居中显示,但是这种设置对于第一列是没有作用的。这时我们可以考虑将我们的内容从第二列开始插入(设置为居中显示)。先插入第一列,然后删除第一列,这样原先的第二列就充当了第一列。

     8、设置行高和字体

            设置CListCtrl的行高没有函数接口,可以通过自绘来实现,但是比较麻烦。有一个比较简单的方法是通过使用一个空白的图像将行撑起来,使其高度发生变化。示例如下:

    [cpp] view plaincopy
     
    1. CImageList m_image;  
    2. m_image.Create(1,24,ILC_COLOR32,1,0);  
    3. m_listInfo.SetImageList(&m_image, LVSIL_SMALL);  

            对于字体的设置,我们可以使用SetFont函数来实现。以修改CListView的字体为例,在OnInitialUpdate函数中插入列之前调用SetFontSelf函数(该函数自定义,如下示例所示)。首先创建一个字体,然后调用SetFont进行设置。需要注意的是,在退出时需要delete 掉创建的字体,避免内存泄露。

    [cpp] view plaincopy
     
    1. //设置字体和大小  
    2. void CMyListView::SetFontSelf(int nHeight, LPCTSTR lpszFacename)  
    3. {  
    4.     //先删除原有字体  
    5.     if(m_font != NULL)  
    6.         delete m_font;  
    7.     m_font = new CFont;  
    8.     //创建字体  
    9.     m_font->CreateFont(  
    10.         nHeight,                   // nHeight  
    11.         0,                         // nWidth  
    12.         0,                         // nEscapement  
    13.         0,                         // nOrientation  
    14.         FW_NORMAL,                 // nWeight  
    15.         FALSE,                     // bItalic  
    16.         FALSE,                     // bUnderline  
    17.         0,                         // cStrikeOut  
    18.         ANSI_CHARSET,              // nCharSet  
    19.         OUT_DEFAULT_PRECIS,        // nOutPrecision  
    20.         CLIP_DEFAULT_PRECIS,       // nClipPrecision  
    21.         DEFAULT_QUALITY,           // nQuality  
    22.         DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily  
    23.         lpszFacename);             // lpszFacename  
    24.   
    25.     //设置字体  
    26.     CListCtrl &theCtrl = GetListCtrl();     //获取控制权,引用变量  
    27.     theCtrl.SetFont(m_font, TRUE);  
    28. }  

     9、虚拟列表技术

            给一个链接,介绍的比较详细:http://hi.baidu.com/qi_xian/blog/item/929b04ce27d02c0592457ef8.html

           当数据量大时,使用InsertItem插入数据的过程是很漫长的。这时我们有两个方法来解决该问题:一是使用CListCtrl的虚拟列表技术,二是采用分页显示的方法。对于虚拟列表技术,上述链接中的文章讲的很详细,我用过它的比较简单的方法,后来改用了分页方法。

           使用虚拟列表技术,有三点需要搞清楚:

     使用虚拟技术时,需要将CListCtrl控件的Owner Data属性设置为ture。

     给虚拟列表添加元素时,不需要使用InserItem函数,通过调用SetItemCount设置数据总个数,然后由系统产生不同的消息,在相应的消息响应函数中完成插入工作。

     虚拟列表向父窗口发送的消息有三种: ⑴ 当它需要数据时,发送LVN_GETDISPINFO消息; ⑵ 当用户试图查找某个元素时,发送LVN_ODFINDITEM消息; ⑶当需要缓冲数据时,发送 LVN_ODCACHEHINT消息。    

            当我们使用LVN_GETDISPINFO 的消息处理函数来插入元素时,必须首先检查列表请求的是什么数据(如LVIF_TEXT、LVIF_IMAGE等),然后插入不同的子项。示例如下:

    [cpp] view plaincopy
     
    1. void CDataAnalysis::OnLvnGetdispinfoAnalysisList(NMHDR *pNMHDR, LRESULT *pResult)  
    2. {  
    3.     NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);  
    4.     // TODO: Add your control notification handler code here  
    5.     LV_ITEM* pItem= &(pDispInfo)->item;  
    6.     int iItemIndex= pItem->iItem;  
    7.     size_t converted = 0;  
    8.     wchar_t wStr[30];            //Unicode字符串  
    9.     if (pItem->mask & LVIF_TEXT) //字符串缓冲区有效  
    10.     {  
    11.         switch(pItem->iSubItem)  
    12.         {  
    13.         case 0: //填充数据项的名字,xxxxx表示要填充的字符  
    14.             mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);  
    15.             lstrcpy(pItem->pszText,wStr);  
    16.             break;  
    17.         case 1: //填充子项1  
    18.             mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);  
    19.             lstrcpy(pItem->pszText,wStr);  
    20.             break;  
    21.         case 2: //填充子项2  
    22.             mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);  
    23.             lstrcpy(pItem->pszText,wStr);  
    24.             break;  
    25.         case 3: //填充子项3  
    26.             lstrcpy(pItem->pszText,xxxxxx);  
    27.             break;  
    28.         }  
    29.     }  
    30.   
    31.     *pResult = 0;  
    32. }  

     10、点击表头时进行归类排序

             系统通过发送LVM_SORTITEMS消息来处理归类问题,在该消息的处理函数中需要调用一个回调函数,这个回调函数需要我们来设计,以完成不同的归类方法。回调函数原型如下:

                          int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)

              针对上述回调函数,有以下几点需要搞清楚:

     对于参数lparam1和lparam2,分别为CListCtrl的两行数据,是用于比较的对象。通过CListCtrl的成员函数SetItemData来设置,该函数原型:

                         int SetItemData(int nIndex,  DWORD_PTR dwItemData )

    其第一个参数为行号,第二个参数指明了该行对应的参数。参数dwItemData 通常设为一行参数的数组,如: pData[2][2] = {{1, 3},{2, 3}}; 每次使用pData[i]作为dwItemData。

     对于参数lParamSort,用于指明列项,即第几列。该参数和回调函数一同通过CListCtrl的成员函数SortItems来设置,其函数原型为:

                        BOOL SortItems( PFNLVCOMPARE pfnCompare,DWORD_PTR dwData )

    参数 pfnCompare 为回调函数入口地址, 参数dwData 为列项。

    ③ SetItemData在初始插入数据时进行调用来设置,SortItems则在点击列表头时响应的消息处理函数中进行设置。

    示例如下:

    [cpp] view plaincopy
     
    1. //初始化列表视图控件  
    2. BOOL CDataAnalysis::InitListCtl()  
    3. {  
    4.     //其他处理,包括设置风格,插入列等等  
    5.     //插入行  
    6.     for(int i=0; i<LineNum; i++)  
    7.     {  
    8.         //要将char*转换为wchar_t*  
    9.         mbstowcs_s(&converted, wStr, 30, m_analysis[i].Date, _TRUNCATE);  
    10.         m_listAnalysis.InsertItem(i, wStr);                             //日期  
    11.         mbstowcs_s(&converted, wStr, 30, m_analysis[i].Time, _TRUNCATE);  
    12.         m_listAnalysis.SetItemText(i, 1, wStr);                         //时间  
    13.         mbstowcs_s(&converted, wStr, 30, m_analysis[i].ID, _TRUNCATE);  
    14.         m_listAnalysis.SetItemText(i, 2, wStr);                         //ID  
    15.         m_listAnalysis.SetItemText(i, 3, m_analysis[i].lpszEvent);      //事件  
    16.   
    17.         //设置回调函数的参数  
    18.         m_listAnalysis.SetItemData(i, (LPARAM)(m_analysis+i));  
    19.     }  
    20.   
    21.     return TRUE;  
    22. }  
    23. void CDataAnalysis::OnHdnItemclickAnalysisList(NMHDR *pNMHDR, LRESULT *pResult)  
    24. {  
    25.     LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);  
    26.     // TODO: Add your control notification handler code here  
    27.   
    28.     //设置回调函数的参数和入口地址  
    29.     m_listAnalysis.SortItems(SortFunc, phdr->iItem);  
    30.   
    31.     *pResult = 0;  
    32. }  
    33. //排序的回调函数  
    34. int CALLBACK SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)  
    35. {  
    36.     int result;     //返回值  
    37.   
    38.     //两行的参数,用于比较  
    39.     ANALYSISFORMAT* pAnalysis1 = (ANALYSISFORMAT*)lParam1;  
    40.     ANALYSISFORMAT* pAnalysis2 = (ANALYSISFORMAT*)lParam2;  
    41.   
    42.     //排序  
    43.     switch(lParamSort)  
    44.     {  
    45.     case 0:     //日期  
    46.         result = strcmp(pAnalysis1->Date, pAnalysis2->Date);  
    47.         break;  
    48.     case 1:     //时间  
    49.         result = strcmp(pAnalysis1->Time, pAnalysis2->Time);  
    50.         break;  
    51.     case 2:     //ID  
    52.         result = strcmp(pAnalysis1->ID, pAnalysis2->ID);  
    53.         break;  
    54.     case 3:     //事件  
    55.         result = wcscmp(pAnalysis1->lpszEvent, pAnalysis2->lpszEvent);  
    56.         break;  
    57.     default:  
    58.         break;  
    59.     }  
    60.   
    61.     return result;  
    62. }  

     11、向上与向下移动

            有时需要向上或向下移动表项内容,这里给出向上移动的方法,向下移动的方法类似。

    ① 利用第2节所述的内容获取行号nItem,判断行号是否为行首,如果不是行首则进入②;

    ② 获取第nItem行的所有子项内容;

    ③ 删除第nItem行,并在nItem-1的位置重新插入原先的第nItem行的内容;

    ④ 使nItem-1的位置高亮显示

    示例如下:

    [cpp] view plaincopy
     
    1. /*************************上移子项**************************/  
    2. void CStudyTestDlg::OnPageup()   
    3. {  
    4.     if (nItem == 0)  
    5.     {  
    6.         MessageBox("该子项已经位于第一行!");  
    7.         return;  
    8.     }  
    9.   
    10.     // 提取内容  
    11.     CString temp[4];  
    12.     int i;  
    13.     for(i=0;i<4;i++)  
    14.         temp[i] = m_ListCtrl.GetItemText(nItem, i);  
    15.   
    16.     // 删除  
    17.     m_ListCtrl.DeleteItem(nItem);  
    18.   
    19.     // 在nItem-1位置处插入  
    20.     for (i=0; i<4; i++)  
    21.         m_ListCtrl.SetItemText(nItem-1,i,temp[i]);  
    22.   
    23.     //高亮显示  
    24.     UINT flag = LVIS_SELECTED|LVIS_FOCUSED;  
    25.     m_ListCtrl.SetItemState(--nItem, flag, flag);  
    26. }  
    27.   
    28. /*************************下移子项**************************/  
    29. void CStudyTestDlg::OnPagedown()   
    30. {  
    31.     if (nItem == m_ListCtrl.GetItemCount()-1)  
    32.     {  
    33.         MessageBox("该子项已经位于最后一行!");  
    34.         return;  
    35.     }  
    36.   
    37.     // 提取内容  
    38.     CString temp[4];  
    39.     int i;  
    40.     for (i=0; i<4; i++)  
    41.         temp[i] = m_ListCtrl.GetItemText(nItem, i);  
    42.   
    43.     // 删除  
    44.     m_ListCtrl.DeleteItem(nItem);  
    45.   
    46.     // 在nItem+1位置处插入  
    47.     for (i=0; i<4; i++)  
    48.         m_ListCtrl.SetItemText(nItem+1, i,temp[i]);  
    49.   
    50.     //高亮显示  
    51.     UINT flag = LVIS_SELECTED|LVIS_FOCUSED;  
    52.     m_ListCtrl.SetItemState(++nItem, flag, flag);  
    53. }  

     12、避免闪烁问题

               这个问题在我的前面一篇博文有提到。

      http://blog.csdn.net/zwgdft/article/details/7394318

     

    13、动态调整大小

            有时由于不确定软件运行时的电脑屏幕大小,需要根据屏幕大小动态设置CListCtrl控件的大小。动态大小的设置时,需要注意不要将高度和宽度设置的超过区域限制,否则就没有滚动条了,导致部分内容无法查看。以我遇到的一个例子来说,其情况见第12节提到的那篇博文所述:将View划分为三个窗格,在左上角View上有个CPropertySheet,其上有几个CPropertyPage,每个属性页上有个CListCtrl,供用户查看信息。那么这时需要设置的CListCtrl的大小即为:

                                                  宽度 = 左上角View宽度

                                                  高度 = 左上角View高度 - 属性页的Tab项高度

    调用MoveWindow函数进行设置即可。

  • 相关阅读:
    xgboost保险赔偿预测
    XGBoost对波士顿房价进行预测
    XGBoost 重要参数(调参使用)
    xgboost与gdbt的不同和优化
    基于OpenCV制作道路车辆计数应用程序
    卷积神经网络cnn的实现
    记一次bond引起的网络故障
    虚拟化讲座
    ubuntu16安装dhcp server
    frp内网穿透新玩法--结合xshell隧道
  • 原文地址:https://www.cnblogs.com/jack-jia-moonew/p/4278844.html
Copyright © 2011-2022 走看看