Created On:  14 August 2012

Problem:

I am using a ListView control in a .NET Windows Forms application.  How can I programatically sort the data in the ListView by a specific column when that columns header is clicked by the user?

Resolution:

Attached is an example program that demonstrates how to do this in Visual COBOL 2.0.

*-----------------------------------------------------------------------------------*
*                                   TESTLISTSORT                                    *
*                                                                                   *
* This example program demonstrates how to sort the rows in a Windows Forms ListView*
* control by a specific column when that columns header has been clicked. If the    *
* ListView has not been sorted previously then ascending will be used as the sort   *
* sequence otherwise each time a header has been clicked it will be sorted in the   *
* opposite sequence in which it was previously sorted.                              *
*                                                                                   *
* This works by setting the ListViewItemSorter property of the ListView to an in-   *
* stance of a class which implements the ICompare Interface, in this case, ListView *
* ColumnSorter.                                                                     *
*-----------------------------------------------------------------------------------*

class-id testlistsort.Form1 is partial
    inherits type System.Windows.Forms.Form.
working-storage section.
01 lvwColumnSorter type testlistsort.ListViewColumnSorter.
method-id NEW.
procedure division.
   invoke self::InitializeComponent
*> Create an instance of a ListView column sorter and assign it
*> to the ListView control.
   set lvwColumnSorter to new testlistsort.ListViewColumnSorter
   set listView1::ListViewItemSorter to lvwColumnSorter
   goback.
end method.
method-id Form1_Load final private.
01 row type ListViewItem.
procedure division using by value sender as object e as type System.EventArgs.
*> Populate the ListView control with data
   set row to new ListViewItem("John Smith")
   invoke row::SubItems::Add("ABC Software")
   invoke row::SubItems::Add("800-555-1234")
   invoke listView1::Items::Add(row)
   set row to new ListViewItem("Ann Johnson")
   invoke row::SubItems::Add("Z-Systems Data")
   invoke row::SubItems::Add("111-222-3333")
   invoke listView1::Items::Add(row)
   set row to new ListViewItem("Zachary Jones")
   invoke row::SubItems::Add("New Life Systems")
   invoke row::SubItems::Add("555-987-1111")
   invoke listView1::Items::Add(row)
*> sets first row in listview to be selected row as indexes are 0 based.
   set listView1::Items[0]::Selected to true
   goback
end method.
method-id listView1_ColumnClick final private.
procedure division using by value sender as object e as type System.Windows.Forms.ColumnClickEventArgs.
*> Determine if clicked column is already the column that is being sorted.
   if e::Column = lvwColumnSorter::SortColumn
*> Reverse the current sort direction for this column.
      if lvwColumnSorter::Order = type SortOrder::Ascending
         set lvwColumnSorter::Order to type SortOrder::Descending
      else
         set lvwColumnSorter::Order to type SortOrder::Ascending
      end-if
   else
*> Set the column number that is to be sorted; default to ascending.
      set lvwColumnSorter::SortColumn to e::Column
      set lvwColumnSorter::Order to type SortOrder::Ascending
   end-if
*> Perform the sort with these new sort options.
   invoke listView1::Sort
   goback
end method.
method-id button1_Click final private.
procedure division using by value sender as object e as type System.EventArgs.
   invoke self::Close
end method.
end class.

ListViewColumnSorter class:
*>
*> This class is an implementation of the 'IComparer' interface.
*>
$set ilusing"System.Collections"
$set ilusing"System.Windows.Forms"
class-id testlistsort.ListViewColumnSorter implements type IComparer.
working-storage section.
01 ColumnToSort binary-long.
01 OrderOfSort type SortOrder.
01 ObjectCompare type CaseInsensitiveComparer.
property-id SortColumn binary-long.
   getter.
      set property-value to ColumnToSort
   setter.
      set ColumnToSort to property-value
end property.
property-id Order type SortOrder.
   getter.
      set property-value to OrderOfSort
   setter.
      set OrderOfSort to property-value
end property.
method-id New.
local-storage section.
procedure division.
*> Initialize the column to '0'
   set ColumnToSort to 0
*> Initialize the sort order to 'none'
   set OrderOfSort to type SortOrder::None
*> Initialize the CaseInsensitiveComparer object
   set ObjectCompare to new CaseInsensitiveComparer
   goback.
end method.
*> Automatically inserted Methods from System.Collections.IComparer
*> This method is inherited from the IComparer interface.
*> It compares the two objects passed using a case insensitive comparison.
method-id Compare.
01 compareResult binary-long.
01 listViewX type ListViewItem.
01 listViewY type ListViewItem.
procedure division using by value #x as object #y as object returning return-item as binary-long.
*> Cast the objects to be compared to ListViewItem objects
   set listViewX to #x as type ListViewItem
   set listviewY to #y as type ListViewItem
*> Compare the two items
   set compareResult to ObjectCompare::Compare(listviewX::SubItems[ColumnToSort]::Text, listviewY::SubItems[ColumnToSort]::Text)
*> Calculate correct return value based on object comparison
   if OrderOfSort = type SortOrder::Ascending
*> Ascending sort is selected, return normal result of compare operation
      set return-item to compareResult
   else
      if OrderOfSort = type SortOrder::Descending
*> Descending sort is selected, return negative result of compare operation
         compute return-item = compareResult * -1
      else
*> Return '0' to indicate they are equal
         set return-item to 0
      end-if
   end-if
   goback
end method.
end class.