本篇重点介绍: 禁 止拖动表头、让第一列居中显示、设置行高与字体、虚拟列表技术、点击表头时进行归类、向上与向下移动、动态调整大小问题、避免闪烁问题 。 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函数进行设置即可。
------------------全文完--------------------