ListCtrl.txt
上传用户:kcl123420
上传日期:2020-11-27
资源大小:5k
文件大小:11k
源码类别:

组合框控件

开发平台:

Visual C++

  1. VC中使用ListCtrl经验总结
  2.   评论:0 条   查看:542 次   chuanzhu 发表于 2007-06-01 15:34 
  3.   ListCtrl在工作中,常常用到,也常常看到大家发帖问怎么用这个控件,
  4. 故总结了一下自己的使用经验,以供参考使用。
  5.   先注明一下,这里,我们用m_listctrl来表示一个CListCtrl的类对象,
  6. 然后这里我们的ListCtrl都是report形式,至于其他的如什么大图标,小图标
  7. 的暂时不讲,毕竟report是大众话的使用。其次,我们这里用条款一,条款二
  8. 来描述第一点,第二点,这个是参照《Effective C++》的叫法,俺觉得这么
  9. 叫比较COOL :)
  10.  条款一:设置ListCtrl的风格
  11.  在CSDN上常常看到有人问怎么设置风格的,他们ListCtrl的样子是一个列表
  12. ,有横条和竖条分界线,然后选中一行,要整一行都选中,而不是只有某一列
  13. 被选中,等等,这里给一个比较全面的设置方法。
  14. //获得原有风格
  15.  DWORD dwStyle = ::GetWindowLong(m_listctrl.m_hWnd, GWL_STYLE); 
  16.  dwStyle &= ~(LVS_TYPEMASK);
  17.  dwStyle &= ~(LVS_EDITLABELS);
  18.  //设置新风格
  19.     SetWindowLong(m_listctrl.m_hWnd, GWL_STYLE, 
  20. dwStyle,|LVS_REPORT|LVS_NOLABELWRAP|LVS_SHOWSELALWAYS);
  21.  
  22.  //设置扩展风格
  23.  DWORD styles = 
  24. LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_CHECKBOXES;
  25.  ListView_SetExtendedListViewStyleEx(m_listctrl.m_hWnd, styles, 
  26. styles );
  27. 其中LVS_EX_FULLROWSELECT 就是前面说得整行选中
  28. LVS_EX_GRIDLINES 网格线(只适用与report风格的listctrl)
  29. LVS_EX_CHECKBOXES 前面加个checkbox
  30. pListCtrl->SetExtendedStyle(  m_listctrl.GetExtendedStyle()
  31. |LVS_EX_SUBITEMIMAGES);
  32. 这也是一个很重要的属性,这样的话,可以在列表中加ICON,记得windows的
  33. 任务管理器吗,你想做得那样,这个属性也要加哦,这个我以后会讲的~
  34. 条款二:加入列头
  35.   这是一个比较实质的东西,给列表框分列,然后加上列头
  36.   代码说话,来了
  37.   TCHAR rgtsz[2][10] = {_T("列头1"), _T("列头2")};
  38.  
  39.  LV_COLUMN lvcolumn;
  40.  CRect rect;
  41.  m_listctrl.GetWindowRect(&rect);
  42.  for(int i=0;i<2;i++)
  43.  {
  44.    lvcolumn.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT 
  45.               | LVCF_WIDTH | LVCF_ORDER;
  46.    lvcolumn.fmt = LVCFMT_LEFT;
  47.    lvcolumn.pszText = rgtsz[i];
  48.    lvcolumn.iSubItem = i;
  49.    lvcolumn.iOrder = i;
  50.    if(i==0)
  51.    {
  52.       lvcolumn.cx = rect.Width()*3/5 ; 
  53.    }
  54.    else
  55.      lvcolumn.cx = rect.Width()*2/5;
  56.       
  57.    m_listctrl.InsertColumn(i, &lvcolumn);
  58.   }
  59. 这是插入两列的做法,你要插入20列??随便你,依样画葫芦~~
  60.  lvcolumn.mask 中那个mask可以有各种属性,具体去看msdn吧,
  61.  
  62. 条款三:把记录,插入列表框中
  63.  int nIndex = m_listctrl.GetItemCount();
  64.   LV_ITEM   lvitemAdd = {0};
  65.   lvitemAdd.mask = LVIF_TEXT;
  66.   lvitemAdd.iItem = nIndex ;
  67.   lvitemAdd.iSubItem = 0;
  68.   lvitemAdd.pszText =_T("毛毛1");;
  69.   
  70.   if (m_listctrl.InsertItem(&lvitemAdd) != -1)
  71.   { 
  72.    LV_ITEM lvitem = {0};
  73.    lvitem.mask = LVIF_TEXT;
  74.    lvitem.iItem = nIndex ;
  75.    lvitem.iSubItem = 1;
  76.  
  77.    lvitem.pszText =_T("毛毛2");
  78.    m_listctrl.SetItem(&lvitem);
  79.    
  80.   }
  81. nIndex 是当前的行数,然后把新的一行,插在最下面,
  82.  
  83. 条款四:给列表中插入图标
  84.  在report格式中,也能插入图标
  85.  继续代码说话
  86.  m_image是个CImageList对象
  87.  m_image.Create(16,16, TRUE|ILC_COLOR24, 3, 1);
  88.  m_listctrl.SetImageList(&m_image,LVSIL_SMALL);
  89. 然后调用CImageList的成员函数int CImageList::Add( HICON hIcon );
  90. 把ICON插入到imagelist,
  91.  然后在插入记录的时候
  92.  lvitemAdd.mask = LVIF_TEXT; -》 lvitemAdd.mask = 
  93. LVIF_TEXT|LVIF_IMAGE
  94.  然后添加一个lvitemAdd.iImage = n;
  95.  这个n是imagelist中的序号,表示是具体的哪一个图标,list么,呵呵
  96.  
  97. 条款五: 插入记录时使用额外的信息,lParam 的使用
  98.    有时候,你想对于某一行,加入一些额外的信息,那么就可以使用这个
  99. lParam 
  100.   msdn是这么描述的Specifies the 32-bit value of the item
  101.   我上次是为了在某一行加入一个信息,窗口句柄,然后是这么加的,
  102.   int nIndex = m_listctrl.GetItemCount();
  103.   LV_ITEM   lvitemAdd = {0};
  104.   lvitemAdd.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
  105.   lvitemAdd.iItem = nIndex ;
  106.   lvitemAdd.iSubItem = 0;
  107.   lvitemAdd.pszText =_T("毛毛1");;
  108.   lvitemAdd.iImage = n;
  109.   lvitemAdd.lParam = (LPARAM)hwnd;(某个窗口的窗口句柄)
  110.   if (m_listctrl.InsertItem(&lvitemAdd) != -1)
  111.   { 
  112.    LV_ITEM lvitem = {0};
  113.    lvitem.mask = LVIF_TEXT;
  114.    lvitem.iItem = nIndex ;
  115.    lvitem.iSubItem = 1;
  116.  
  117.    lvitem.pszText =_T("毛毛2");
  118.    m_listctrl.SetItem(&lvitem);
  119.    
  120.   }
  121. ok,这是一个比较全的例子的,又插ICON,又使用PARAM的
  122. 条款六 : 点击列表框,获取选中行信息
  123.   响应NM_CLICK消息,如果你有MSDN,可以看到,有专门关于listview的
  124. NM_CLICK的介绍
  125. void CMyDlg::OnItemClick(NMHDR* pNMHDR, LRESULT* pResult) 
  126. {
  127.  // TODO: Add your control notification handler code here
  128.  int nItem = -1;
  129.  
  130.  LPNMITEMACTIVATE lpNMItemActivate = (LPNMITEMACTIVATE)pNMHDR;
  131.  if(lpNMItemActivate != NULL)
  132.  {
  133.      nItem = lpNMItemActivate->iItem;
  134.  }
  135. 现在nItem就是点击选中那行的index了,有了index,获取那行的信息还难吗
  136. 懒汉说:难,因为你还没讲,晕,那就继续说
  137.  
  138. 条款七: 根据行的index,获取该行的信息
  139.  直接上代码吧
  140.  LV_ITEM lvitem = {0};
  141.    lvitem.iItem = nIndex;
  142.    lvitem.iSubItem = 0;
  143.    lvitem.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
  144. m_listctrl.GetItem(&lvitem)
  145. 这样,就把nindex,第一列的信息取出来了,包括刚才我们加入的ICON,和那个
  146. 额外信息(窗口句柄),
  147. 比如我要获取窗口句柄,就可以hwnd = (HWND)lvitem.lParam; 
  148. mask 用来指明你想获取那些信息
  149. 具体可以查msdn中LVITEM Structure的定义和CListCtrl::GetItem
  150.  
  151. 条款八:用程序选中某一行,使之选中
  152. 选中之 
  153. m_listctrl.SetItemState
  154. (nIndex,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);
  155. 不选中,取消选中之
  156. m_listctrl.SetItemState(nIndex,0,LVIS_SELECTED|LVIS_FOCUSED);
  157.  
  158. 条款九:获取当前所有选中的行(多选)
  159.   这个,俺就比较懒了,抄msdn的代码吧,反正很简单
  160.   
  161. Example
  162. // CListCtrl* pListCtrl = (CListCtrl*) GetDlgItem
  163. (IDC_YOURLISTCONTROL);
  164. ASSERT(pListCtrl != NULL);
  165. POSITION pos = pList->GetFirstSelectedItemPosition();
  166. if (pos == NULL)
  167.    TRACE0("No items were selected!n");
  168. else
  169. {
  170.   while (pos)
  171.   {
  172.      int nItem = pList->GetNextSelectedItem(pos);
  173.      TRACE1("Item %d was selected!n", nItem);
  174.      // you could do your own processing on nItem here
  175.   }
  176. }
  177.  
  178. 条款十:删除条款九中选中的行
  179.   这个相对前面九个条款是比较麻烦的,因为如果你要删除多行的话。往往要出错
  180.   比如,我现在要删除第0行和第1行(列表的行序列是从0开始的)
  181.   那么好啊。我来删了
  182.   m_listctrl.DeleteItem(0)
  183.         m_listctrl.DeleteItem(1)
  184.   恭喜你,错了,我好开心啊 :)
  185.   因为你删除第0行以后,下面的行会往上移,那么原来的第1行就变成了第0行,那么你再 m_listctrl.DeleteItem(1),那么删除的是原来的第2行,真麻烦,
  186.    所以,只有从下往上删,才是安全的,先删的,不会影响后面的操作,
  187.     m_listctrl.DeleteItem(1)
  188.         m_listctrl.DeleteItem(0)
  189.   但有时候,我们也不知道要删除哪些行,只知道要删除选中的那些行,像条款九中的那些
  190.    如果我们还是用
  191.    
  192. POSITION pos = m_listctrl.GetFirstSelectedItemPosition();
  193. if (pos == NULL)
  194.    TRACE0("No items were selected!n");
  195. else
  196. {
  197.   while (pos)
  198.   {
  199.      int nItem = m_listctrl.GetNextSelectedItem(pos);
  200.     
  201.  m_listctrl.DeleteItem(nItem );
  202.   }
  203. }
  204. 你就等着收尸吧
  205. 这时候我们就要B4微软了,为虾米木有GetLastselectedItemPosition 和GetPrevSelectedItem
  206. 多写一对成员函数会死啊 :(
  207.  没办法,办法自己想,这里有个笨办法
  208.  POSITION  sSelPos = NULL;
  209.  while(sSelPos = m_listctrl.GetFirstSelectedItemPosition())
  210.  {
  211.         int nSelItem = -1;
  212.          nSelItem = m_listctrl.GetNextSelectedItem(sSelPos);
  213.          if(nSelItem >= 0 && nSelItem<m_listctrl.GetItemCount())
  214.        {
  215.              好了,这个nSelItem 就是我们要的DD
  216.       }
  217. }
  218. GetNextSelectedItem这个函数,看msdn的用法,其实是返回第一个的index,然后走到下一个选中的行去,所以这么做也是安全的,在实际中,俺也是这么做的,测试也通过,没问题的
  219. 当然,还有个办法,先通过GetFirstSelectedItemPosition和GetNextSelectedItem
  220. 来获取所有的选中行的index,然后把这些index放到一个数组里,然后再从下往上删
  221. 唉真麻烦啊,还要不定数组,不说用new在堆上开吧,那么一个vector总是要的吧,麻烦啊
  222. 所以我暂时是用上述的办法来删除,也供大家参考,希望能找到更好的办法
  223.  
  224. 1。先来介绍REPORT类型的CListCtrl:
  225. 首先使用下面的语句设置CListCtrl的style:
  226.  DWORD SetExtendedStyle( DWORD dwNewStyle );
  227. 其中
  228.  LVS_EX_CHECKBOXES 表示添加CheckBox
  229.  LVS_EX_FULLROWSELECT 表示选择整行
  230.  LVS_EX_GRIDLINES 表示添加表格线
  231. 如果设置了LVS_EX_CHECKBOXES属性,则可以用
  232.  BOOL GetCheck( int nItem ) const;
  233. 来得到某一行是否Checked。
  234. 可以先用下面的语句来删除以前的东西:
  235.  for(int k=2;k>=0;k--) //注意要从后往前删,否则出错
  236.   m_ListCtrl.DeleteColumn(k);
  237.  m_ListCtrl.DeleteAllItems();
  238. 用下面的语句新建列:
  239.  m_ListCtrl.InsertColumn(0,_T("文件名"),LVCFMT_IMAGE|LVCFMT_LEFT);
  240.  m_ListCtrl.InsertColumn(1,_T("仪器类别"));
  241.  m_ListCtrl.InsertColumn(2,_T("项目类别"));
  242.  
  243. 其中LVCFMT_IMAGE表示可以在第一列加入图标。如果不要图标可以删去。
  244. 然后设置列宽:
  245.  for(j=0;j<3;j++)
  246.   m_ListCtrl.SetColumnWidth(j ,100);
  247.  
  248. 以下为列表加入图标,如果不需要图标,可以跳过这一步。注意只在第一次加入,如果多次加入会出错!
  249. 先在头文件中加入声明:
  250.  CImageList m_ImageList;
  251. 这是必要的,如果在cpp的某个函数中加入由于生命期结束,CImageList自动释放,则效果是列表中看不到图标,只看到一个白方块。
  252. 下面生成CImageList,并将其绑定到CListCtrl中,这是CImageList中还没有图标,只是一个容器:
  253.  static int flag=2;
  254.  if(flag==2){//只调用一次SetImageList,否则出错
  255.   m_ImageList.Create(128, 128, ILC_COLORDDB|ILC_MASK, 20, 1); 
  256.   m_ListCtrl.SetImageList(&m_ImageList,LVSIL_SMALL);
  257.  }
  258.  flag=(flag+1)%2;
  259. 如果CListCtrl已经用过,曾经加过图标进去,这时就要删除上次放进m_ImageList中的Image
  260.  for(int kk=0;kk<m_ImageList.GetImageCount();kk++)
  261.   m_ImageList.Remove(k);
  262.  
  263. 下面介绍如何向CListCtrl里面加入行,并同时为每一行动态加入图标:
  264. 假设m_listRowCount为要加入的行数。
  265.  CBitmap* bitmap;
  266.  bitmap=new CBitmap[m_list1rowCount];
  267.  HBITMAP hbitmap; 
  268.  
  269.  for(int i = 0; i < m_listRowCount; i++)
  270.  {
  271.   //为每一行插入相应的缩略图
  272.   CFile f;
  273.   CFileException e;  
  274.   if( !f.Open(m_FileName, CFile::modeRead, &e )){ //m_FileName为bmp文件名,由你来定
  275.    hbitmap = (HBITMAP)LoadImage(NULL,path+"blank.bmp",IMAGE_BITMAP,0,0,
  276.     LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE);
  277.   }else{
  278.    f.Close();
  279.    hbitmap = (HBITMAP)LoadImage(NULL,bmpFile,IMAGE_BITMAP,0,0,
  280.     LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE);
  281.   }
  282.   bitmap[i].Attach(hbitmap);
  283.   m_ImageList.Add(&bitmap[i], RGB(0, 128, 128));
  284.   
  285.   //插入行
  286.   m_ListCtrl.InsertItem(i,m_FileName,i);
  287.   m_ListCtrl.SetItemText(i,1,type);
  288.   m_ListCtrl.SetItemText(i,2,m_Path);
  289.  }
  290.   
  291.  //记得删除已经没用的临时文件
  292.  if(m_list1rowCount!=0)
  293.   delete[] bitmap;
  294. 2。如果是ICON类型的CListCtrl,则要做一点点改动:
  295. 把绑定图标集的代码由
  296.  SetImageList(&m_ImageList,LVSIL_SMALL);
  297. 改为
  298.  SetImageList(&m_ImageList,LVSIL_NORMAL);
  299. 插入行时只用
  300.  InsertItem(i,mainSet.m_FileName,i);
  301. 不用
  302.  SetItemText(i,1,type);
  303. 之类的代码。