您的位置:首页 > 其它

自身支持排序的CSortListCtrl

2006-07-27 18:33 351 查看
自身支持排序的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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: