m_ctlList.SetExtendedStyle( LVS_EX_HEADERDRAGDROP );
...to the dialog initialization section.
#pragma once
// CListCtrlEx.h
//--------------- these two structs are used to define the default column layout
typedef enum {
SORT_None = 0,
SORT_AZ = 1,
SORT_ZA = -1,
} SortOrder;
typedef struct {
int nId;
int nClmNum;
int nWidth; // pixels
SortOrder eSortOrder;
LPSTR pszName;
} ListClmInfo;
#define CNUM_MaxListClms 20
//------------------------------------------- the class object extends CListCtrl
class CListCtrlEx : public CListCtrl
{
DECLARE_DYNAMIC(CListCtrlEx)
public:
CListCtrlEx();
virtual ~CListCtrlEx();
void SetupClms( ListClmInfo* parLCI, BOOL fSetFromSaved=FALSE, LPCSTR szKey=0 );
void SaveClmInfo( LPCSTR sKey );
void UseSavedClmInfo( LPCSTR sKey );
void EraseList();
BOOL PutItem(int nItem,int nSubItem, int nVal, BOOL fAddRow=FALSE );
BOOL PutItem(int nItem,int nSubItem, LPCTSTR sItem, BOOL fAddRow=FALSE );
private:
void SaveClmHdrInfo( LPCSTR sKey, int nClms, LPINT paiIndexes, LPINT paiWidths );
BOOL ReadClmHdrInfo( LPCSTR sKey, int nClms, LPINT paiIndexes, LPINT paiWidths );
protected:
DECLARE_MESSAGE_MAP()
};
// ListCtrlEx.cpp : implementation file
//
#include "stdafx.h"
#include "ListClms.h"
#include "ListCtrlEx.h"
IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)
//----------------------------------- nothing special for the ctor or dtor
CListCtrlEx::CListCtrlEx(){}
CListCtrlEx::~CListCtrlEx(){}
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Save and restore user prefs on column positions and widths
// to the registry key for this program
//
void CListCtrlEx::SaveClmInfo( LPCSTR szKey )
{
HDITEM rHdr;
int nClmCnt= GetHeaderCtrl()->GetItemCount();
int anClmIdxs[ CNUM_MaxListClms ];
int anClmWidths[ CNUM_MaxListClms ];
for (int j=0; j< nClmCnt; j++ ) {
rHdr.mask= HDI_WIDTH | HDI_ORDER;
GetHeaderCtrl()->GetItem(j, &rHdr);
anClmIdxs[ j ] = rHdr.iOrder;
anClmWidths[ j ] = rHdr.cxy;
}
SaveClmHdrInfo( szKey, nClmCnt, anClmIdxs, anClmWidths );
}
void CListCtrlEx::UseSavedClmInfo( LPCSTR szKey )
{
HDITEM rHdr;
int nClmCnt= GetHeaderCtrl()->GetItemCount();
int anClmIdxs[ CNUM_MaxListClms ];
int anClmWidths[ CNUM_MaxListClms ];
if ( ReadClmHdrInfo( szKey, nClmCnt, anClmIdxs, anClmWidths ) ) {
for (int j=0; j< nClmCnt; j++ ) {
if ( anClmWidths[j] == 0 ) anClmWidths[j]= 5;
rHdr.mask= HDI_WIDTH | HDI_ORDER;
rHdr.iOrder= anClmIdxs[ j ];
rHdr.cxy = anClmWidths[ j ];
GetHeaderCtrl()->SetItem(j, &rHdr);
}
}
}
//-------------------------------------------
// private functions used by the above to get data from registry
//
void CListCtrlEx::SaveClmHdrInfo( LPCSTR szKey, int nClms, LPINT paiIndexes, LPINT paiWidths )
{
CString sAll="";
CString sOne;
if (szKey==0) szKey= "ListClms";
for (int j=0; j< nClms; j++ ) {
sOne.Format("%d,", *paiIndexes++ );
sAll += sOne;
sOne.Format("%d, ", *paiWidths++ );
sAll += sOne;
}
AfxGetApp()->WriteProfileString("Prefs", sKey, sAll);
}
BOOL CListCtrlEx::ReadClmHdrInfo( LPCSTR szKey, int nClms, LPINT paiIndexes, LPINT paiWidths )
{
if (szKey==0) szKey= "ListClms";
CString sAll= AfxGetApp()->GetProfileString("Prefs", szKey );
if (sAll.IsEmpty()) {
return FALSE;
}
sAll += " ";
int nCurPos=0;
for (int j=0; j<nClms; j++ ) {
*paiIndexes++ = atoi( sAll.Mid( nCurPos ) );
nCurPos= sAll.Find( ',', nCurPos ) +1; // get past the comma
if ( nCurPos <= 0) return( FALSE );
*paiWidths++ = atoi( sAll.Mid( nCurPos ) );
nCurPos= sAll.Find( ',', nCurPos ) +1; // get past the comma
if ( nCurPos <= 0) return( FALSE );
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// Generic utilities simplify use of ListView control
//------------------------------------------------------------------
// Set up the columns -- given title, order and widths from a setup array
//
void CListCtrlEx::SetupClms( ListClmInfo* parLCI, BOOL fSetFromSaved /*=FALSE*/, LPCSTR szKey /*=0*/ )
{
LV_COLUMN rLVC;
EraseList(); // delete all columns
while (parLCI->nId != -1 ) {
rLVC.mask= LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
rLVC.cx= parLCI->nWidth; // in pixels
rLVC.iSubItem= parLCI->nId; //
rLVC.pszText= parLCI->pszName;
rLVC.fmt= LVCFMT_LEFT;
int nRet= InsertColumn( parLCI->nClmNum, &rLVC );
parLCI++;
}
if ( fSetFromSaved ) {
UseSavedClmInfo( szKey );
}
}
void CListCtrlEx::EraseList()
{
DeleteAllItems();
while( DeleteColumn(0) )
;
UpdateWindow();
}
//--------------------------------------
// Used in populating the list -- overloads given for an int or a string value
// Use fAddRow=TRUE when populating the first column in the new row
//
BOOL CListCtrlEx::PutItem(int nItem,int nSubItem, int nVal, BOOL fAddRow /*=FALSE*/ )
{
CString str; str.Format("%d",nVal);
return( PutItem(nItem, nSubItem, str) );
}
BOOL CListCtrlEx::PutItem(int nItem,int nSubItem, LPCTSTR sItem, BOOL fAddRow /*=FALSE*/)
{
LV_ITEM lvItm;
lvItm.mask= LVIF_TEXT;
lvItm.iItem= nItem;
lvItm.iSubItem= nSubItem;
lvItm.pszText= (LPSTR)sItem;
if( nSubItem == 0 && fAddRow ) {
return InsertItem( &lvItm );
}
return SetItem( &lvItm );
}
ListClmInfo arClmsList[]= {
{ 0, 0, 130, SORT_None, "Name" },
{ 1, 1, 90, SORT_None, "Created" },
{ 2, 2, 60, SORT_None, "Your Ref" },
{ 3, 3, 80, SORT_None, "Type" },
{ 4, 4, 40, SORT_None, "Status" },
{ 5, 5, 80, SORT_None, "Comment" },
{ -1, 0, 0, SORT_None, 0 }
};
...
m_ctlList.SetExtendedStyle( LVS_EX_HEADERDRAGDROP );
m_ctlList.SetupClms( arClmsList, FALSE );
To use this, just use the dialog editor to create a ListView control object. Set its properties to Report View. Right-click it, choose Add Variable..., and create a control-type variable (e.g., m_ctlList). Then in the dialog's header, change the line:
//CListCtrl m_ctlList; // put here by the class wizard
CListCtrlEx m_ctlList; // <<<==== Change to this
You can create and populate your lists in whatever way you normally do, and the save/restore functions will work as described.
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)