http://www.vchelp.net/vchelp/zart/sortl.asp?type_id=9&class_id=1&cata_id=1&article_id=73&search_term
在ListCtrl中进行排序 |
闻怡洋 wyy_cq@21cn.com http://www.vchelp.net/ |
列表控件(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; } |
http://www.cppblog.com/zhouhuishine/articles/28210.html
1. 排序函数。
排序主要依靠的是SortItems(CompareProc , (LPARAM)this)函数,因此需要一个排序的静态函数CompareProc 作为参数,在CompareProc 函数中,获得ItemText的操作不可以使用简单的GetItemText(lParam1,szItemText)函数,因为在排序操作中lParam1已经不再是简单的Item Index了(由于排序过程中Index)在不断的变化。所以需要在每个Item中先使用SetItemData保存Index,这样的Index才是静态的。在排序时,这样操作:
![](https://i-blog.csdnimg.cn/blog_migrate/cbef093dcc044b2793832001e2365e43.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/2f88ce130b654eb5dc6788e02dbcfc90.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
10
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
11
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
12
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
13
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
14
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
15
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
16
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
17
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
18
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
19
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
20
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
21
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
22
![](https://i-blog.csdnimg.cn/blog_migrate/0ac3a2d53663ec01c7f7225264eeefae.gif)
这里使用了FindItem来获得Item的静态Index。
2. 使用表示上下的小图标来表示排序顺序。
首先需要给表头栏添加小图标:
CImageList m_ImageList;
m_ImageList.Create(IDB_SORT , 16 , 1 , RGB(255 , 255 , 255));
GetHeaderCtrl()->SetImageList(&m_ImageList);
点击了表头栏后,需要按照排序顺序来选择贴图:
![](https://i-blog.csdnimg.cn/blog_migrate/cbef093dcc044b2793832001e2365e43.gif)
2
![](https://i-blog.csdnimg.cn/blog_migrate/cbef093dcc044b2793832001e2365e43.gif)
3
![](https://i-blog.csdnimg.cn/blog_migrate/cbef093dcc044b2793832001e2365e43.gif)
4
![](https://i-blog.csdnimg.cn/blog_migrate/cbef093dcc044b2793832001e2365e43.gif)
5
![](https://i-blog.csdnimg.cn/blog_migrate/cbef093dcc044b2793832001e2365e43.gif)
6
![](https://i-blog.csdnimg.cn/blog_migrate/2f88ce130b654eb5dc6788e02dbcfc90.gif)
7
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
8
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
9
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
10
![](https://i-blog.csdnimg.cn/blog_migrate/f70a0fde2b51b7dd92a70e712e540cf6.gif)
11
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
12
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
13
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
14
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
15
![](https://i-blog.csdnimg.cn/blog_migrate/4a5daaec04350a363f186a4d2c5ed6ce.gif)
16
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
17
![](https://i-blog.csdnimg.cn/blog_migrate/f70a0fde2b51b7dd92a70e712e540cf6.gif)
18
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
19
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
20
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
21
![](https://i-blog.csdnimg.cn/blog_migrate/4a5daaec04350a363f186a4d2c5ed6ce.gif)
22
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
23
![](https://i-blog.csdnimg.cn/blog_migrate/df37983f39daa189b8c814e01a6a9011.gif)
24
![](https://i-blog.csdnimg.cn/blog_migrate/0ac3a2d53663ec01c7f7225264eeefae.gif)
通过HDITEM的iImage参数即可决定选择第几个图片(图片的顺序按照m_ImageList.Create(IDB_SORT , 16 , 1 , RGB(255 , 255 , 255));
中的16像素点来决定)。
http://www.china-askpro.com/msg11/qa52.shtml
列表框排序
//对指定子项排序
void CDataListCtrl::Sort(DWORD dwSub)
{
static DWORD LastSort;
//相同翻转
if(dwSub == LastSort)
{
m_Revert *= -1;
}
else
{
LastSort = dwSub;
m_Revert = 1;
}
m_pThis = this;
SortItems( (PFNLVCOMPARE)CompareFunc, dwSub );
TRACE("Sort By Sub %d/n", dwSub);
}
//排序回调函数
int CALLBACK CDataListCtrl::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
/*
The comparison function must return a negative value
if the first item should precede the second, a positive
value if the first item should follow the second, or
zero if the two items are equivalent.
*/
if((lParamSort == 0) || (m_pThis == NULL))
{
if (lParam1 > lParam2)
return -m_Revert;
else
return m_Revert;
}
int nItem1, nItem2;
LVFINDINFO FindInfo;
FindInfo.flags = LVFI_PARAM;
FindInfo.lParam = lParam1;
nItem1 = m_pThis->FindItem(&FindInfo, -1);
FindInfo.lParam = lParam2;
nItem2 = m_pThis->FindItem(&FindInfo, -1);
if((nItem1 == -1) || (nItem2 == -1))
{
TRACE("无法找到!/n");
return 0;
}
CString Str1,Str2;
Str1 = m_pThis->GetItemText(nItem1, lParamSort);
Str2 = m_pThis->GetItemText(nItem2, lParamSort);
if(Str1 > Str2)
return -m_Revert;
else if(Str1 == Str2)
return 0;
else
return m_Revert;
}
如前面的例子,排序是通过成员函数SortItems来进行的。SortItems成员函数有两个参数。第一个是排序所用的排序回调函数,第二个是需传入的参数。
排序回调函数是排序的关键。它有三个入口参数:第一和第二个为进行比较的两项的参数;第三参数为排序时传入的参数即SortItems中传入的参数。
比较过程是这样的:
如果,第一项应在第二项的前面,则返回一个负值;
如果,第一项应在第二项的后面,则返回一个正值;
否则,第一项与第二项相等,则返回零。
这里,需要注意的是,回调函数是在对什么进行比较。回调函数进行比较的两项,是在插入列表项的参数数据项。也就是说,在插入列表项时,必须设置参数项(LVIF_PARAM)。这样才能对列表项的每一项进行比较,而这往往是我们忽略的参数。
这样,我们就可以对列表控件单击列表头,响应消息进行排序了。
此外,你可以访问http://www.codeguru.com/listview/index.shtml,那里有很多排序的实际例子。
列表控制有一个特殊的功能,当以详细资料方式显示时,列表顶部的表头可以当作按钮来使用,这可以通过列表控制创建时的风格来控制。当鼠标点击列表头名称时,列表控制就会向其父窗口发送一个LNV_COLUMNCLICK消息,利用类导向中列表控制IDC_LISTCTRL对应的LNV_COLUMNCLICK消息加入相应处理函数,就可将表列按照特定顺序进行排列。其函数使用方法见程序,其中iSort为排序的表列索引号,(PFNLVCOMPARE)CompareFunc为进行具体排序的回调函数,也就是说,通过鼠标点击表头实现的排序过程是由第三方开发的专用排序函数来实现的,排序函数只是实现表项的具体比较操作,而整个排序过程是由SortItemS属性通过不断调用这个函数来实现的。正常的排序过程是升序方式,通过调换排序函数中的参数值,就可实现降序排列,即将PARAM1与PARAM2调换位置。这个回调函数的前两个参数为表列中表项的索引号,第三个参数为排序的表列索引号。
void CVCLISTDlg::OnColumnclickListctrl(NMHDR* pNMHDR, LRESULT* pResult)
{ //鼠标左键单击表头处理函数
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
static int iSorted=-1;//排列序号
if (pNMListView->iSubItem==iSorted) return;
iSorted=pNMListView->iSubItem;
m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,iSorted);
*pResult = 0;
}
//排序时比较表项的回调函数
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,LPARAM lParamSort)
{ char *text1,*text2;
switch (lParamSort){
case 0L:text1=Sps[lParam1].szPm;
text2=Sps[lParam2].szPm;break;
case 1L:text1=Sps[lParam1].szSl;
text2=Sps[lParam2].szSl;break;
case 2L:text1=Sps[lParam1].szDj;
text2=Sps[lParam2].szDj;break;
case 3L:text1=Sps[lParam1].szJe;
text2=Sps[lParam2].szJe;break;
}
return (strcmp(text1,text2));//结果为>0 =0 <0
}
同样,也可以通过专用按钮来实现排序功能,如本文的排序按钮对应的功能代码如下:
void CVCLISTDlg::OnSort()
{ // TODO: Add your control notification handler code here
m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);}
实现点击列头排序:
定义可以排序的列表类
只需要多定义两个变量
class SortCListCtrl : public CListCtrl
{
// Construction
public:
SortCListCtrl();
// Attributes
public:
BOOL m_fAsc;//是否顺序排序
int m_nSortedCol;//当前排序的列
....
}
在使用可以排序列表时 实例化自己的变量
SortCListCtrl m_yktlist;
//响应点击列函数
void CAuditingCertView::OnColumnclickListYkt(NMHDR* pNMHDR, LRESULT* pResult)
{
for (int i = 0; i < m_yktlist.GetItemCount(); ++i)
{
m_yktlist.SetItemData(i, i);//供排序使用的item编号
}
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
//设置排序方式
if( pNMListView->iSubItem ==m_yktlist.m_nSortedCol )
m_yktlist.m_fAsc = !m_yktlist.m_fAsc;
else
{
m_yktlist.m_fAsc = TRUE;
m_yktlist.m_nSortedCol = pNMListView->iSubItem;
}
//调用排序函数,此函数为CListCtrl定义好的,但是需要调用我们定义的函数才比较任意两个项目的值
m_yktlist.SortItems(MyListCompare, (LPARAM)&m_yktlist);
for ( i = 0; i < m_yktlist.GetItemCount(); ++i){
m_yktlist.SetItemData(i, i);//供排序使用的item编号
CString s;
s.Format("%d",i+1);//编号
m_yktlist.SetItemText(i,0,s);
}
*pResult = 0;
}
///全局函数,比较两个项目的依据
int CALLBACK MyListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
//通过传递的参数来得到CSortList对象指针,从而得到排序方式
SortCListCtrl * pV=(SortCListCtrl *)lParamSort;
//通过ItemData来确定数据
CString szComp1,szComp2;
int iCompRes;
szComp1=pV->GetItemText(lParam1,pV->m_nSortedCol);
szComp2=pV->GetItemText(lParam2,pV->m_nSortedCol);
switch(pV->m_nSortedCol)
{
case(0):
//以第一列为根据排序 编号
iCompRes=atof(szComp1)<=atof(szComp2)?-1:1;
break;
case(4):
//以第5列为根据排序 总次数
iCompRes=atof(szComp1)<=atof(szComp2)?-1:1;
break;
default:
iCompRes=szComp1.Compare(szComp2);
break;
}
//根据当前的排序方式进行调整
if(pV->m_fAsc)
return iCompRes;
else
return -iCompRes;
}
导出数据为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<item_count;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;
}