MFC CListCtrl 使用介绍

列表控件可以看作是功能增强的ListBox,它提供了四种风格,而且可以同时显示一列的多中属性值。MFC中使用CListCtrl类来封装列表控件的各种操作。通过调用
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );创建一个窗口,dwStyle中可以使用以下一些列表控件的专用风格:

  • LVS_ICON LVS_SMALLICON LVS_LIST LVS_REPORT 这四种风格决定控件的外观,同时只可以选择其中一种,分别对应:大图标显示,小图标显示,列表显示,详细报表显示
  • LVS_EDITLABELS 结点的显示字符可以被编辑,对于报表风格来讲可编辑的只为第一列。
  • LVS_SHOWSELALWAYS 在失去焦点时也显示当前选中的结点
  • LVS_SINGLESEL 同时只能选中列表中一项

首先你需要设置列表控件所使用的ImageList,如果你使用大图标显示风格,你就需要以如下形式调用:
CImageList* SetImageList( CImageList* pImageList, LVSIL_NORMAL);
如果使用其它三种风格显示而不想显示图标你可以不进行任何设置,否则需要以如下形式调用:
CImageList* SetImageList( CImageList* pImageList, LVSIL_SMALL);

通过调用int InsertItem( int nItem, LPCTSTR lpszItem );可以在列表控件中nItem指明位置插入一项,lpszItem为显示字符。除LVS_REPORT风格外其他三种风格都只需要直接调用 InsertItem就可以了,但如果使用报表风格就必须先设置列表控件中的列信息。

通过调用int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat , int nWidth, int nSubItem);可以插入列。iCol为列的位置,从零开始,lpszColumnHeading为显示的列名,nFormat为显示对齐方式, nWidth为显示宽度,nSubItem为分配给该列的列索引。

在有多列的列表控件中就需要为每一项指明其在每一列中的显示字符,通过调用
BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );可以设置每列的显示字符。nItem为设置的项的位置,nSubItem为列位置,lpszText为显示字符。下面的代码演示了如何设置多列并插入数据:

m_list.SetImageList(&m_listSmall,LVSIL_SMALL);//设置ImageList
m_list.InsertColumn(0,"Col 1",LVCFMT_LEFT,300,0);//设置列
m_list.InsertColumn(1,"Col 2",LVCFMT_LEFT,300,1);
m_list.InsertColumn(2,"Col 3",LVCFMT_LEFT,300,2);

m_list.InsertItem(0,"Item 1_1");//插入行
m_list.SetItemText(0,1,"Item 1_2");//设置该行的不同列的显示字符
m_list.SetItemText(0,2,"Item 1_3");

此外CListCtrl还提供了一些函数用于得到/修改控件的状态。
COLORREF GetTextColor( )/BOOL SetTextColor( COLORREF cr );用于得到/设置显示的字符颜色。
COLORREF GetTextBkColor( )/BOOL SetTextBkColor( COLORREF cr );用于得到/设置显示的背景颜色。
void SetItemCount( int iCount );用于得到添加进列表中项的数量。
BOOL DeleteItem(int nItem);用于删除某一项,BOOL DeleteAllItems( );将删除所有项。
BOOL SetBkImage(HBITMAP hbm, BOOL fTile , int xOffsetPercent, int yOffsetPercent);用于设置背景位图。
CString GetItemText( int nItem, int nSubItem );用于得到某项的显示字符。

列表控件的消息映射同样使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn ),wNotifyCode为通知代码,id为产生该消息的窗口ID,memberFxn为处理函数,函数的原型如同void OnXXXList(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR为一数据结构,在具体使用时需要转换成其他类型的结构。对于列表控件可能取值和对应的数据结构为:

  • LVN_BEGINLABELEDIT 在开始某项编辑字符时发送,所用结构:NMLVDISPINFO
  • LVN_ENDLABELEDIT 在结束某项编辑字符时发送,所用结构:NMLVDISPINFO
  • LVN_GETDISPINFO 在需要得到某项信息时发送,(如得到某项的显示字符)所用结构:NMLVDISPINFO

关于ON_NOTIFY有很多内容,将在以后的内容中进行详细讲解。

关于动态提供结点所显示的字符:首先你在项时需要指明lpszItem参数为: LPSTR_TEXTCALLBACK。在控件显示该结点时会通过发送TVN_GETDISPINFO来取得所需要的字符,在处理该消息时先将参数 pNMHDR转换为LPNMLVDISPINFO,然后填充其中item.pszText。通过item中的iItem,iSubItem可以知道当前显示的为那一项。下面的代码演示了这种方法:

char szOut[8][3]={"No.1","No.2","No.3"};

//添加结点
m_list.InsertItem(LPSTR_TEXTCALLBACK,...)
m_list.InsertItem(LPSTR_TEXTCALLBACK,...)
//处理消息
void CParentWnd::OnGetDispInfoList(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR;
pLVDI->item.pszText=szOut[pTVDI->item.iItem];//通过iItem得到需要显示的字符在数组中的位置
*pResult = 0;
}

关于编辑某项的显示字符:(在报表风格中只对第一列有效)首先需要设置列表控件的 LVS_EDITLABELS风格,在开始编辑时该控件将会发送LVN_BEGINLABELEDIT,你可以通过在处理函数中返回TRUE来取消接下来的编辑,在编辑完成后会发送LVN_ENDLABELEDIT,在处理该消息时需要将参数pNMHDR转换为LPNMLVDISPINFO,然后通过其中的item.pszText得到编辑后的字符,并重置显示字符。如果编辑在中途中取消该变量为NULL。下面的代码说明如何处理这些消息:

//处理消息 LVN_BEGINLABELEDIT
void CParentWnd::OnBeginEditList(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR;
if(pLVDI->item.iItem==0);//判断是否取消该操作
*pResult = 1;
else
*pResult = 0;
}
//处理消息 LVN_BEGINLABELEDIT
void CParentWnd::OnBeginEditList(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pLVDI = (LV_DISPINFO*)pNMHDR;
if(pLVDI->item.pszText==NULL);//判断是否已经取消取消编辑
m_list.SetItemText(pLVDI->item.iItem,0,pLVDI->pszText);//重置显示字符
*pResult = 0;
}
上面讲述的方法所进行的消息映射必须在父窗口中进行(同样WM_NOTIFY的所有消息都需要在父窗口中处理)。

如何得到当前选中项位置:在列表控件中没有一个类似于ListBox中GetCurSel()的函数,但是可以通过调用GetNextItem( -1, LVNI_ALL | LVNI_SELECTED);得到选中项位置。

下面是一些例子

作者:lixiaosan
时间:04/06/2006

以下未经说明,listctrl默认view 风格为report

相关类及处理函数

MFC:CListCtrl类

SDK:以 “ListView_”开头的一些宏。如 ListView_InsertColumn


1. CListCtrl 风格

      LVS_ICON: 为每个item显示大图标
      LVS_SMALLICON: 为每个item显示小图标
      LVS_LIST: 显示一列带有小图标的item
      LVS_REPORT: 显示item详细资料

      直观的理解:windows资源管理器,“查看”标签下的“大图标,小图标,列表,详细资料”


2. 设置listctrl 风格及扩展风格

      LONG lStyle;
      lStyle. = GetWindowLong(m_list.m_hWnd, GWL_STYLE);//获取当前窗口style
      lStyle. &= ~LVS_TYPEMASK; //清除显示方式位
      lStyle.|= LVS_REPORT; //设置style
      SetWindowLong(m_list.m_hWnd, GWL_STYLE, lStyle);//设置style
      DWORD dwStyle. = m_list.GetExtendedStyle();
      dwStyle.|= LVS_EX_FULLROWSELECT;//选中某行使整行高亮(只适用与report风格的listctrl)
      dwStyle.|= LVS_EX_GRIDLINES;//网格线(只适用与report风格的listctrl)
      dwStyle.|= LVS_EX_CHECKBOXES;//item前生成checkbox控件
      m_list.SetExtendedStyle(dwStyle); //设置扩展风格
      注:listview的style请查阅msdn
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceshellui5/html/wce50lrflistviewstyles.asp


3. 插入数据

      m_list.InsertColumn( 0, "ID", LVCFMT_LEFT, 40 );//插入列
      m_list.InsertColumn( 1, "NAME", LVCFMT_LEFT, 50 );
      int nRow = m_list.InsertItem(0, “11”);//插入行
      m_list.SetItemText(nRow, 1, “jacky”);//设置数据


4. 一直选中item

选中style中的Show selection always,或者在上面第2点中设置LVS_SHOWSELALWAYS


5. 选中和取消选中一行

    int nIndex = 0;
    //选中
    m_list.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
    //取消选中
    m_list.SetItemState(nIndex, 0, LVIS_SELECTED|LVIS_FOCUSED);


6. 得到listctrl中所有行的checkbox的状态

      m_list.SetExtendedStyle(LVS_EX_CHECKBOXES);
      CString str;
      for(int i=0; i      {
           if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED || m_list.GetCheck(i))
           {
                str.Format(_T("第%d行的checkbox为选中状态"), i);
                AfxMessageBox(str);
           }
      }


7. 得到listctrl中所有选中行的序号
 

      方法一:
      CString str;
      for(int i=0; i      {
           if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED )
           {
                str.Format(_T("选中了第%d行"), i);
                AfxMessageBox(str);
           }
      }

      方法二:
      POSITION pos = m_list.GetFirstSelectedItemPosition();
      if (pos == NULL)
           TRACE0("No items were selected!\n");
      else
      {
           while (pos)
           {
                int nItem = m_list.GetNextSelectedItem(pos);
                TRACE1("Item %d was selected!\n", nItem);
                // you could do your own processing on nItem here
           }
      }


8. 得到item的信息

      TCHAR szBuf[1024];
      LVITEM lvi;
      lvi.iItem = nItemIndex;
      lvi.iSubItem = 0;
      lvi.mask = LVIF_TEXT;
      lvi.pszText = szBuf;
      lvi.cchTextMax = 1024;
      m_list.GetItem(&lvi);

      关于得到设置item的状态,还可以参考msdn文章
      Q173242: Use Masks to Set/Get Item States in CListCtrl
http://support.microsoft.com/kb/173242/en-us


9. 得到listctrl的所有列的header字符串内容

      LVCOLUMN lvcol;
      char str[256];
      int   nColNum;
      CString strColumnName[4];//假如有4列

      nColNum = 0;
      lvcol.mask = LVCF_TEXT;
      lvcol.pszText = str;
      lvcol.cchTextMax = 256;
      while(m_list.GetColumn(nColNum, &lvcol))
      {
           strColumnName[nColNum] = lvcol.pszText;
           nColNum++;
      }


10. 使listctrl中一项可见,即滚动滚动条

     m_list.EnsureVisible(i, FALSE);


11. 得到listctrl列数

     int nHeadNum = m_list.GetHeaderCtrl()->GetItemCount();


12. 删除所有列

    方法一:
         while ( m_list.DeleteColumn (0))
       因为你删除了第一列后,后面的列会依次向上移动。

    方法二:
      int nColumns = 4;
      for (int i=nColumns-1; i>=0; i--)
          m_list.DeleteColumn (i);


13. 得到单击的listctrl的行列号

      添加listctrl控件的NM_CLICK消息相应函数
      void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           // 方法一:
           /*
           DWORD dwPos = GetMessagePos();
           CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
           m_list.ScreenToClient(&point);
           LVHITTESTINFO lvinfo;
           lvinfo.pt = point;
           lvinfo.flags = LVHT_ABOVE;
           int nItem = m_list.SubItemHitTest(&lvinfo);
           if(nItem != -1)
           {
                CString strtemp;
                strtemp.Format("单击的是第%d行第%d列", lvinfo.iItem, lvinfo.iSubItem);
                AfxMessageBox(strtemp);
           }
          */
          // 方法二:
          /*
           NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
           if(pNMListView->iItem != -1)
           {
                CString strtemp;
                strtemp.Format("单击的是第%d行第%d列",
                                pNMListView->iItem, pNMListView->iSubItem);
                AfxMessageBox(strtemp);
           }
          */
           *pResult = 0;
      }


14. 判断是否点击在listctrl的checkbox上

      添加listctrl控件的NM_CLICK消息相应函数
      void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           DWORD dwPos = GetMessagePos();
           CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
           m_list.ScreenToClient(&point);
           LVHITTESTINFO lvinfo;
           lvinfo.pt = point;
           lvinfo.flags = LVHT_ABOVE;
           UINT nFlag;
           int nItem = m_list.HitTest(point, &nFlag);
           //判断是否点在checkbox上
           if(nFlag == LVHT_ONITEMSTATEICON)
           {
                AfxMessageBox("点在listctrl的checkbox上");
           }
           *pResult = 0;
      }


15. 右键点击listctrl的item弹出菜单

      添加listctrl控件的NM_RCLICK消息相应函数
      void CTest6Dlg::OnRclickList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
           if(pNMListView->iItem != -1)
           {
                DWORD dwPos = GetMessagePos();
                CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
                CMenu menu;
                VERIFY( menu.LoadMenu( IDR_MENU1 ) );
                CMenu* popup = menu.GetSubMenu(0);
                ASSERT( popup != NULL );
                popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this );
           }
           *pResult = 0;
}


16. item切换焦点时(包括用键盘和鼠标切换item时),状态的一些变化顺序

      添加listctrl控件的LVN_ITEMCHANGED消息相应函数
      void CTest6Dlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
           // TODO: Add your control notification handler code here
           CString sTemp;
           if((pNMListView->uOldState & LVIS_FOCUSED) == LVIS_FOCUSED &&
            (pNMListView->uNewState & LVIS_FOCUSED) == 0)
           {
                sTemp.Format("%d losted focus",pNMListView->iItem);
           }
           else if((pNMListView->uOldState & LVIS_FOCUSED) == 0 &&
               (pNMListView->uNewState & LVIS_FOCUSED) == LVIS_FOCUSED)
           {
                sTemp.Format("%d got focus",pNMListView->iItem);
           }
           if((pNMListView->uOldState & LVIS_SELECTED) == LVIS_SELECTED &&
            (pNMListView->uNewState & LVIS_SELECTED) == 0)
           {
                sTemp.Format("%d losted selected",pNMListView->iItem);
           }
           else if((pNMListView->uOldState & LVIS_SELECTED) == 0 &&
            (pNMListView->uNewState & LVIS_SELECTED) == LVIS_SELECTED)
           {
                sTemp.Format("%d got selected",pNMListView->iItem);
           }
           *pResult = 0;
      }


17. 得到另一个进程里的listctrl控件的item内容
 

http://www.codeproject.com/threads/int64_memsteal.asp


18. 选中listview中的item

Q131284: How To Select a Listview Item Programmatically
http://support.microsoft.com/kb/131284/en-us


19. 如何在CListView中使用CListCtrl的派生类

http://www.codeguru.com/cpp/controls/listview/introduction/article.php/c919/


20. listctrl的subitem添加图标

      m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES);
      m_list.SetItem(..); //具体参数请参考msdn


21. 在CListCtrl显示文件,并根据文件类型来显示图标

      网上找到的代码,share
      BOOL CTest6Dlg::OnInitDialog()
      {
           CDialog::OnInitDialog();
           HIMAGELIST himlSmall;
           HIMAGELIST himlLarge;
           SHFILEINFO sfi;
           char cSysDir[MAX_PATH];
           CString strBuf;
           memset(cSysDir, 0, MAX_PATH);
           GetWindowsDirectory(cSysDir, MAX_PATH);
           strBuf = cSysDir;
           sprintf(cSysDir, "%s", strBuf.Left(strBuf.Find("\\")+1));
           himlSmall = (HIMAGELIST)SHGetFileInfo ((LPCSTR)cSysDir,
                      0,
                      &sfi,
                      sizeof(SHFILEINFO),
                      SHGFI_SYSICONINDEX | SHGFI_SMALLICON );
           himlLarge = (HIMAGELIST)SHGetFileInfo((LPCSTR)cSysDir,
                      0,
                      &sfi,
                      sizeof(SHFILEINFO),
                      SHGFI_SYSICONINDEX | SHGFI_LARGEICON);
           if (himlSmall && himlLarge)
           {
                ::SendMessage(m_list.m_hWnd, LVM_SETIMAGELIST,
                             (WPARAM)LVSIL_SMALL, (LPARAM)himlSmall);
                ::SendMessage(m_list.m_hWnd, LVM_SETIMAGELIST,
                             (WPARAM)LVSIL_NORMAL, (LPARAM)himlLarge);
           }
           return TRUE; // return TRUE unless you set the focus to a control
      }
      void CTest6Dlg::AddFiles(LPCTSTR lpszFileName, BOOL bAddToDocument)
      {
           int nIcon = GetIconIndex(lpszFileName, FALSE, FALSE);
           CString strSize;
           CFileFind filefind;
           // get file size
           if (filefind.FindFile(lpszFileName))
           {
                filefind.FindNextFile();
                strSize.Format("%d", filefind.GetLength());
           }
           else
                strSize = "0";
           // split path and filename
           CString strFileName = lpszFileName;
           CString strPath;
           int nPos = strFileName.ReverseFind('\\');
           if (nPos != -1)
           {
                strPath = strFileName.Left(nPos);
                strFileName = strFileName.Mid(nPos + 1);
           }
           // insert to list
           int nItem = m_list.GetItemCount();
           m_list.InsertItem(nItem, strFileName, nIcon);
           m_list.SetItemText(nItem, 1, strSize);
           m_list.SetItemText(nItem, 2, strFileName.Right(3));
           m_list.SetItemText(nItem, 3, strPath);
      }
      int CTest6Dlg::GetIconIndex(LPCTSTR lpszPath, BOOL bIsDir, BOOL bSelected)
      {
           SHFILEINFO sfi;
           memset(&sfi, 0, sizeof(sfi));
           if (bIsDir)
           {
            SHGetFileInfo(lpszPath,
                         FILE_ATTRIBUTE_DIRECTORY,
                         &sfi,
                         sizeof(sfi),
                         SHGFI_SMALLICON | SHGFI_SYSICONINDEX |
                         SHGFI_USEFILEATTRIBUTES |(bSelected ? SHGFI_OPENICON : 0));
            return sfi.iIcon;
           }
           else
           {
            SHGetFileInfo (lpszPath,
                         FILE_ATTRIBUTE_NORMAL,
                         &sfi,
                         sizeof(sfi),
                         SHGFI_SMALLICON | SHGFI_SYSICONINDEX |
                         SHGFI_USEFILEATTRIBUTES | (bSelected ? SHGFI_OPENICON : 0));
            return   sfi.iIcon;
           }
           return -1;
      }

转自:http://apps.hi.baidu.com/share/detail/21176843

 

VC CListCtrl的使用完全指南

2008-04-12 14:46

创建图形列表并和CListCtrl关联:
m_image_list.Create(IDB_CALLER2, 16, 10, RGB(192,192, 192));
m_image_list.SetBkColor( GetSysColor( COLOR_WINDOW ) );
m_caller_list.SetImageList( &m_image_list, LVSIL_SMALL);

为报表添加4列:
   char *szColumn[]={"昵称","IP地址","登陆时间","状态"};
   int widths[]={100,98,70,55};
   LV_COLUMN lvc;
   lvc.mask=LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
   lvc.fmt=LVCFMT_LEFT;
   for(int i=0;i<4;i++) {//插入各列
    lvc.pszText=szColumn[i];
    lvc.cx=widths[i];
    lvc.iSubItem=i;
    m_caller_list.InsertColumn(i,&lvc);
   }

为报表添加两项,以附加方式添加:
char* data[4];
data[0]="所有人";
data[1]="0.0.0.0";
data[3]="在线";
data[2]=new char;
CTime now=CTime::GetCurrentTime();
       CString temp = now.Format("%H:%M:%S");
data[2]=temp.GetBuffer(1);
LV_ITEM lvi;
lvi.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
lvi.iSubItem=0;
lvi.pszText=(char *)data[0];
lvi.iImage = 0;
lvi.iItem=0;
m_caller_list.InsertItem(&lvi);
for (int j=0;j<4;j++) m_caller_list.SetItemText(count,j,data[j]);
count++;
lvi.iImage = 1;
lvi.iItem=count;
m_caller_list.InsertItem(&lvi);
data[0]="cherami";
data[1]="127.0.0.1";
for (int n=0;n<4;n++) m_caller_list.SetItemText(count,n,data[n]);
count++;

设置报表的样式
选中一整行:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
绘制表格:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_GRIDLINES);
带复选框:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_CHECKBOXES);
自动切换:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_TRACKSELECT);

选定一行:
设置CListCtrl的Show selection always选项
SetItemState (iIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED)
选中一个或多个项目时,会发送LVN_ITEMCHANGED消息,可以使用
GetSelectedCount()方法得到被选定的项的数目。

点击列头的消息响应:
ON_NOTIFY(HDN_ITEMCLICKW, 0, ResponseFunc)
消息,需要自己添加
或者:
ON_NOTIFY(LVN_COLUMNCLICK, ID_yourCtrl, ResponseFunc)//向导添加
前者后响应,后者先响应

响应函数:
ResponseFunc(NMHDR *pNMHDR, LRESULT *pResult)

双击CListCtrl中的ITEM的消息是及消息函数:
ON_NOTIFY(NM_DBLCLK, ID_yourCtrl, ResponseFunc)

单击ITEM的消息响应:
ON_NOTIFY(NM_CLICK, ID_yourCtrl, ResponseFunc)
ResponseFunc(NMHDR *pNMHDR, LRESULT *pResult)

HDN_ITEMCLICK    就是Header control Notify message for mouse left click on the Header control!
而HDN_ITEMCLICK是当List View中存在一个Header Contrl时,Header Ctrl通知父窗口List View的!

CListCtrl中的Item被选中触发LBN_SELCHANGE(通过WM_COMMAND)消息!

删除CListCtrl中选定的项:
POSITION pos;
int nIndex;

for(; pos= GetFirstSelectedItemPosition();)
{
nIndex = GetNextSelectedItem(pos);
DeleteItem(nIndex);
}

在ListCtrl中进行排序
列表控件(CListCtrl)的顶部有一排按钮,用户可以通过选择不同的列来对记录进行排序。但是 CListCtrl并没有自动排序的功能,我们需要自己添加一个用于排序的回调函数来比较两个数据的大小,此外还需要响应排序按钮被点击的消息。下面讲述一下具体的做法。

CListCtrl提供了用于排序的函数,函数原型为:BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData )。其中第一个参数为全局排序函数的地址,第二个参数为用户数据,你可以根据你的需要传递一个数据或是指针。该函数返回-1代表第一项排应在第二项前面,返回1代表第一项排应在第二项后面,返回0代表两项相等。

用于排序的函数原形为:int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort),其中第三个参数为调用者传递的数据(即调用SortItems时的第二个参数dwData)。第一和第二个参数为用于比较的两项的ItemData,你可以通过DWORD CListCtrl::GetItemData( int nItem )/BOOL CListCtrl::SetItemData( int nItem, DWORD dwData )来对每一项的ItemData进行存取。在添加项时选用特定的CListCtrl::InsertItem也可以设置该值。由于你在排序时只能通过该值来确定项的位置所以你应该比较明确的确定该值的含义。

最后一点,我们需要知道什么时候需要排序,实现这点可以在父窗口中对LVN_COLUMNCLICK消息进行处理来实现。

下面我们看一个例子,这个例子是一个派生类,并支持顺序/倒序两种方式排序。为了简单我对全局数据进行排序,而在实际应用中会有多组需要排序的数据,所以需要通过传递参数的方式来告诉派序函数需要对什么数据进行排序。

//全局数据
struct DEMO_DATA
{
char szName[20];
int iAge;
}strAllData[5]={{"王某",30},{"张某",40},{"武某",32},{"陈某",20},{"李某",36}};

//CListCtrl派生类定义
class CSortList : public CListCtrl
{
// Construction
public:
CSortList();
BOOL m_fAsc;//是否顺序排序
int m_nSortedCol;//当前排序的列
protected:
//{{AFX_MSG(CSortList)
//}}AFX_MSG
...
};

//父窗口中包含该CListCtrl派生类对象
class CSort_in_list_ctrlDlg : public CDialog
{
// Construction
public:
CSort_in_list_ctrlDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
//{{AFX_DATA(CSort_in_list_ctrlDlg)
enum { IDD = IDD_SORT_IN_LIST_CTRL_DIALOG };
CSortList m_listTest;
//}}AFX_DATA
}

//在父窗口中定义LVN_COLUMNCLICK消息映射
BEGIN_MESSAGE_MAP(CSort_in_list_ctrlDlg, CDialog)
//{{AFX_MSG_MAP(CSort_in_list_ctrlDlg)
ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//初始化数据
BOOL CSort_in_list_ctrlDlg::OnInitDialog()
{
CDialog::OnInitDialog();

//初始化ListCtrl中数据列表
m_listTest.InsertColumn(0,"姓名");
m_listTest.InsertColumn(1,"年龄");
m_listTest.SetColumnWidth(0,80);
m_listTest.SetColumnWidth(1,80);
for(int i=0;i<5;i++)
{
   m_listTest.InsertItem(i,strAllData[i].szName);
   char szAge[10];
   sprintf(szAge,"%d",strAllData[i].iAge);
   m_listTest.SetItemText(i,1,szAge);
   //设置每项的ItemData为数组中数据的索引
   //在排序函数中通过该ItemData来确定数据
   m_listTest.SetItemData(i,i);
}
return TRUE; // return TRUE unless you set the focus to a control
}

//处理消息
void CSort_in_list_ctrlDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
//设置排序方式
if( pNMListView->iSubItem == m_listTest.m_nSortedCol )
   m_listTest.m_fAsc = !m_listTest.m_fAsc;
else
{
   m_listTest.m_fAsc = TRUE;
   m_listTest.m_nSortedCol = pNMListView->iSubItem;
}
//调用排序函数
m_listTest.SortItems( ListCompare, (DWORD)&m_listTest );       
*pResult = 0;
}

//排序函数实现
int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
//通过传递的参数来得到CSortList对象指针,从而得到排序方式
CSortList* pV=(CSortList*)lParamSort;
//通过ItemData来确定数据
DEMO_DATA* pInfo1=strAllData+lParam1;
DEMO_DATA* pInfo2=strAllData+lParam2;
CString szComp1,szComp2;
int iCompRes;
switch(pV->m_nSortedCol)
{
case(0):
   //以第一列为根据排序
   szComp1=pInfo1->szName;
   szComp2=pInfo2->szName;
   iCompRes=szComp1.Compare(szComp2);
   break;
case(1):
   //以第二列为根据排序
   if(pInfo1->iAge == pInfo2->iAge)
    iCompRes = 0;
   else
    iCompRes=(pInfo1->iAge < pInfo2->iAge)?-1:1;
   break;
default:
   ASSERT(0);
   break;
}
//根据当前的排序方式进行调整
if(pV->m_fAsc)
   return iCompRes;
else
   return iCompRes*-1;
}

排序最快:
CListCtrl::SortItems
Example

// Sort the item in reverse alphabetical order.
static int CALLBACK
MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
// lParamSort contains a pointer to the list view control.
// The lParam of an item is just its index.
CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
CString    strItem1 = pListCtrl->GetItemText(lParam1, 0);
CString    strItem2 = pListCtrl->GetItemText(lParam2, 0);

return strcmp(strItem2, strItem1);
}

void snip_CListCtrl_SortItems()
{
// The pointer to my list view control.
extern CListCtrl* pmyListCtrl;

// Sort the list view items using my callback procedure.
pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
}

If you don’t want to allow the users to sort the list by clicking on the header, you can use the style LVS_NOSORTHEADER. However, if you do want to allow sorting, you do not specify the LVS_NOSORTHEADER. The control, though, does not sort the items. You have to handle the HDN_ITEMCLICK notification from the header control and process it appropriately. In the code below, we have used the sorting function SortTextItems() developed in a previous section. You may choose to sort the items in a different manner.
Step 1: Add two member variables
Add two member variables to the CListCtrl. The first variable to track which column has been sorted on, if any. The second variable to track if the sort is ascending or descending.
        int nSortedCol;
        BOOL bSortAscending;

Step 2: Initialize them in the constructor.
Initialize nSortedCol to -1 to indicate that no column has been sorted on. If the list is initially sorted, then this variable should reflect that.
        nSortedCol = -1;
        bSortAscending = TRUE;
Step 3: Add entry in message map to handle HDN_ITEMCLICK
Actually you need to add two entries. For HDN_ITEMCLICKA and HDN_ITEMCLICKW. Do not use the class wizard to add the entry. For one, you need to add two entries whereas the class wizard will allow you only one. Secondly, the class wizard uses the wrong macro in the entry. It uses ON_NOTIFY_REFLECT() instead of ON_NOTIFY(). Since the HDN_ITEMCLICK is a notification from the header control to the list view control, it is a direct notification and not a reflected one.
ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked)
ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
Note that we specify the same function for both the notification. Actually the program will receive one or the other and not both. What notification it receives will depend on the OS. The list view control on Windows 95 will send the ANSI version and the control on NT will send the UNICODE version.
Also, note that the second argument is zero. This value filters for the id of the control and we know that header control id is zero.

Step 4: Write the OnHeaderClicked() function
Here’s where you decide what to do when the user clicks on a column header. The expected behaviour is to sort the list based on the values of the items in that column. In this function we have used the SortTextItems() function developed in a previous section. If any of the columns displays numeric or date values, then you would have to provide custom sorting for them.
void CMyListCtrl::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
        HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;

        if( phdn->iButton == 0 )
        {
                // User clicked on header using left mouse button
                if( phdn->iItem == nSortedCol )
                        bSortAscending = !bSortAscending;
                else
                        bSortAscending = TRUE;

                nSortedCol = phdn->iItem;
                SortTextItems( nSortedCol, bSortAscending );

        }
        *pResult = 0;
}

让CListCtrl的SubItem也具有编辑功能:
要重载一个文本框,然后在LVN_BEGINLABELEDIT时改变文本框位置。
CInEdit m_InEdit;

    if( ( GetStyle() & LVS_TYPEMASK ) == LVS_REPORT && ( m_nEditSubItem != 0 ) )
    {
        HWND    hwndEdit;
        CRect    rtBound;
        CString strText;

        hwndEdit = (HWND)SendMessage( LVM_GETEDITCONTROL );
        GetSubItemRect( pDispInfo->item.iItem, m_nEditSubItem, LVIR_LABEL, rtBound );
        m_InEdit.SubclassWindow( hwndEdit );
        m_InEdit.m_left = rtBound.left;
        strText = GetItemText( pDispInfo->item.iItem, m_nEditSubItem );
        m_InEdit.SetWindowText( strText );
    }

void CInEdit::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
    CRect rtClient;

    lpwndpos->x = m_left; // m_left在LVN_BEGINLABELEDIT中设置

    CEdit::OnWindowPosChanging(lpwndpos);
    // TODO: Add your message handler code here
}

导出数据为excel文件
使用ODBC将数据输出到excel数据区
void ExportAsExcel(CString filename,CListCtrl &resultlist,CWnd * wnd)
{
CDatabase database;
CString sDriver = "MICROSOFT EXCEL DRIVER (*.XLS)"; // Excel安装驱动
CString sSql,sExcelFile;
//弹出对话框选择路径
   CFileDialog fileDlg (FALSE, "Path", filename,OFN_FILEMUSTEXIST| OFN_HIDEREADONLY, "*.xls",wnd);
if( fileDlg.DoModal()==IDOK)
{
   sExcelFile = fileDlg.GetPathName();     // 要建立的Excel文件
   CFileFind finder;
   BOOL bWorking = finder.FindFile(sExcelFile);//寻找文件
   if (bWorking)//如果已经存在文件,则删除
   {
    CFile::Remove((LPCTSTR)sExcelFile);
   }
}
else return;
TRY
{
   // 创建进行存取的字符串
   sSql.Format("DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s",sDriver, sExcelFile, sExcelFile);
   // 创建数据库 (既Excel表格文件)
   if( database.OpenEx(sSql,CDatabase::noOdbcDialog) )
   {
    CHeaderCtrl* pHeader = resultlist.GetHeaderCtrl();
    //获得行,列的个数
    int nColCount = pHeader->GetItemCount();
    int nLineCount = resultlist.GetItemCount();
    int ColOrderArray[100];
    CString ca[100];
    resultlist.GetColumnOrderArray(ColOrderArray, nColCount);
    //检索各列的信息,确定列标题的内容
    for(int i =0 ; i< nColCount; i++)
    {
     LVCOLUMN lvc;
     char text[100];
     lvc.mask = LVCF_TEXT|LVCF_SUBITEM;
     lvc.pszText = text;
     lvc.cchTextMax = 100;
     resultlist.GetColumn(ColOrderArray[i], &lvc);
     ca[i] = lvc.pszText;      
    }
    // 创建表结构
    CString tempsql="(";
    for(i =0 ; i< nColCount-1; i++)
    {
     tempsql+=ca[i];
     tempsql+=" TEXT,";
    }
    tempsql+=ca[nColCount-1];
    tempsql+=" TEXT)";
    sSql = "CREATE TABLE Sheet1 ";
    sSql+=tempsql;
    database.ExecuteSQL(sSql);
    //插入数据
    int item_count=resultlist.GetItemCount();
    tempsql="(";
    for(i =0 ; i< nColCount-1; i++)
    {
     tempsql+=ca[i];
     tempsql+=" ,";
    }
    tempsql+=ca[nColCount-1];
    tempsql+=")";
    for(int itemnum=0;itemnum     sSql="";
     sSql ="INSERT INTO Sheet1 ";
     sSql+=tempsql;
     sSql+="VALUES ('";
     for(i =0 ; i< nColCount-1; i++)
     {
      sSql+=resultlist.GetItemText(itemnum, i);
      sSql+="','";
     }
     sSql+=resultlist.GetItemText(itemnum, nColCount-1);
     sSql+="')";
     database.ExecuteSQL(sSql);
    }
   }     
   // 关闭数据库
   database.Close();
   AfxMessageBox("Excel文件写入成功!");
}
CATCH_ALL(e)
{
   TRACE1("Excel驱动没有安装: %s",sDriver);
}
END_CATCH_ALL;
}

 

转自:http://hi.baidu.com/silyt/blog/item/f813be24077f07368744f9f1.html

转载于:https://www.cnblogs.com/afarmer/archive/2012/03/31/2427217.html

你可能感兴趣的:(数据结构与算法,数据库,php)