自身支持排序的ListCtrl
VC里的CListCtrl是个很不好用的控件,特别是排序,实现起来很麻烦。
关于排序的基本用法,有一篇很好的文章:
http://www.vchelp.net/vchelp/zart/sortl.asp?type_id=9&class_id=1&cata_id=1&article_id=73&search_term=
但是,在这个例子里也存在个问题:排序的实现是和数据源相关的,如果有若干个表需要排序的话,每个表都要写相应的代码,这是一件非常痛苦的事。
所以,在上面的基础上,重新写了一个自身支持排序的CSoftList,从某个意义上说,也算是实现文档和视图的真正分离。
下面说说几个主要的地方。
1. CSortList是CListCtrl的派生类 (好象是废话)
2. 要让CSortList自行排序,当然得让CSortList自己处理LVN_COLUMNCLICK消息
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnclick)
OnLvnColumnclick的作用就是设置排序列,排序方式,最后调用SortItems()。
3. 排序的具体实现,也就是CALLBACK ListCompare(...)的实现:
基本要求: 让CSortList根据Item进行排序,而与数据源无关。(当然这样也有不足之处,就是排序只能按文本排序了。如果你需要按数字排序的话,那就只有特殊处理了。)
这里涉及到的最主要的函数是:
1)ListCompare - 实现排序的回调函数
int CALLBACK CSortList::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
2)CListCtrl::FindItem - 查找相应的Item, 查找方式可指定
int FindItem( LVFINDINFO* pFindInfo, int nStart = -1 ) const;
FindItem的具体细节请参考MSDN.
基本思路:
1)ListCompare的原型:传入的参数lParam1,lParam2是相应两行的ItemData;
2)FindItem根据lParam1,lParam2得到相应的Item;
FindItem有多种查找方式(查找方式通过LVFINDINFO->flags进行设置),这里只是根据ItemData进行查找。
3)再用GetItemText得到排序列的ItemText;
4)最后就是比较ItemText了。
4. 显示指明排序方式的标记
简单地说,就是给CListCtrl的CHeadCtrl指定一个CImageList,根据需要指定Column要显示的Image。
主要函数:
1)CHeaderCtrl::GetItem - 取得表头控制中某一项目的内容
BOOL GetItem( int nPos, HDITEM* pHeaderItem ) const;
2)CHeaderCtrl::SetItem - 设置表头控制中某一项目的内容
BOOL SetItem( int nPos, HDITEM* pHeaderItem );
下面是源代码:
只有两个文件:CSortList.H, CSortList.cpp
直接把这两个文件放到项目里就可以了。
唯一要注意的是:IDB_HDRUP, IDB_HDRDOWN 是两个位图资源,分别表示顺序和倒序,这个就需要自己弄了。
A. SoftList.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
//
/////////////////////////////////////////////////////////////////////////////
// CSortList window
class CSortList : public CListCtrl
{
// Construction
public:
CSortList();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSortList)
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CSortList();
// Generated message map functions
protected:
//{{AFX_MSG(CSortList)
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 );
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. SoftList.cpp
// SortList.cpp : implementation file
//
#include "stdafx.h"
#include "SortList.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CSortList
CSortList::CSortList()
{
m_bAsc=TRUE;
//this->m_nSortedCol = -1;
CreateSortIcons();
//GetHeaderCtrl()->SetImageList(&m_imglstSortIcons);
}
CSortList::~CSortList()
{
m_imglstSortIcons.DeleteImageList();
m_bmpUpArrow.DeleteObject();
m_bmpDownArrow.DeleteObject();
}
BEGIN_MESSAGE_MAP(CSortList, CListCtrl)
//{{AFX_MSG_MAP(CSortList)
//}}AFX_MSG_MAP
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnLvnColumnclick)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSortList message handlers
void CSortList::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 CSortList::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 CSortList::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 CSortList::GetFullRowSelect()
{
return ( GetExtendedStyle()&LVS_EX_FULLROWSELECT) == LVS_EX_FULLROWSELECT;
}
void CSortList::SetFullRowSelect( bool bFullRowSelect )
{
if( bFullRowSelect )
SetExtendedStyle( GetExtendedStyle()|LVS_EX_FULLROWSELECT );
else
SetExtendedStyle( GetExtendedStyle()&(~LVS_EX_FULLROWSELECT) );
}
bool CSortList::GetGridLines()
{
return ( GetExtendedStyle() & LVS_EX_GRIDLINES ) == LVS_EX_GRIDLINES;
}
void CSortList::SetGridLines( bool bGridLines )
{
if( bGridLines )
SetExtendedStyle( GetExtendedStyle()|LVS_EX_GRIDLINES );
else
SetExtendedStyle( GetExtendedStyle()&(~LVS_EX_GRIDLINES) );
}
int CALLBACK CSortList::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CSortList* pList=(CSortList*)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);
int iCompRes = 0;
if(Str1 > Str2)
iCompRes = 1;
else if(Str1 == Str2)
iCompRes = 0;
else
iCompRes = -1;
if(pList->m_bAsc)
return iCompRes;
else
return iCompRes*-1;
return 0;
}