请问ListCtr控件,如何按某一项进行排序?请提供一下详细的方法,和如何使用的说明?谢谢了!

解决方案 »

  1.   

    www.vckbase.com, 找CSortCtrlList的代码
      

  2.   

    对CListCtrl中的数据进行排序;
    将数据写入到CListCtrl向CListCtrl中写入数据,一般使用3个成员方法:
    CListCtrl::InsertColumn; 
    CListCtrl::InsertItem; 
    CListCtrl::SetItemText;
    InsertColumn被用于向显示列表中插入显示的列,例如:
        list.InsertColumn(0,"主项",LVCFMT_LEFT, 80);
        list.InsertColumn(1,"子项", LVCFMT_LEFT, 50);
    以上代码的目的是向列表中插入两个显示列,第一列标题为"主项",对齐方式为左对齐,宽度为80;第二列标题为"子项",对齐方式为左对齐,宽度为50。注意: CListCtrl在显示为Report样式时,主项为第一项,后面紧跟着的为子项。主项和子项的数据写入不同,这将在后面提到。InsertItem被用于向显示列表中插入主项数据,SetItemText被用于向列表中的子项写入数据,例如:
     list.InsertItem(0,"主数据");
    插入数据"主数据"到第一行的主项
     list.SetItemText(0,1,"子数据");
    将第一行的子项数据设置为"子数据"其完整的代码为:
    /*list为CTestDlg中的成员变量; 已经在别处声明并成功初始化,并在属性页将该控件的View属性调整为Report*/
    BOOL CTestDlg::OnInitDialog(){
        CDialog::OnInitDialog();
        .........
        list.InsertColumn(0,"主项",LVCFMT_LEFT, 80);
        list.InsertColumn(1,"子项", LVCFMT_LEFT, 50);
        list.InsertItem(0,"主数据");
        list.SetItemText(0,1,"子数据");    .........
    }
    如图:
     
     排序问题在我的另一个程序中,需要通过CListCtrl控件显示日期数据,希望更具日期的升序显示,如图:
    要将排序功能加入到CListCtrl控件,首先必须从CListCtrl继承,然后将排序方法加入到继承的类中。这里我想到了两种写法:创建一个虚拟放方法
    在".h"文件中:
    class CSortableListCtrl:CListCtrl{
          virtual void SortAfterInsert(void); 

    创建一个方法,该方法的参数为进行排序的函数地址
    在".h"文件中:
    typedef void(CALLBACK *SORTLISTPROC)(LPARAM dwCtrl);//声明函数指针的类型在类中,声明成员方法:
    class CSortableListCtrl:CListCtrl{
          void SortAfterInsert(SORTLISTPROC lpSort, LPARAM dwCtrl); 

    比较这两种方法后,得出一下结论:
    采用第一种方法,要对列表中的数据进行排序,就必须从CSortableListCtrl类继承,然后再使用它;使用第二种方法,要对列表中的数据进行排序,就可以直接使用CSortableListCtrl来声明,然后定义SORTLISTPROC的执行函数来进行排序。
    很显然,对列表中的数据进行排序,实际上就是对排序方法的重载,所以使用第二种方法更直接。现在已经定了用哪构成方法来处理排序,那么下一步便是如何排序。由于我们采用的是第二种方法,那么直接写排序的执行函数。
    void CALLBACK SortListCallBack(LPARAM dwCtrl)
    { CSortableListCtrl* list = (CSortableListCtrl*) dwCtrl; ITEM *lItem;
     lItem = new ITEM;
     COleDateTime tmFirst, tmItem; //获取第一项数据
     strcpy(lItem->lpszItem, (LPCTSTR)list->GetItemText(0,0));//主键
     strcpy(lItem->lpszSItem1 , (LPCTSTR)list->GetItemText(0,1));//修改时间
     strcpy(lItem->lpszSItem2, (LPCTSTR)list->GetItemText(0,2));//读取时间 //第一个数据的修改时间
     tmFirst.ParseDateTime(lItem->lpszSItem1);
         
     int items;//指针位置,当前的数据
     items = list->GetItemCount();
     //比较数据 
     for (int i = 1; i< items; i++)
     {
        //以时间排序
      tmItem.ParseDateTime((LPCTSTR)list->GetItemText(i, 1));
     
      if (tmFirst > tmItem)
      {
       //插入时间比读取的时间大
       //将第一项的数据插入到i - 1这个位置
       list->InsertItem( i , lItem->lpszItem);
       list->SetItemText(i ,1,lItem->lpszSItem1);
       list->SetItemText(i ,2,lItem->lpszSItem2);
       //将第一项删除
       list->DeleteItem(0);
       delete lItem;
       return;
      } }
      //当程序执行到这个位置时,说明插入的值为最小值
      list->InsertItem(items, lItem->lpszItem);
      list->SetItemText(items, 1, lItem->lpszSItem1);
      list->SetItemText(items, 2, lItem->lpszSItem2);  list->DeleteItem(0);
                  
      delete lItem;
    }SortListCallBack必须在每一条数据写入完毕后调用,因为排序的算法是假定在写入数据之前,列表中的数据排列是有序的。在这个排序中,其中将两个值进行比较的方法还有待改进,希望朋友们多提意见。小节CListCtrl对于初次使用MFC框架的程序员来说,其数据写入的方法有点让人难以理解,我在这里也只是根据我自己在工作中遇到的问题,而将CListCtrl的一种使用方法讲解了一下,大家如果有兴趣去看一下MSDN上关于CListCtrl的其他用途。关于上面提到的数据排序,我使用了通过函数地址来调用函数的方法。这种方法的一个好处就是可以在不同的情况下定义不同的排序方式,而对已有的程序结构没有影响。
    以下为具体的代码,供大家参考,欢迎大家批评指教!.h文件#pragma once
    // CSortableListCtrltypedef void(CALLBACK* SORTLISTPROC)(LPARAM dwCtl);class CSortableListCtrl : public CListCtrl
    {
     DECLARE_DYNAMIC(CSortableListCtrl)public:
     CSortableListCtrl();
     virtual ~CSortableListCtrl();protected:
     DECLARE_MESSAGE_MAP()public:
     //在插入数据到列表后立即排序,如果用此方法,在插入数据时必须将该数据插入到第一行
     void SortAfterInsert(SORTLISTPROC lpSort, LPARAM dwCtrl);}; .Cpp文件// SortableListCtrl.cpp : 实现文件
    //#include "stdafx.h"
    #include "KnAssistant.h"
    #include "SortableListCtrl.h"
    #include ".\sortablelistctrl.h"
    // CSortableListCtrlIMPLEMENT_DYNAMIC(CSortableListCtrl, CListCtrl)
    CSortableListCtrl::CSortableListCtrl()
    {
    }CSortableListCtrl::~CSortableListCtrl()
    {
    }
    BEGIN_MESSAGE_MAP(CSortableListCtrl, CWnd)
    END_MESSAGE_MAP()//在插入数据到列表后立即排序
    void CSortableListCtrl::SortAfterInsert(SORTLISTPROC lpSort, LPARAM dwCtrl)
    {
     lpSort(dwCtrl);
    } 执行函数
    //对ListCtrl进行排序的回调函数
    //始终将第一个数据进行排序,在每个执行函数针对不同的排序方式,在这里,只针对第二列(日期)进行降序排列
    void CALLBACK SortListCallBack(LPARAM dwCtrl)
    {
     CSortableListCtrl* list = (CSortableListCtrl*) dwCtrl; ITEM *lItem;
     lItem = new ITEM;
     COleDateTime tmFirst, tmItem; //获取第一项数据
     strcpy(lItem->lpszItem, (LPCTSTR)list->GetItemText(0,0));//主键
     strcpy(lItem->lpszSItem1 , (LPCTSTR)list->GetItemText(0,1));//修改时间
     strcpy(lItem->lpszSItem2, (LPCTSTR)list->GetItemText(0,2));//读取时间 //修改时间
     tmFirst.ParseDateTime(lItem->lpszSItem1);
         
     int items;//指针位置,当前的数据
     items = list->GetItemCount();
     //比较数据 
     for (int i = 1; i< items; i++)
     {
        //以时间排序
      tmItem.ParseDateTime((LPCTSTR)list->GetItemText(i, 1));
     
      if (tmFirst > tmItem)
      {
       //插入时间比读取的时间大
       //将第一项的数据插入到i - 1这个位置
       list->InsertItem( i , lItem->lpszItem);
       list->SetItemText(i ,1,lItem->lpszSItem1);
       list->SetItemText(i ,2,lItem->lpszSItem2);
       //将第一项删除
       list->DeleteItem(0);
       delete lItem;
       return;
      } }
      //当程序执行到这个位置时,说明插入的值为最小值
      list->InsertItem(items, lItem->lpszItem);
      list->SetItemText(items, 1, lItem->lpszSItem1);
      list->SetItemText(items, 2, lItem->lpszSItem2);  list->DeleteItem(0);
                  
      delete lItem; 
    }
      

  3.   

    在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);
    }