自身支持排序的ListCtrl

自身支持排序的CSortListCtrl


VC里的CListCtrl是个很不好用的控件,特别是排序,实现起来很麻烦。

关于排序的基本用法,有一篇很好的文章:
http://www.vchelp.net/vchelp/zart/sortl.asp?type_id=9&class_id=1&cata_id=1&article_id=73&search_term=

但是,在这个例子里也存在个问题:排序的实现是和数据源相关的,如果有若干个表需要排序的话,每个表都要写相应的代码,这是一件非常痛苦的事。

所以,自己重新写了一个自身支持排序的CSoftListCtrl。并且实现了按不同数据类型排序,比较有实用价值!
下面说说几个主要的地方。

   需要用到的一些函数:
   int CListCtrl::InsertColumn( int nCol, const LVCOLUMN* pColumn ); - 创建Column
   BOOL CListCtrl::SetItem( const LVITEM* pItem ); - 设置ListCtrl的Item
   int CListCtrl::FindItem( LVFINDINFO* pFindInfo, int nStart = -1 ) const; - 查找相应的Item
   BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData ); - 调用回调函数进行排序
   int CALLBACK CListCtrl::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); - 负责排序的回调函数
   CHeaderCtrl* CListCtrl::GetHeaderCtrl( ); - 得到CHeaderCtrl
   BOOL CHeaderCtrl::SetItem( int nPos, HDITEM* pHeaderItem ); - 设置CHeaderCtrl的Item

   函数的详细细节请参考MSDN.
  

1. CSortListCtrl是CListCtrl的派生类

2. 要让CSortList自行排序,需要让CSortList自己处理LVN_COLUMNCLICK消息
 ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnclick)
   OnLvnColumnclick的作用就是设置排序列,排序方式,最后调用SortItems()。

3. 记录数据类型的信息
   为了支持不同的数据类型,需要在Header中保存必要的类型信息
   在SortListCtrl.h定义数据类型:  enum { INT_TYPE = 0, STRING_TYPE, DOUBLE_TYPE };
   目前只定义了三种,可自行添加。
   在创建Column时,用CHeaderCtrl::SetItem()在HeaderCtrl中设置数据类型信息。

4. 排序方式和排序列
   在CSortListCtrl中增加两个成员:
   BOOL m_bAsc;//是否顺序排序
   int m_nSortedCol;//当前排序的列
   在OnLvnColumnclick()中设置这两个值

5. BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData );
   pfnCompare - 回调函数
   dwData - 传递到回调函数的参数,实际传递的是CListCtrl的this的指针
   
6.  int CALLBACK CSortListCtrl::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
   lParam1,lParam2 - CListCtrl::SetItem()所设置的LVITEM::lParam, 参阅LVITEM结构和CListCtrl::SetItemData()定义
   lParamSort - lParamSort 即 CListCtrl::SortItems()的第二个参数 DWORD dwData
   实现:
   根据lParam1,lParam2得到Item及ItemText
   根据m_nSortedCol从Header中得到数据类型
   根据数据类型和m_bAsc对ItemText进行排序
  

下面是示例代码和源文件:

只有两个文件:CSortListCtrl.H, CSortListCtrl.cpp
直接把这两个文件放到项目里就可以了。
唯一要注意的是:IDB_HDRUP, IDB_HDRDOWN 是两个位图资源,分别表示顺序和倒序,这个就需要自己弄了。


A. SoftListCtrl.h

#if !defined(AFX_SORTLIST_H__6ACE2F6F_AEFE_11D3_BDE9_F4145AA4F676__INCLUDED_)
#define AFX_SORTLIST_H__6ACE2F6F_AEFE_11D3_BDE9_F4145AA4F676__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// SortList.h : header file
//

/
// CSortListCtrl window

class CSortListCtrl : public CListCtrl
{
// Construction
public:
 CSortListCtrl();
// Attributes
public:

// Operations
public:
 enum { INT_TYPE = 0, STRING_TYPE, DOUBLE_TYPE };
// Overrides
 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CSortListCtrl)
 //}}AFX_VIRTUAL

// Implementation
public:
 virtual ~CSortListCtrl();

 // Generated message map functions
protected:
 //{{AFX_MSG(CSortListCtrl)
 afx_msg bool OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult);
 //}}AFX_MSG
 
 DECLARE_MESSAGE_MAP()
public:
 afx_msg void OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult);

 void CreateSortIcons();
 void SetSortIcon();

 bool GetFullRowSelect();
 // 设置为行选中
 void SetFullRowSelect( bool bFullRowSelect );

 bool GetGridLines();
 // 设置绘制表格
 void SetGridLines( bool bGridLines );

 void UpdateControlTitle();

 static CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
public:
 BOOL m_bAsc;//是否顺序排序
 int m_nSortedCol;//当前排序的列

private:
 CBitmap                            m_bmpUpArrow;
 CBitmap                            m_bmpDownArrow;
 int                                m_nUpArrow;
 int                                m_nDownArrow;
 CImageList                         m_imglstSortIcons;
};

/

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SORTLIST_H__6ACE2F6F_AEFE_11D3_BDE9_F4145AA4F676__INCLUDED_)


B. SoftListCtrl.cpp

// SortList.cpp : implementation file
//

#include "stdafx.h"
#include "../DbpsGUI.h"
#include "SortListCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CSortListCtrl

CSortListCtrl::CSortListCtrl()
{
 m_bAsc=TRUE;
 m_nSortedCol = 0;
 CreateSortIcons();
 //GetHeaderCtrl()->SetImageList(&m_imglstSortIcons);
}

CSortListCtrl::~CSortListCtrl()
{
 m_imglstSortIcons.DeleteImageList();
    m_bmpUpArrow.DeleteObject();
    m_bmpDownArrow.DeleteObject();
}

BEGIN_MESSAGE_MAP(CSortListCtrl, CListCtrl)
 //{{AFX_MSG_MAP(CSortListCtrl)
 //}}AFX_MSG_MAP
 ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnclick)
END_MESSAGE_MAP()

/
// CSortListCtrl message handlers

void CSortListCtrl::OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult)
{
 LPNMLISTVIEW pNMListView = reinterpret_cast(pNMHDR);
 // TODO: Add your control notification handler code here
 //NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
 if( pNMListView->iSubItem == m_nSortedCol )
 {
  m_bAsc = !m_bAsc;
 }
 else
 {
  m_bAsc = TRUE;
  m_nSortedCol = pNMListView->iSubItem;
 }
 SortItems( ListCompare, (DWORD)this );
 SetSortIcon();
 *pResult = 0;
}


void CSortListCtrl::CreateSortIcons()
{
 if (!m_imglstSortIcons.m_hImageList)
 {
  COLORMAP cm = {RGB(0, 0, 0), GetSysColor(COLOR_GRAYTEXT)};
  m_imglstSortIcons.Create   (9, 5, ILC_COLOR24 | ILC_MASK, 2, 0);
  m_bmpUpArrow.LoadMappedBitmap(IDB_HDRUP, 0, &cm, 1);
  m_nUpArrow = m_imglstSortIcons.Add(&m_bmpUpArrow, RGB(255, 255, 255));
  m_bmpDownArrow.LoadMappedBitmap(IDB_HDRDOWN, 0, &cm, 1);
  m_nDownArrow = m_imglstSortIcons.Add(&m_bmpDownArrow, RGB(255, 255, 255));
 }
}

void CSortListCtrl::SetSortIcon()
{
 CHeaderCtrl* pHeaderCtrl = this->GetHeaderCtrl();
 ASSERT(pHeaderCtrl);

 pHeaderCtrl->SetImageList(&m_imglstSortIcons);
 for( int col = 0; col< GetHeaderCtrl()->GetItemCount(); col++ )
 {
  HDITEM hdrItem = { 0,};

  hdrItem.mask = HDI_FORMAT | HDI_IMAGE;

  BOOL ret = pHeaderCtrl->GetItem(col-1, &hdrItem);
  ret = pHeaderCtrl->GetItem(col+1, &hdrItem);
  ret = pHeaderCtrl->GetItem(col, &hdrItem);
  if ( m_nSortedCol == col)
  {
   hdrItem.fmt = hdrItem.fmt & HDF_JUSTIFYMASK | HDF_IMAGE | HDF_STRING | HDF_BITMAP_ON_RIGHT;
   if( m_bAsc )
    hdrItem.iImage = m_nUpArrow;
   else
    hdrItem.iImage = m_nDownArrow;
  }
  else
  {
   hdrItem.fmt = hdrItem.fmt & HDF_JUSTIFYMASK | HDF_STRING;
  }
  pHeaderCtrl->SetItem(col, &hdrItem);
 }
}

bool CSortListCtrl::GetFullRowSelect()
{
 return ( GetExtendedStyle()&LVS_EX_FULLROWSELECT) == LVS_EX_FULLROWSELECT;
}

void CSortListCtrl::SetFullRowSelect( bool bFullRowSelect )
{
 if( bFullRowSelect )
  SetExtendedStyle( GetExtendedStyle()|LVS_EX_FULLROWSELECT );
 else
  SetExtendedStyle( GetExtendedStyle()&(~LVS_EX_FULLROWSELECT) ); 
}

bool CSortListCtrl::GetGridLines()
{
 return ( GetExtendedStyle() & LVS_EX_GRIDLINES ) == LVS_EX_GRIDLINES;
}

void CSortListCtrl::SetGridLines( bool bGridLines )
{
 if( bGridLines )
  SetExtendedStyle( GetExtendedStyle()|LVS_EX_GRIDLINES );
 else
  SetExtendedStyle( GetExtendedStyle()&(~LVS_EX_GRIDLINES) ); 
}

void CSortListCtrl::UpdateControlTitle()
{
 CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl( );
 if( pHeaderCtrl != NULL && pHeaderCtrl->GetItemCount()>0 )
 {
  HDITEM headerItem = {0};
  for(int i=0;iGetItemCount();i++)
  {
   headerItem.mask = HDI_LPARAM;
   pHeaderCtrl->GetItem( i, &headerItem );
   UINT nID = (UINT)(headerItem.lParam) >> 16;
   CString sHead;
   sHead.Format( nID );
   headerItem.mask = HDI_TEXT ;
   headerItem.pszText = (LPSTR)(LPCSTR)sHead;
   pHeaderCtrl->SetItem( i, &headerItem );
  }
 }
}

int CALLBACK CSortListCtrl::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
 CSortListCtrl* pList=(CSortListCtrl*)lParamSort;
 int nItem1, nItem2;
    
 LVFINDINFO FindInfo;
 FindInfo.flags = LVFI_PARAM;    // 指定查找方式
 FindInfo.lParam = lParam1;
 nItem1 = pList->FindItem(&FindInfo, -1); // 得到对应Item索引
 FindInfo.lParam = lParam2;
 nItem2 = pList->FindItem(&FindInfo, -1);

 if((nItem1 == -1) || (nItem2 == -1))
 {
  TRACE("无法找到!/n");
  return 0;
 }

 CString str1,str2;
 str1 = pList->GetItemText(nItem1, pList->m_nSortedCol); // 得到排序列的Text
 str2 = pList->GetItemText(nItem2, pList->m_nSortedCol);

 HDITEM headerItem;
 headerItem.mask = HDI_LPARAM;
 CHeaderCtrl* pHeaderCtrl = pList->GetHeaderCtrl( );
 pHeaderCtrl->GetItem( pList->m_nSortedCol, &headerItem );
 UINT nType = (UINT)(headerItem.lParam);
 
 int iCompRes = 0;
 switch( nType )
 {
 case INT_TYPE:
  {
   int i1,i2;
   i1 = atoi( str1 );
   i2 = atoi( str2 );
   if( i1 > i2)
    iCompRes = 1;
   else if( i1 == i2 )
    iCompRes = 0;
   else
    iCompRes = -1;
  }
  break;
 case DOUBLE_TYPE:
  {
   double i1,i2;
   i1 = atof( str1 );
   i2 = atof( str2 );
   if( i1 > i2)
    iCompRes = 1;
   else if( i1 == i2 )
    iCompRes = 0;
   else
    iCompRes = -1;
  }
  break;
 case STRING_TYPE:
 default:
  {
   if( str1 > str2)
    iCompRes = 1;
   else if( str1 == str2 )
    iCompRes = 0;
   else
    iCompRes = -1;
  }
  break;

 }
 if(pList->m_bAsc)
  return iCompRes;
 else
  return iCompRes*-1;
 return 0;
}

你可能感兴趣的:(自身支持排序的ListCtrl)