自身支持排序的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<LPNMLISTVIEW>(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;i<pHeaderCtrl->GetItemCount();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;
}