关于CListCtrl 排序与导出

 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才是静态的。在排序时,这样操作:

 1 int  CListInfo::CompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
 2 {
 3    // lParamSort contains a pointer to the list view control.
 4    // The lParam of an item is just its index.
 5    CString strFir , strSec;
 6    int cmp = 0;
 7    CListInfo* pListInfo = (CListInfo*) lParamSort;
 8
 9    LVFINDINFO info; 
10    int nIndex; 
11    info.flags = LVFI_PARAM; //set LVFI_PARAM to info
12    info.lParam = lParam1; //get the item text item data(lParam1)
13    if ( (nIndex=pListInfo->FindItem(&info)) != -1
14        strFir = pListInfo->GetItemText(nIndex, pListInfo->m_nSortCol); 
15    info.lParam = lParam2; //get the item text item data(lParam2)
16    if ( (nIndex=pListInfo->FindItem(&info)) != -1)
17        strSec = pListInfo->GetItemText(nIndex, pListInfo->m_nSortCol); 
18
19    cmp = strFir.CompareNoCase(strSec);
20
21    return pListInfo->m_bAscending ? cmp : -cmp;
22}

这里使用了FindItem来获得Item的静态Index。


2. 使用表示上下的小图标来表示排序顺序。
   首先需要给表头栏添加小图标:
   CImageList m_ImageList;
   m_ImageList.Create(IDB_SORT , 16 , 1 , RGB(255 , 255 , 255));
   GetHeaderCtrl()->SetImageList(&m_ImageList);
   点击了表头栏后,需要按照排序顺序来选择贴图:
   

 1 HDITEM hdi;
 2 CHeaderCtrl *  pHdrCtrl  =  GetHeaderCtrl();
 3 int  nCount  =  pHdrCtrl -> GetItemCount();
 4
 5 for  ( int  nItem  =   0 ; nItem  <  nCount; nItem ++ )
 6 {
 7    pHdrCtrl->GetItem(nItem, &hdi);
 8
 9    if (nItem == m_nSortCol)
10    {
11        hdi.mask= HDI_IMAGE | HDI_FORMAT;
12
13        hdi.iImage = m_bAscending ? 0 : 1;
14        hdi.fmt = HDF_LEFT | HDF_STRING |HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
15    }

16    else
17    {
18        hdi.mask= HDI_FORMAT;
19
20        hdi.fmt = HDF_LEFT | HDF_STRING;
21    }

22
23    pHdrCtrl->SetItem(nItem, &hdi);
24}

通过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; 

  

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值