Advanced User Interface Controls and Components
January 12, 2010
The IntegralUI ListView control supports predefined sorting based on some predefined types. This is determined by ComparerObjectType property which can have one of these values: Double, Integer and String. Although, most of sorting would satisfy comparing basic values, in some cases a sort operation using custom values is needed. In order to do that, we need to create a class that implements the IComparer interface and set the ListItemSorter property to an object of that class.
In the following example we will show how to create a class used for sorting DateTime values. At first initialize the ListView with some data:
private void btnInit_Click(object sender, EventArgs e)
{
int j = 0;
// Add two columns
LidorSystems.IntegralUI.Lists.ListViewColumn column = null;
for (j = 1; j <= 2; j++)
{
column = new LidorSystems.IntegralUI.Lists.ListViewColumn();
column.HeaderText = String.Format("Header {0}", j.ToString());
column.FooterText = String.Format("Footer {0}", j.ToString());
column.Width = 100;
this.listView1.Columns.Add(column);
}
// Create a list of items each containg two subitems
LidorSystems.IntegralUI.Lists.ListViewItem item = null;
LidorSystems.IntegralUI.Lists.ListViewSubItem subItem = null;
// A variable used to create random dates
DateTime sampleDate = new DateTime(2009, 1, 1);
Random gen = new Random((int)DateTime.Now.Ticks);
for (int i = 1; i <= 50; i++)
{
item = new LidorSystems.IntegralUI.Lists.ListViewItem();
for (j = 0; j < this.listView1.Columns.Count; j++)
{
subItem = new LidorSystems.IntegralUI.Lists.ListViewSubItem();
// Add DateTime only to the second column
if (j == 1)
{
sampleDate = sampleDate.AddDays(gen.Next(27));
subItem.Text = sampleDate.ToString("dd MMM yyyy");
subItem.SortTag = sampleDate;
}
else
subItem.Text = String.Format("Item {0}{1}", i.ToString(), j.ToString());
item.SubItems.Add(subItem);
}
this.listView1.Items.Add(item);
}
}
Private Sub btnInit_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnInit.Click
Dim j As Integer = 0
' Add two columns
Dim column As LidorSystems.IntegralUI.Lists.ListViewColumn = Nothing
For j = 1 To 2
column = New LidorSystems.IntegralUI.Lists.ListViewColumn()
column.HeaderText = [String].Format("Header {0}", j.ToString())
column.FooterText = [String].Format("Footer {0}", j.ToString())
column.Width = 100
Me.listView1.Columns.Add(column)
Next
' Create a list of items each containg two subitems
Dim item As LidorSystems.IntegralUI.Lists.ListViewItem = Nothing
Dim subItem As LidorSystems.IntegralUI.Lists.ListViewSubItem = Nothing
' A variable used to create random dates
Dim sampleDate As New DateTime(2009, 1, 1)
Dim gen As New Random()
For i As Integer = 1 To 50
item = New LidorSystems.IntegralUI.Lists.ListViewItem()
For j = 0 To Me.listView1.Columns.Count - 1
subItem = New LidorSystems.IntegralUI.Lists.ListViewSubItem()
' Add DateTime only to the second column
If j = 1 Then
sampleDate = sampleDate.AddDays(gen.[Next](27))
subItem.Text = sampleDate.ToString("dd MMM yyyy")
subItem.SortTag = sampleDate
Else
subItem.Text = [String].Format("Item {0}{1}", i.ToString(), j.ToString())
End If
item.SubItems.Add(subItem)
Next
Me.listView1.Items.Add(item)
Next
End Sub
One way to trigger sort operation is by handling the ColumnClick event and change the current sort order:
private void listView1_ColumnClick(object sender, LidorSystems.IntegralUI.ObjectClickEventArgs e)
{
if (e.Object is LidorSystems.IntegralUI.Lists.ListViewColumn)
{
LidorSystems.IntegralUI.Lists.ListViewColumn column = (LidorSystems.IntegralUI.Lists.ListViewColumn)e.Object;
// Start the Sort operation only when second column is clicked
if (column.Index == 1)
{
switch (this.listView1.Sorting)
{
case SortOrder.Ascending:
this.listView1.Sorting = SortOrder.Descending;
break;
case SortOrder.Descending:
this.listView1.Sorting = SortOrder.Ascending;
break;
default:
this.listView1.Sorting = SortOrder.Descending;
break;
// Apply the custom sort operation
this.listView1.ListItemSorter = new DateTimeComparer(this.listView1.Sorting, column.Index);
}
}
}
Private Sub listView1_ColumnClick(ByVal sender As Object, ByVal e As LidorSystems.IntegralUI.ObjectClickEventArgs) Handles listView1.ColumnClick
If TypeOf e.[Object] Is LidorSystems.IntegralUI.Lists.ListViewColumn Then
Dim column As LidorSystems.IntegralUI.Lists.ListViewColumn = DirectCast(e.[Object], LidorSystems.IntegralUI.Lists.ListViewColumn)
' Start the Sort operation only when second column is clicked
If column.Index = 1 Then
Select Case Me.listView1.Sorting
Case SortOrder.Ascending
Me.listView1.Sorting = SortOrder.Descending
Exit Select
Case SortOrder.Descending
Me.listView1.Sorting = SortOrder.Ascending
Exit Select
Case Else
Me.listView1.Sorting = SortOrder.Descending
Exit Select
End Select
' Apply the custom sort operation
Me.listView1.ListItemSorter = New DateTimeComparer(Me.listView1.Sorting, column.Index)
End If
End If
End Sub
A class which will compare DateTime values is presented in the code below.
public class DateTimeComparer : System.Collections.IComparer
{
private System.Windows.Forms.SortOrder sortType = System.Windows.Forms.SortOrder.Ascending;
private int col = -1;
public DateTimeComparer()
{
}
public DateTimeComparer(System.Windows.Forms.SortOrder order, int colIndex)
{
sortType = order;
col = colIndex;
}
public int Compare(object x, object y)
{
// Switch the objects if the sort order is Descending
if (sortType == System.Windows.Forms.SortOrder.Descending)
{
object temp = x;
x = y;
y = temp;
}
object firstDate = null;
object secondDate = null;
if (col >= 0)
{
if (col < (x as LidorSystems.IntegralUI.Lists.ListViewItem).SubItems.Count)
firstDate = (x as LidorSystems.IntegralUI.Lists.ListViewItem).SubItems[col].SortTag;
if (col < (y as LidorSystems.IntegralUI.Lists.ListViewItem).SubItems.Count)
secondDate = (y as LidorSystems.IntegralUI.Lists.ListViewItem).SubItems[col].SortTag;
}
// Check objects are not empty
if (firstDate != null && secondDate != null)
{
// Compare object values only if they contain DateTime value
if (firstDate.GetType() == typeof(DateTime) && secondDate.GetType() == typeof(DateTime))
return DateTime.Compare((DateTime)firstDate, (DateTime)secondDate);
}
return 0;
}
}
Public Class DateTimeComparer
Implements System.Collections.IComparer
Private sortType As System.Windows.Forms.SortOrder = System.Windows.Forms.SortOrder.Ascending
Private col As Integer = -1
Public Sub New()
End Sub
Public Sub New(ByVal order As System.Windows.Forms.SortOrder, ByVal colIndex As Integer)
sortType = order
col = colIndex
End Sub
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
' Switch the objects if the sort order is Descending
If sortType = System.Windows.Forms.SortOrder.Descending Then
Dim temp As Object = x
x = y
y = temp
End If
Dim firstDate As Object = Nothing
Dim secondDate As Object = Nothing
If col >= 0 Then
If col < TryCast(x, LidorSystems.IntegralUI.Lists.ListViewItem).SubItems.Count Then
firstDate = TryCast(x, LidorSystems.IntegralUI.Lists.ListViewItem).SubItems(col).SortTag
End If
If col < TryCast(y, LidorSystems.IntegralUI.Lists.ListViewItem).SubItems.Count Then
secondDate = TryCast(y, LidorSystems.IntegralUI.Lists.ListViewItem).SubItems(col).SortTag
End If
End If
' Check objects are not empty
If firstDate IsNot Nothing AndAlso secondDate IsNot Nothing Then
' Compare object values only if they contain DateTime value
If firstDate.[GetType]() Is GetType(DateTime) AndAlso secondDate.[GetType]() Is GetType(DateTime) Then
Return DateTime.Compare(DirectCast(firstDate, DateTime), DirectCast(secondDate, DateTime))
End If
End If
Return 0
End Function
End Class
A sample project in C# and VB showing how to create custom sort operation in ListView is available for download from here: ListView with Custom Sort Operation