Free Information Technology Magazines and eBooks

Wednesday, May 13, 2009

VB.NET: How to sort listview by clicked column

One of my favorite .NET control is the ListView. It is very useful for displaying multiple records on a spreadsheet format. Column sorting is one the feature of spreadsheet applications that most of end-users are used to. Unfortunately, this is not readily available as property in VB.NET. The ListView Sorting property only sorts items and not the sub-items so it can't be use if we want to allow the user to sort your list by any clicked column.


To make your ListView application capable of column sorting, follow these steps:

1. On your existing project, add a new class with following code:



Public Class clsListviewSorter ' Implements a comparer
Implements IComparer
Private m_ColumnNumber As Integer
Private m_SortOrder As SortOrder
Public Sub New(ByVal column_number As Integer, ByVal sort_order As SortOrder)
m_ColumnNumber = column_number
m_SortOrder = sort_order
End Sub
' Compare the items in the appropriate column
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
Dim item_x As ListViewItem = DirectCast(x, ListViewItem)
Dim item_y As ListViewItem = DirectCast(y, ListViewItem)
' Get the sub-item values.
Dim string_x As String
If item_x.SubItems.Count <= m_ColumnNumber Then
string_x = ""
Else
string_x = item_x.SubItems(m_ColumnNumber).Text
End If
Dim string_y As String
If item_y.SubItems.Count <= m_ColumnNumber Then
string_y = ""
Else
string_y = item_y.SubItems(m_ColumnNumber).Text
End If
' Compare them.
If m_SortOrder = SortOrder.Ascending Then
If IsNumeric(string_x) And IsNumeric(string_y) Then
Return Val(string_x).CompareTo(Val(string_y))
ElseIf IsDate(string_x) And IsDate(string_y) Then
Return DateTime.Parse(string_x).CompareTo(DateTime.Parse(string_y))
Else
Return String.Compare(string_x, string_y)
End If
Else
If IsNumeric(string_x) And IsNumeric(string_y) Then
Return Val(string_y).CompareTo(Val(string_x))
ElseIf IsDate(string_x) And IsDate(string_y) Then
Return DateTime.Parse(string_y).CompareTo(DateTime.Parse(string_x))
Else
Return String.Compare(string_y, string_x)
End If
End If
End Function
End Class

2. Declare a private variable on the form where the listview you want to be sorted is located.


Private m_SortingColumn As ColumnHeader


3. Then on the listview's ColumnClick event, add the following code


Private Sub ListView1_ColumnClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles ListView1.ColumnClick
' Get the new sorting column.
Dim new_sorting_column As ColumnHeader = ListView1.Columns(e.Column)
' Figure out the new sorting order.
Dim sort_order As System.Windows.Forms.SortOrder
If m_SortingColumn Is Nothing Then
' New column. Sort ascending.
sort_order = SortOrder.Ascending
Else ' See if this is the same column.
If new_sorting_column.Equals(m_SortingColumn) Then
' Same column. Switch the sort order.
If m_SortingColumn.Text.StartsWith("> ") Then
sort_order = SortOrder.Descending
Else
sort_order = SortOrder.Ascending
End If
Else
' New column. Sort ascending.
sort_order = SortOrder.Ascending
End If
' Remove the old sort indicator.
m_SortingColumn.Text = m_SortingColumn.Text.Substring(2)
End If
' Display the new sort order.
m_SortingColumn = new_sorting_column
If sort_order = SortOrder.Ascending Then
m_SortingColumn.Text = "> " & m_SortingColumn.Text
Else
m_SortingColumn.Text = "< " & m_SortingColumn.Text
End If
' Create a comparer.
ListView1.ListViewItemSorter = New clsListviewSorter(e.Column, sort_order)
' Sort.
ListView1.Sort()
End Sub



There you have it, test your listview application and it should be sorting by the column clicked.

Download the sample VB.NET 2008 project

For more VB.NET tips and tricks, subscribe now


33 comments:

Anonymous said...

Good stuff. Works like a charm.

Keep up the good work mate.

Anonymous said...

I almost lost all my hair pondering over this problem. All solutions were either very slow, or didn't work properly, or (mostly) both.

And your solution works with alphabet values AND numerical ones (Wow !!!)

Many thanks, mate !

Anonymous said...

Works great - just what I needed and concise. Also, I like that it does not error out when sorting on a date column and a record has no date in it. Very good. Thanks!

Murf

Wilmat said...

Yes, this works well. However, I made a small change.

Even though we can sort any column, listview will only search on the first column. To get around this I made the first column zero width, so it doesn't display. Then in the listview column click event I added code at the end of the procedure to copy the contents of the selected column to the first column.

When I enter some characters, the listview looks like it is searching on the selected column. In actual fact it is sorting on the hidden first column, which has the same contents as the selected column.

Joel said...

I don't know why everyone gives such great comments. Paste that code into a new class and you get 12 compile errors. I'm sure its great code but please try and preserve line spacing, and there seems to be missing import statements.

Fryan Valdez said...

@joel - i added it modified this post to help you more.

Anonymous said...

Thanks alot for the code sample. I had a very similar version using ICompare, but it only sorted text, and didn't properly sort positive and negative numbers. Yours works great.

Anonymous said...

Hi this is not working on VS 2008
In the class im getting error on this line "Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare"


ERROR : "Error 3 Interface 'System.Collections.IComparer' is not implemented by this class. D:\=VB Projects=\LoadFilesManager\LoadFilesManager\clsListViewSorter.vb 10 89 LoadFilesManager
"

Please any help would be great.

Thx Mike

Fryan Valdez said...

@mike
--your missing the following line

Implements System.Collections.IComparer.Compare

I already adjusted the code above to make it copy and paste friendly

thanks :)

michael said...

I'm also getting some errors in VS2008

__________________
Error 1 Class 'clsListviewSorter' must implement 'Function Compare(x As Object, y As Object) As Integer' for interface 'System.Collections.IComparer'.
__________________
Error 2 Statement is not valid inside a method.
__________________

Any help would be kind of you.
Thanks

Fryan Valdez said...

@michael

I included a sample VB.NET 2008 project for your reference.

Thanks!

Anonymous said...

Finally a tutorial on sorting the listview component that works! Thank you :)

Im using VS 2008 and it works like a charm, keep up the good work.

michael said...

Fryan; Thank you for the project file.

Anonymous said...

I'm using WinXP Pro and VS2008 VB.NET

Follow the 3 steps instruction, copy and paste, it works perfectly. Good job!

Thank you.

Anonymous said...

Great work thanks. Worked first go.

John Sawyer said...

Thanks for this - grand stuff

Ivancxo said...

Hey dude, Thanks a lot.
I'm using the code in a project i'm workin on, and i had this problem to solve.

Anonymous said...

tnx fv

Anonymous said...

Wow - works as advertised!

Thank you!

Anonymous said...

Thank u dude!


>jboy

Anonymous said...

Hey there,
thank you very much for this piece of code. It works like a charm.

I have only one problem... when I refresh the data, in the listview I just sorted, my application freezes.

You have any idea how I could solve this problem? Maybe reset the sorting order to nothing?

Sean said...

Works great!!! Thanks!

Anonymous said...

Great work, my days work has been reduced to few mins (to copy/paste and do few modifications as per my need). Keep up the great work and thanks for sharing the code.

Anonymous said...

Excellent job my friend...

Anonymous said...

ok...that was way to easy! thanks heraps for posting this code :)

Anonymous said...

this is not simple enough?

If Listview1.Sorting = Windows.Forms.SortOrder.Ascending Then
Listview1.Sorting = Windows.Forms.SortOrder.Descending
Else
Listview1.Sorting = Windows.Forms.SortOrder.Ascending
End If

Listview1.Sort()

Anonymous said...

thanks.. it's works great

kyrone said...

Very much thanks bro..It works great. Hoping for more tips and tricks for vb.net. Your helping bro..Just keep it up and more blessings to come.

Anonymous said...

What's nice about the class is you can modify it to sort by more then just the data in the column you are clicking. I have a column of scores and one with user ages, now instead of just sorting by scores it also compares the age so when scores are tied the younger age trumps the elders, you just can't do stuff like that using the normal LV sort. Thanks!!!

Louis said...

Thanks man. Works very good..

bhfan said...

BIG Thanks, I really looking for this for a loooong time :)

Anonymous said...

If you run into trouble regarding missing subitems when you switch the contents of the listview.. add this to the bottom of the column-click-event-code:

ListView1.ListViewItemSorter = nothing

(right after listview1.sort())

Bogdan said...

Thank you so much, your code was very helpful!