Sorting Items Listed in the List Control

In the applications like Find, Explorer, etc. where data is displayed in the list control if we click on a column the data listed in the control gets sorted in either ascending or descending order as per the values in the column. If we use the list control in our application we won’t get such a facility from MFC. We need to write code for this ourselves. Let us see how to do this.

Create an SDI application ‘sortlist’ with ‘Doc/View’ support. Select CListView as the base class of the view class. We would display the files of the root directory, their size and last access date. For this insert the columns and data in the list view control in OnInitialUpdate( ) function as given below:

void CSortlistView::OnInitialUpdate( )

{

CListView::OnInitialUpdate( ) ;

GetListCtrl( ).InsertColumn ( 0,"Filename",LVCFMT_LEFT, 100);

GetListCtrl( ).InsertColumn ( 1, "Size", LVCFMT_LEFT, 100 ) ;

GetListCtrl( ).InsertColumn ( 2, "Last Access Date", LVCFMT_LEFT, 150 ) ;

CFileFind file ;

CString name ;

int count = 0 ;

file.FindFile ( "C:\\*.*" ) ;

BOOL found = TRUE ;

while ( found )

{

found = file.FindNextFile( ) ;

if ( file.IsDots( ) )

continue ;

if ( file.IsDirectory( ) )

continue ;

name = file.GetFileName( ) ;

iteminfo *it = new iteminfo ;

it -> filename = name ;

it -> filesize = file.GetLength( ) ;

file.GetLastAccessTime ( it -> filedate ) ;

LV_ITEM lvi ;

lvi.mask = LVIF_TEXT | LVIF_PARAM ;

lvi.iItem = count ;

lvi.iSubItem = 0 ;

lvi.iImage = 0 ;

lvi.pszText = ( LPSTR ) name.operator LPCTSTR( ) ;

lvi.lParam = ( LPARAM ) it ;

GetListCtrl( ).InsertItem ( &lvi ) ;

count++ ;

}

}

Here, we have inserted three columns ‘Filename’, ‘Size’ and ‘Last Access Date’ by calling the function CListCtrl::InsertColumn( ). GetListCtrl( ) function returns a reference to the list control associated with the view using which we have called the member functions of CListCtrl. We have browsed the files of the root directory by calling member functions of CFileFind class. We have allocated memory in a pointer to iteminfo structure and stored the details of the file obtained in a structure iteminfo. iteminfo is a user defined structure which is given below:

struct iteminfo

{

CString filename ;

int filesize ;

CTime filedate ;

} ;

To insert the filename, size and date in the list control we have used LV_ITEM structure. The data is added to the list control using CListCtrl::InsertItem( ) function.

There are two ways in which we can display the data in list control so as to make the comparison of items possible. First way is to maintain a copy of each item’s data and assign the data to lParam parameter of InsertItem( ) function. However, if this way is used we will have to deallocate the memory in which the data is stored. The second way is to provide data to list control in response to LVN_GETDISPINFO notification. LVN_GETDISPINFO is sent to the parent window to provide information needed to display or sort a list view item. The OnGetdispinfo( ) function looks as shown below:

void CSortlistView::OnGetdispinfo ( NMHDR* pNMHDR, LRESULT* pResult )

{

LV_DISPINFO* pDispInfo = ( LV_DISPINFO* ) pNMHDR ;

CString string ;

if ( pDispInfo -> item.mask & LVIF_TEXT )

{

iteminfo *pitem = ( iteminfo * ) pDispInfo -> item.lParam ;

switch ( pDispInfo -> item.iSubItem )

{

case 0 : // File Name

strcpy ( pDispInfo -> item.pszText, pitem->filename ) ;

break ;

case 1 : // File Size

string.Format ( "%u", pitem -> filesize ) ;

strcpy ( pDispInfo -> item.pszText, string ) ;

break ;

case 2 : // Date and Time

CTime time ( pitem -> filedate ) ;

string.Format ( "%d/%0.2d/%0.2d",

time.GetMonth( ), time.GetDay( ),

time.GetYear( ) % 100 ) ;

strcpy ( pDispInfo -> item.pszText, string ) ;

break ;

}

}

*pResult = 0 ;

}

pNMHDR points to the LV_DISPINFO structure The structure’s item.lParam element contains the address of the iteminfo structure for the item in question and item.iSubItem element contains index of the subitem.

Add a handler for LVN_COLUMNCLICK notification and add the following code to it.

void CSortlistView::OnColumnclick ( NMHDR* pNMHDR, LRESULT* pResult )

{

NM_LISTVIEW* pNMListView = ( NM_LISTVIEW* ) pNMHDR ;

if ( m_flag == false )

m_flag = true ;

else

m_flag = false ;

GetListCtrl( ).SortItems ( compare, pNMListView -> iSubItem ) ;

*pResult = 0;

}

OnColumnclick( ) handler would get called whenever the user clicks the column. Since we intend to sort the contents in ascending order if clicked for the first time and then in ascending order if clicked again we have kept a flag to ascertain whether the sorting should take place in ascending or descending order. Add m_flag as a static data member of CSortlistView of type bool. Add the following statement in ‘sortlistView.cpp’.

bool CSortlistView::m_flag = true ;

Next, we have called CListCtrl::SortItems( ) function. The first parameter passed to SortItems( ) is address of a function that performs the comparison of arbitrarily selected items. It is a callback function which the control’s built in sorting routine calls. The second parameter is the zero based index of column on which user has clicked.

Declare the compare( ) function in ‘sortlistView.cpp’ as given below:

static int CALLBACK compare ( LPARAM lparam1, LPARAM lparam2, LPARAM lparamsort ) ;

The function compare( ) is given below:

static int CALLBACK compare ( LPARAM lparam1, LPARAM lparam2, LPARAM lparamsort )

{

iteminfo *item1 = ( iteminfo * ) lparam1 ;

iteminfo *item2 = ( iteminfo * ) lparam2 ;

iteminfo *item = new iteminfo ;

if ( CSortlistView::m_flag )

{

item = item2 ;

item2 = item1 ;

item1 = item ;

}

int res = 0 ;

switch ( lparamsort )

{

case 0 : // file name

res = item1 -> filename.CompareNoCase ( item2 -> filename ) ;

break ;

case 1 : // size

res = item1 -> filesize - item2 -> filesize ;

break ;

case 2 : // date

res = ::CompareFileTime ( ( FILETIME* ) &item1 -> filedate,

FILETIME* ) &item2 -> filedate ) ;

break ;

}

return res ;

}

The lparam1 and lparam2 parameters specify the item data for the two items being compared. The lparamsort parameter specifies the zero based index of the column that has been clicked. We have first checked the value of m_flag and swapped the items if it is true. The comparison function must return a negative value if the first item should precede the second, a positive value if the first item should follow the second, or zero if the two items are equivalent.

The memory allocated for each item is deallocated in OnDestroy( ) function as given below:

void CSortlistView::OnDestroy( )

{

int count = GetListCtrl( ).GetItemCount( ) ;

for ( int i = 0 ; i <>

delete ( iteminfo* ) GetListCtrl( ).GetItemData ( i ) ;

CListView::OnDestroy( ) ;

}

GetItemData( ) function returns the value stored in lParam element (pointer to iteminfo structure) of LV_ITEM structure.

Run the program and click on the ‘Filename’ column. You will see the items sorted as shown in the following figure:

Click here to Download Project

No comments: