您好,欢迎访问一九零五行业门户网

MFC总结之CListCtrl用法及技巧(二)

本篇重点介绍: 禁 止拖动表头、让第一列居中显示、设置行高与字体、虚拟列表技术、点击表头时进行归类、向上与向下移动、动态调整大小问题、避免闪烁问题 。 6 、禁止拖动表头 重载 onnotify 消息响应函数,屏蔽两个消息通知码: hdn_begintrackw 和 hdn_div
本篇重点介绍:禁止拖动表头、让第一列居中显示、设置行高与字体、虚拟列表技术、点击表头时进行归类、向上与向下移动、动态调整大小问题、避免闪烁问题。
6、禁止拖动表头       重载onnotify消息响应函数,屏蔽两个消息通知码:hdn_begintrackw 和hdn_dividerdblclickw。示例如下:
[cpp] view plaincopy
bool cxxxx::onnotify(wparam wparam, lparam lparam, lresult* presult)  {      // todo: add your specialized code here and/or call the base class      //屏蔽两个消息通知码,使得禁止拖动list表头      nmheader* pnmheader = (nmheader*)lparam;      if(((pnmheader->hdr.code == hdn_begintrackw) |                (pnmheader->hdr.code == hdn_dividerdblclickw)))      {          *presult = true;          return true;      }        return cdialog::onnotify(wparam, lparam, presult);  }   7、让第一列居中显示        在插入列时,我们可以通过参数nformat来设置文本居中显示,但是这种设置对于第一列是没有作用的。这时我们可以考虑将我们的内容从第二列开始插入(设置为居中显示)。先插入第一列,然后删除第一列,这样原先的第二列就充当了第一列。
8、设置行高和字体        设置clistctrl的行高没有函数接口,可以通过自绘来实现,但是比较麻烦。有一个比较简单的方法是通过使用一个空白的图像将行撑起来,使其高度发生变化。示例如下:
[cpp] view plaincopy
cimagelist m_image;  m_image.create(1,24,ilc_color32,1,0);  m_listinfo.setimagelist(&m_image, lvsil_small);          对于字体的设置,我们可以使用setfont函数来实现。以修改clistview的字体为例,在oninitialupdate函数中插入列之前调用setfontself函数(该函数自定义,如下示例所示)。首先创建一个字体,然后调用setfont进行设置。需要注意的是,在退出时需要delete 掉创建的字体,避免内存泄露。
[cpp] view plaincopy
//设置字体和大小  void cmylistview::setfontself(int nheight, lpctstr lpszfacename)  {      //先删除原有字体      if(m_font != null)          delete m_font;      m_font = new cfont;      //创建字体      m_font->createfont(          nheight,                   // nheight          0,                         // nwidth          0,                         // nescapement          0,                         // norientation          fw_normal,                 // nweight          false,                     // bitalic          false,                     // bunderline          0,                         // cstrikeout          ansi_charset,              // ncharset          out_default_precis,        // noutprecision          clip_default_precis,       // nclipprecision          default_quality,           // nquality          default_pitch | ff_swiss,  // npitchandfamily          lpszfacename);             // lpszfacename        //设置字体      clistctrl &thectrl = getlistctrl();     //获取控制权,引用变量      thectrl.setfont(m_font, true);  }
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
void cdataanalysis::onlvngetdispinfoanalysislist(nmhdr *pnmhdr, lresult *presult)  {      nmlvdispinfo *pdispinfo = reinterpret_cast(pnmhdr);      // todo: add your control notification handler code here      lv_item* pitem= &(pdispinfo)->item;      int iitemindex= pitem->iitem;      size_t converted = 0;      wchar_t wstr[30];            //unicode字符串      if (pitem->mask & lvif_text) //字符串缓冲区有效      {          switch(pitem->isubitem)          {          case 0: //填充数据项的名字,xxxxx表示要填充的字符              mbstowcs_s(&converted, wstr, 30, xxxxxx, _truncate);              lstrcpy(pitem->psztext,wstr);              break;          case 1: //填充子项1              mbstowcs_s(&converted, wstr, 30, xxxxxx, _truncate);              lstrcpy(pitem->psztext,wstr);              break;          case 2: //填充子项2              mbstowcs_s(&converted, wstr, 30, xxxxxx, _truncate);              lstrcpy(pitem->psztext,wstr);              break;          case 3: //填充子项3              lstrcpy(pitem->psztext,xxxxxx);              break;          }      }        *presult = 0;  }
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
//初始化列表视图控件  bool cdataanalysis::initlistctl()  {      //其他处理,包括设置风格,插入列等等      //插入行      for(int i=0; i    {          //要将char*转换为wchar_t*          mbstowcs_s(&converted, wstr, 30, m_analysis[i].date, _truncate);          m_listanalysis.insertitem(i, wstr);                             //日期          mbstowcs_s(&converted, wstr, 30, m_analysis[i].time, _truncate);          m_listanalysis.setitemtext(i, 1, wstr);                         //时间          mbstowcs_s(&converted, wstr, 30, m_analysis[i].id, _truncate);          m_listanalysis.setitemtext(i, 2, wstr);                         //id          m_listanalysis.setitemtext(i, 3, m_analysis[i].lpszevent);      //事件            //设置回调函数的参数          m_listanalysis.setitemdata(i, (lparam)(m_analysis+i));      }        return true;  }  void cdataanalysis::onhdnitemclickanalysislist(nmhdr *pnmhdr, lresult *presult)  {      lpnmheader phdr = reinterpret_cast(pnmhdr);      // todo: add your control notification handler code here        //设置回调函数的参数和入口地址      m_listanalysis.sortitems(sortfunc, phdr->iitem);        *presult = 0;  }  //排序的回调函数  int callback sortfunc(lparam lparam1, lparam lparam2, lparam lparamsort)  {      int result;     //返回值        //两行的参数,用于比较      analysisformat* panalysis1 = (analysisformat*)lparam1;      analysisformat* panalysis2 = (analysisformat*)lparam2;        //排序      switch(lparamsort)      {      case 0:     //日期          result = strcmp(panalysis1->date, panalysis2->date);          break;      case 1:     //时间          result = strcmp(panalysis1->time, panalysis2->time);          break;      case 2:     //id          result = strcmp(panalysis1->id, panalysis2->id);          break;      case 3:     //事件          result = wcscmp(panalysis1->lpszevent, panalysis2->lpszevent);          break;      default:          break;      }        return result;  }  
 11、向上与向下移动        有时需要向上或向下移动表项内容,这里给出向上移动的方法,向下移动的方法类似。
① 利用第2节所述的内容获取行号nitem,判断行号是否为行首,如果不是行首则进入②;
② 获取第nitem行的所有子项内容;
③ 删除第nitem行,并在nitem-1的位置重新插入原先的第nitem行的内容;
④ 使nitem-1的位置高亮显示
示例如下:
[cpp] view plaincopy
/*************************上移子项**************************/  void cstudytestdlg::onpageup()   {      if (nitem == 0)      {          messagebox(该子项已经位于第一行!);          return;      }        // 提取内容      cstring temp[4];      int i;      for(i=0;i        temp[i] = m_listctrl.getitemtext(nitem, i);        // 删除      m_listctrl.deleteitem(nitem);        // 在nitem-1位置处插入      for (i=0; i        m_listctrl.setitemtext(nitem-1,i,temp[i]);        //高亮显示      uint flag = lvis_selected|lvis_focused;      m_listctrl.setitemstate(--nitem, flag, flag);  }    /*************************下移子项**************************/  void cstudytestdlg::onpagedown()   {      if (nitem == m_listctrl.getitemcount()-1)      {          messagebox(该子项已经位于最后一行!);          return;      }        // 提取内容      cstring temp[4];      int i;      for (i=0; i        temp[i] = m_listctrl.getitemtext(nitem, i);        // 删除      m_listctrl.deleteitem(nitem);        // 在nitem+1位置处插入      for (i=0; i        m_listctrl.setitemtext(nitem+1, i,temp[i]);        //高亮显示      uint flag = lvis_selected|lvis_focused;      m_listctrl.setitemstate(++nitem, flag, flag);  }  
 12、避免闪烁问题           这个问题在我的前面一篇博文有提到。  http://blog.csdn.net/zwgdft/article/details/7394318
 13、动态调整大小        有时由于不确定软件运行时的电脑屏幕大小,需要根据屏幕大小动态设置clistctrl控件的大小。动态大小的设置时,需要注意不要将高度和宽度设置的超过区域限制,否则就没有滚动条了,导致部分内容无法查看。以我遇到的一个例子来说,其情况见第12节提到的那篇博文所述:将view划分为三个窗格,在左上角view上有个cpropertysheet,其上有几个cpropertypage,每个属性页上有个clistctrl,供用户查看信息。那么这时需要设置的clistctrl的大小即为:
                                              宽度 = 左上角view宽度
                                              高度 = 左上角view高度 - 属性页的tab项高度
调用movewindow函数进行设置即可。
------------------全文完--------------------
其它类似信息

推荐信息