www.pudn.com > CListCtrlfjs.rar > SortListCtrl.cpp
/*******************************************
没事儿,尽管用。
--栾义明
*********************************************/
#include "stdafx.h"
#include "SortListCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define ID_LIST_PRINT 200
#define ID_LIST_SAVE 201
#define ID_LIST_DELETE 202
#define ID_LIST_DELETEALL 203
LPCTSTR g_pszSection = _T("ListCtrls");
struct ItemData
{
public:
ItemData() : arrpsz( NULL ), dwData( NULL ),crText ( NULL ),crBak (NULL),nImage(NULL){}
LPTSTR* arrpsz;
DWORD dwData;
//color
COLORREF * crText;
COLORREF * crBak;
int *nImage;
private:
// ban copying.
ItemData( const ItemData& );
ItemData& operator=( const ItemData& );
};
CSortListCtrl::CSortListCtrl()
: m_iNumColumns( 0 )
, m_iSortColumn( -1 )
, m_bSortAscending( TRUE )
{
m_backcolor=crWindow = ::GetSysColor(COLOR_WINDOW);
m_textcolor=crWindowText = ::GetSysColor(COLOR_WINDOWFRAME);
m_checkbackcolor= crHighLight = ::GetSysColor(COLOR_HIGHLIGHT);
m_checktextcolor= crHighLightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
m_checkcolumcolor=m_backcolor;
m_OldColumnClick=-1;
m_bColumnColorShow=FALSE;
m_bExcelShow = FALSE ;
}
CSortListCtrl::~CSortListCtrl()
{
}
BEGIN_MESSAGE_MAP(CSortListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CSortListCtrl)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
ON_WM_DESTROY()
ON_WM_RBUTTONDOWN()
ON_WM_PAINT()
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_SYSCOLORCHANGE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSortListCtrl message handlers
void CSortListCtrl::PreSubclassWindow()
{
// the list control must have the report style.
ASSERT( GetStyle() & LVS_REPORT );
CListCtrl::PreSubclassWindow();
m_ctlHeader.SubclassDlgItem (0, this);
// VERIFY( m_ctlHeader.SubclassWindow( GetHeaderCtrl()->GetSafeHwnd() ) );
}
BOOL CSortListCtrl::SetHeadings( UINT uiStringID )
{
CString strHeadings;
VERIFY( strHeadings.LoadString( uiStringID ) );
return SetHeadings( strHeadings );
}
// the heading text is in the format column 1 text,column 1 width;column 2 text,column 3 width;etc.
BOOL CSortListCtrl::SetHeadings( const CString& strHeadings )
{
int iStart = 0;
for( ;; )
{
const int iComma = strHeadings.Find( _T(','), iStart );
if( iComma == -1 )
break;
const CString strHeading = strHeadings.Mid( iStart, iComma - iStart );
iStart = iComma + 1;
int iSemiColon = strHeadings.Find( _T(';'), iStart );
if( iSemiColon == -1 )
iSemiColon = strHeadings.GetLength();
const int iWidth = atoi( strHeadings.Mid( iStart, iSemiColon - iStart ) );
iStart = iSemiColon + 1;
if( InsertColumn( m_iNumColumns++, strHeading, LVCFMT_LEFT, iWidth ) == -1 )
return FALSE;
}
// UpdateHeadColumn();
return TRUE;
}
int CSortListCtrl::AddItem(LPCTSTR pszText, ... )
{
const int iIndex = InsertItem( GetItemCount(), pszText );
LPTSTR* arrpsz = new LPTSTR[ m_iNumColumns ];
COLORREF * clrText = new COLORREF[ m_iNumColumns ];
COLORREF * clrBak= new COLORREF[ m_iNumColumns ];
int * nImage =new int[ m_iNumColumns ];
arrpsz[ 0 ] = new TCHAR[ lstrlen( pszText ) + 1 ];
clrText[ 0 ] = crWindowText;
clrBak[ 0 ] = crWindow;
nImage[ 0 ] =-1;
(void)lstrcpy( arrpsz[ 0 ], pszText );
va_list list;
va_start( list, pszText );
//insert sub item and set subitem data
int iColumn;
for( iColumn = 1; iColumn < m_iNumColumns; iColumn++ )
{
pszText = va_arg( list, LPCTSTR );
ASSERT_VALID_STRING( pszText );
VERIFY( CListCtrl::SetItem( iIndex, iColumn, LVIF_TEXT, pszText, 0, 0, 0, 0 ) );
arrpsz[ iColumn ] = new TCHAR[ lstrlen( pszText ) + 1 ];
clrText[ iColumn ] = crWindowText;
clrBak[ iColumn ] = crWindow;
nImage[ iColumn ] =-1;
(void)lstrcpy( arrpsz[ iColumn ], pszText );
}
va_end( list );
VERIFY( SetArray( iIndex, arrpsz,clrText,clrBak,nImage ) );
//for( iColumn = 1; iColumn < m_iNumColumns; iColumn++ )
//
SetItemColor(iIndex,m_textcolor,m_backcolor);
return iIndex;
}
void CSortListCtrl::FreeItemMemory( const int iItem )
{
ItemData* pid = reinterpret_cast( CListCtrl::GetItemData( iItem ) );
LPTSTR* arrpsz = pid->arrpsz;
for( int i = 0; i < m_iNumColumns; i++ )
{
delete[] arrpsz[ i ];
}
delete[] pid->crText;
delete[] pid->crBak;
delete[] arrpsz;
delete pid;
VERIFY( CListCtrl::SetItemData( iItem, NULL ) );
}
BOOL CSortListCtrl::DeleteItem( int iItem )
{
FreeItemMemory( iItem );
return CListCtrl::DeleteItem( iItem );
}
BOOL CSortListCtrl::DeleteAllItems()
{
for( int iItem = 0; iItem < GetItemCount(); iItem ++ )
FreeItemMemory( iItem );
return CListCtrl::DeleteAllItems();
}
bool IsNumber( LPCTSTR pszText )
{
ASSERT_VALID_STRING( pszText );
for( int i = 0; i < lstrlen( pszText ); i++ )
if( !_istdigit( pszText[ i ] ) )
return false;
return true;
}
int NumberCompare( LPCTSTR pszNumber1, LPCTSTR pszNumber2 )
{
ASSERT_VALID_STRING( pszNumber1 );
ASSERT_VALID_STRING( pszNumber2 );
const int iNumber1 = atoi( pszNumber1 );
const int iNumber2 = atoi( pszNumber2 );
if( iNumber1 < iNumber2 )
return -1;
if( iNumber1 > iNumber2 )
return 1;
return 0;
}
bool IsDate( LPCTSTR pszText )
{
ASSERT_VALID_STRING( pszText );
// format should be 99/99/9999.
if( lstrlen( pszText ) != 10 )
return false;
return _istdigit( pszText[ 0 ] )
&& _istdigit( pszText[ 1 ] )
&& pszText[ 2 ] == _T('/')
&& _istdigit( pszText[ 3 ] )
&& _istdigit( pszText[ 4 ] )
&& pszText[ 5 ] == _T('/')
&& _istdigit( pszText[ 6 ] )
&& _istdigit( pszText[ 7 ] )
&& _istdigit( pszText[ 8 ] )
&& _istdigit( pszText[ 9 ] );
}
int DateCompare( const CString& strDate1, const CString& strDate2 )
{
const int iYear1 = atoi( strDate1.Mid( 6, 4 ) );
const int iYear2 = atoi( strDate2.Mid( 6, 4 ) );
if( iYear1 < iYear2 )
return -1;
if( iYear1 > iYear2 )
return 1;
const int iMonth1 = atoi( strDate1.Mid( 3, 2 ) );
const int iMonth2 = atoi( strDate2.Mid( 3, 2 ) );
if( iMonth1 < iMonth2 )
return -1;
if( iMonth1 > iMonth2 )
return 1;
const int iDay1 = atoi( strDate1.Mid( 0, 2 ) );
const int iDay2 = atoi( strDate2.Mid( 0, 2 ) );
if( iDay1 < iDay2 )
return -1;
if( iDay1 > iDay2 )
return 1;
return 0;
}
int CALLBACK CSortListCtrl::CompareFunction( LPARAM lParam1, LPARAM lParam2, LPARAM lParamData )
{
CSortListCtrl* pListCtrl = reinterpret_cast( lParamData );
ASSERT( pListCtrl->IsKindOf( RUNTIME_CLASS( CListCtrl ) ) );
ItemData* pid1 = reinterpret_cast( lParam1 );
ItemData* pid2 = reinterpret_cast( lParam2 );
ASSERT( pid1 );
ASSERT( pid2 );
LPCTSTR pszText1 = pid1->arrpsz[ pListCtrl->m_iSortColumn ];
LPCTSTR pszText2 = pid2->arrpsz[ pListCtrl->m_iSortColumn ];
ASSERT_VALID_STRING( pszText1 );
ASSERT_VALID_STRING( pszText2 );
if( IsNumber( pszText1 ) )
return pListCtrl->m_bSortAscending ? NumberCompare( pszText1, pszText2 ) : NumberCompare( pszText2, pszText1 );
else if( IsDate( pszText1 ) )
return pListCtrl->m_bSortAscending ? DateCompare( pszText1, pszText2 ) : DateCompare( pszText2, pszText1 );
else
// text.
return pListCtrl->m_bSortAscending ? lstrcmp( pszText1, pszText2 ) : lstrcmp( pszText2, pszText1 );
}
void CSortListCtrl::OnColumnClick( NMHDR* pNMHDR, LRESULT* pResult )
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
const int iColumn = pNMListView->iSubItem;
// if it's a second click on the same column then reverse the sort order,
// otherwise sort the new column in ascending order.
Sort( iColumn, iColumn == m_iSortColumn ? !m_bSortAscending : TRUE );
if(m_OldColumnClick!=iColumn && m_bColumnColorShow )
{
for(int i=0;i( this ) ) );
}
void CSortListCtrl::LoadColumnInfo()
{
// you must call this after setting the column headings.
ASSERT( m_iNumColumns > 0 );
CString strKey;
strKey.Format( _T("%d"), GetDlgCtrlID() );
UINT nBytes = 0;
BYTE* buf = NULL;
if( AfxGetApp()->GetProfileBinary( g_pszSection, strKey, &buf, &nBytes ) )
{
if( nBytes > 0 )
{
CMemFile memFile( buf, nBytes );
CArchive ar( &memFile, CArchive::load );
m_ctlHeader.Serialize( ar );
ar.Close();
m_ctlHeader.Invalidate();
}
delete[] buf;
}
}
void CSortListCtrl::SaveColumnInfo()
{
ASSERT( m_iNumColumns > 0 );
CString strKey;
strKey.Format( _T("%d"), GetDlgCtrlID() );
CMemFile memFile;
CArchive ar( &memFile, CArchive::store );
m_ctlHeader.Serialize( ar );
ar.Close();
DWORD dwLen = memFile.GetLength();
BYTE* buf = memFile.Detach();
VERIFY( AfxGetApp()->WriteProfileBinary( g_pszSection, strKey, buf, dwLen ) );
free( buf );
}
void CSortListCtrl::OnDestroy()
{
for( int iItem = 0; iItem < GetItemCount(); iItem ++ )
FreeItemMemory( iItem );
CListCtrl::OnDestroy();
}
BOOL CSortListCtrl::SetItemText( int nItem, int nSubItem, LPCTSTR lpszText )
{
if( !CListCtrl::SetItemText( nItem, nSubItem, lpszText ) )
return FALSE;
LPTSTR* arrpsz = GetTextArray( nItem );
LPTSTR pszText = arrpsz[ nSubItem ];
delete[] pszText;
pszText = new TCHAR[ lstrlen( lpszText ) + 1 ];
(void)lstrcpy( pszText, lpszText );
arrpsz[ nSubItem ] = pszText;
return TRUE;
}
BOOL CSortListCtrl::SetItemData( int nItem, DWORD dwData )
{
/*
if( nItem >= GetItemCount() )
return FALSE;
ItemData* pid = reinterpret_cast( CListCtrl::GetItemData( nItem ) );
ASSERT( pid );
pid->dwData = dwData;
return TRUE;*/
CListCtrl::SetItemData( nItem, dwData);
return TRUE;
}
DWORD CSortListCtrl::GetItemData( int nItem ) const
{
ASSERT( nItem < GetItemCount() );
ItemData* pid = reinterpret_cast( CListCtrl::GetItemData( nItem ) );
ASSERT( pid );
return pid->dwData;
}
BOOL CSortListCtrl::SetArray( int iItem, LPTSTR* arrpsz,COLORREF * clrText,COLORREF * clrBak,int * nImage )
{
ASSERT( CListCtrl::GetItemData( iItem ) == NULL );
ItemData* pid = new ItemData;
pid->arrpsz = arrpsz;
pid->crText = clrText;
pid->crBak = clrBak;
pid->nImage = nImage;
return CListCtrl::SetItemData( iItem, reinterpret_cast( pid ) );//set item data /
}
LPTSTR* CSortListCtrl::GetTextArray( int iItem ) const
{
ASSERT( iItem < GetItemCount() );
ItemData* pid = reinterpret_cast( CListCtrl::GetItemData( iItem ) );
return pid->arrpsz;
}
void CSortListCtrl::OnPaint()
{
Default();
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CListCtrl::OnPaint() for painting messages
}
void CSortListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
//draw each item.set txt color,bkcolor....
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast(pNMHDR);
// Take the default processing unless we set this to something else below.
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
{
// This is the notification message for an item. We'll request
// notifications before each subitem's prepaint stage.
*pResult = CDRF_NOTIFYSUBITEMDRAW;
}
else if (pLVCD->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM))
{
// This is the prepaint stage for a subitem. Here's where we set the
// item's text and background colors. Our return value will tell
// Windows to draw the subitem itself, but it will use the new colors
// we set here.
int nItem = static_cast (pLVCD->nmcd.dwItemSpec);
int nSubItem = pLVCD->iSubItem;
ItemData *pXLCD = (ItemData *) pLVCD->nmcd.lItemlParam;
ASSERT(pXLCD);
COLORREF crText = m_textcolor;//crWindowText;
COLORREF crBkgnd = m_backcolor;//crWindow;
int nImage;
if (pXLCD){
crText = (pXLCD->crText)[nSubItem];
crBkgnd = (pXLCD->crBak)[nSubItem];
nImage = (pXLCD->nImage)[nSubItem];
}
// store the colors back in the NMLVCUSTOMDRAW struct
pLVCD->clrText = crText;
pLVCD->clrTextBk = crBkgnd;
// pLVCD-> = crBkgnd;
CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect rect;
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
if(nSubItem==0 && m_bExcelShow )
{
// CPen pen1,*oldpen;
rect.left+=1;
rect.top+=1;
rect.right-=1;
rect.bottom-=1;
CBrush brush(RGB(100,100,10)),*oldbrush;
oldbrush=pDC->SelectObject(&brush);
pDC->Rectangle(&rect);
pDC->SelectObject(oldbrush);
if (GetItemState(nItem, LVIS_SELECTED))
pDC->Draw3dRect(&rect,RGB(10,0,10),RGB(255,230,255));
else
pDC->Draw3dRect(&rect,RGB(255,230,255),RGB(10,0,10));
pDC->SetTextColor(RGB(255,255,255));
CString str;
str = GetItemText(nItem, 0);
if (!str.IsEmpty())
pDC->DrawText(str,&rect,DT_CENTER | DT_SINGLELINE | DT_VCENTER );
// pDC->SelectObject(oldpen);
}
else
{
if (GetItemState(nItem, LVIS_SELECTED))
DrawText(nItem, nSubItem, pDC,m_checktextcolor,m_checkbackcolor, rect,nImage);
//crHighLightText, crHighLight
else
DrawText(nItem, nSubItem, pDC, crText, crBkgnd, rect,nImage);
}
*pResult = CDRF_SKIPDEFAULT; // We've painted everything.
}
}
int CSortListCtrl::AddItemColor(LPCTSTR pszText, COLORREF crText, COLORREF crBak)
{
//insert item at the last
const int iIndex = InsertItem( GetItemCount(), pszText );
ItemData *m_pSortItemData=new ItemData[GetColumns()];
/*
m_pSortItemData[0].crText=crText;
m_pSortItemData[0].crBak=crBak;
*/
SetItemData(iIndex,(DWORD) m_pSortItemData);
//no sort function
return iIndex;
}
int CSortListCtrl::GetColumns()
{
return m_ctlHeader.GetItemCount();
}
BOOL CSortListCtrl::GetSubItemRect(int nItem, int nSubItem, int nArea, CRect &rect)
{
ASSERT(nItem >= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return FALSE;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return FALSE;
BOOL bRC = CListCtrl::GetSubItemRect(nItem, nSubItem, nArea, rect);
// if nSubItem == 0, the rect returned by CListCtrl::GetSubItemRect
// is the entire row, so use left edge of second subitem
if (nSubItem == 0)
{
if (GetColumns() > 1)
{
CRect rect1;
bRC = GetSubItemRect(nItem, 1, LVIR_BOUNDS, rect1);
rect.right = rect1.left;
}
}
return bRC;
}
void CSortListCtrl::DrawText(int nItem, int nSubItem, CDC *pDC, COLORREF crText, COLORREF crBkgnd, CRect &rect,int nImage)
{
ASSERT(pDC);
// GetDrawColors(nItem, nSubItem, crText, crBkgnd);
pDC->FillSolidRect(&rect, crBkgnd);
CString str;
str = GetItemText(nItem, nSubItem);
if (!str.IsEmpty())
{
// get text justification
HDITEM hditem;
hditem.mask = HDI_FORMAT;
m_ctlHeader.GetItem(nSubItem, &hditem);
int nFmt = hditem.fmt & HDF_JUSTIFYMASK;
UINT nFormat = DT_VCENTER | DT_SINGLELINE;
if (nFmt == HDF_CENTER)
nFormat |= DT_CENTER;
else if (nFmt == HDF_LEFT)
nFormat |= DT_LEFT;
else
nFormat |= DT_RIGHT;
if(nImage!=-1)
{
CPoint pt;
pt.x=rect.left+2;
pt.y=rect.top+2;
GetImageList(1)->Draw(pDC,nImage,pt,ILD_TRANSPARENT);
rect.left+=30;
}
else
rect.left+=5;
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(crText);
pDC->SetBkColor(crBkgnd);
pDC->DrawText(str, &rect, nFormat);
}
}
void CSortListCtrl::OnSysColorChange()
{
CListCtrl::OnSysColorChange();
// TODO: Add your message handler code here
crWindow = ::GetSysColor(COLOR_WINDOW);
crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
crHighLight = ::GetSysColor(COLOR_HIGHLIGHT);
crHighLightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
}
void CSortListCtrl::SetItemColor(int nItem, int nSubItem, COLORREF clrText, COLORREF clrBkgnd)
{
ASSERT(nItem >= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return ;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return ;
if (nItem < 0)
return ;
ItemData *pid = (ItemData *) CListCtrl::GetItemData(nItem);
if (pid)
{
(pid->crText)[nSubItem] = (clrText == -1) ? crWindowText : clrText;
(pid->crBak)[nSubItem] = (clrBkgnd == -1) ? crWindow : clrBkgnd;
}
UpdateSubItem(nItem, nSubItem);
}
void CSortListCtrl::UpdateSubItem(int nItem, int nSubItem)
{
ASSERT(nItem >= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return;
CRect rect;
if (nSubItem == -1)
{
GetItemRect(nItem, &rect, LVIR_BOUNDS);
//GetDC()->Draw3dRect(&rect,RGB(255,0,0),RGB(0,255,0));
}
else
{
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);
}
InvalidateRect(&rect);
UpdateWindow();
}
void CSortListCtrl::SetCustomColor(COLORREF textcolor, COLORREF backcolor, COLORREF checkcolumcolor, COLORREF checktextcolor, COLORREF checkbackcolor)
{
m_textcolor=textcolor;
m_backcolor=backcolor;
m_checkcolumcolor=checkcolumcolor;
m_checktextcolor=checktextcolor;
m_checkbackcolor=checkbackcolor;
for(int i=0;i=0;j--)
m_ctlHeader.SetSortArrow(j,1);
}
void CSortListCtrl::SetItemColor(int nItem, COLORREF clrText, COLORREF clrBkgnd)
{
for(int j=0;j= 0);
ASSERT(nItem < GetItemCount());
if ((nItem < 0) || nItem >= GetItemCount())
return ;
ASSERT(nSubItem >= 0);
ASSERT(nSubItem < GetColumns());
if ((nSubItem < 0) || nSubItem >= GetColumns())
return ;
if(nImage>GetImageList(1)->GetImageCount())
return;
if (nItem < 0)
return ;
ItemData *pid = (ItemData *) CListCtrl::GetItemData(nItem);
if (pid)
{
(pid->nImage)[nSubItem] = nImage;
}
UpdateSubItem(nItem, nSubItem);
}
void CSortListCtrl::SetHeadImage(UINT back, UINT asc, UINT dec)
{
// m_ctlHeader.SetImage(back,asc,dec);
}
BOOL CSortListCtrl::SetExcelShow(BOOL bShow)
{
BOOL bOld=m_bExcelShow;
m_bExcelShow=bShow;
Invalidate();
return bOld;
}