Simple Listview sorting by columns

  • (2 Pages)
  • +
  • 1
  • 2

17 Replies - 35630 Views - Last Post: 23 February 2019 - 05:41 PM Rate Topic: ***** 2 Votes

#1 JohnESP   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 24
  • Joined: 09-February 15

Simple Listview sorting by columns

Posted 21 February 2015 - 05:31 PM

  Private Sub Listview1_ColumnClick(sender As System.Object, e As System.Windows.Forms.ColumnClickEventArgs) Handles Listview1.ColumnClick
        If Listview1.Sorting = SortOrder.Descending Then
            Listview1.Sorting = SortOrder.Ascending
        Else
            Listview1.Sorting = SortOrder.Descending
        End If
    End Sub


The above code, when Any Listview1 column is clicked, will sort the First column and put the strings in Ascending or Descending alphabetical order.

I'm searching for a way to sort the other two of my three columns when they are clicked.
As of now, when they are clicked, they simply sort the first column.

Thanks for any enlightenment on this topic!

Is This A Good Question/Topic? 0
  • +

Replies To: Simple Listview sorting by columns

#2 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 05:45 PM

There is a full example at the docs

ListView.ColumnClick :MSDN

You basically need to implement an IComparer and use e.Column to determine which column needs sorting.
Was This Post Helpful? 1
  • +
  • -

#3 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 05:55 PM

Actually, I've just created the example and it isn't quite complete. It sorts in ascending order, but doesn't toggle to descending. I'm sure there is an example out there that does this. The example does provide a lot of the essential information though.

It shouldn't be too tricky to modify. Probably just needs to store the clicked column index as a private variable.

Added: An example here

This post has been edited by andrewsw: 21 February 2015 - 05:59 PM

Was This Post Helpful? 0
  • +
  • -

#4 JohnESP   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 24
  • Joined: 09-February 15

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 06:16 PM

That was very easy given your helpful information!

Thanks!
Was This Post Helpful? 0
  • +
  • -

#5 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 06:19 PM

Yes, I've merged the two examples together and they work to sort in ascending, then descending, order, whatever column is clicked.

Attached Image

Form1
Public Class Form1
    Private listView1 As ListView
    Private sortCol As Integer = 0
    ' The column currently used for sorting.
    Private m_SortingColumn As ColumnHeader

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        ' Create ListView items to add to the control. 
        Dim listViewItem1 As New ListViewItem(New String() {"Banana", "a", "b", "c"}, -1, Color.Empty, Color.Yellow, Nothing)
        Dim listViewItem2 As New ListViewItem(New String() {"Cherry", "v", "g", "t"}, -1, Color.Empty, Color.Red, New Font("Microsoft Sans Serif", 8.25F, FontStyle.Regular, GraphicsUnit.Point, CType(0, System.Byte)))
        Dim listViewItem3 As New ListViewItem(New String() {"Apple", "h", "j", "n"}, -1, Color.Empty, Color.Lime, Nothing)
        Dim listViewItem4 As New ListViewItem(New String() {"Pear", "y", "u", "i"}, -1, Color.Empty, Color.FromArgb(CType(192, System.Byte), CType(128, System.Byte), CType(156, System.Byte)), Nothing)

        'Initialize the ListView control and add columns to it. 
        Me.listView1 = New ListView

        ' Set the initial sorting type for the ListView. 
        Me.listView1.Sorting = SortOrder.None
        ' Disable automatic sorting to enable manual sorting. 
        Me.listView1.View = View.Details
        ' Add columns and set their text. 
        Me.listView1.Columns.Add(New ColumnHeader)
        Me.listView1.Columns(0).Text = "Column 1"
        Me.listView1.Columns(0).Width = 100
        listView1.Columns.Add(New ColumnHeader)
        listView1.Columns(1).Text = "Column 2"
        listView1.Columns.Add(New ColumnHeader)
        listView1.Columns(2).Text = "Column 3"
        listView1.Columns.Add(New ColumnHeader)
        listView1.Columns(3).Text = "Column 4"
        ' Suspend control logic until form is done configuring form. 
        Me.SuspendLayout()
        ' Add Items to the ListView control. 
        Me.listView1.Items.AddRange(New ListViewItem() {listViewItem1, listViewItem2, listViewItem3, listViewItem4})
        ' Set the location and size of the ListView control. 
        Me.listView1.Location = New Point(10, 10)
        Me.listView1.Name = "listView1"
        Me.listView1.Size = New Size(300, 100)
        Me.listView1.TabIndex = 0
        ' Enable editing of the items in the ListView. 
        Me.listView1.LabelEdit = True
        ' Connect the ListView.ColumnClick event to the ColumnClick event handler. 
        AddHandler Me.listView1.ColumnClick, AddressOf ColumnClick

        ' Initialize the form. 
        Me.ClientSize = New Size(400, 400)
        Me.Controls.AddRange(New Control() {Me.listView1})
        Me.Name = "ListViewSortForm"
        Me.Text = "Sorted ListView Control"
        ' Resume layout of the form. 
        Me.ResumeLayout(False)
    End Sub

    Private Sub ColumnClick(sender As Object, e As ColumnClickEventArgs)
        ' 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 ListViewItemComparer(e.Column, sort_order)

        ' Sort.
        listView1.Sort()
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub
End Class


ListViewItemComparer.vb
Class ListViewItemComparer
    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
    ' for objects x and y.
    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


Seems unnecessarily complicated to me though, it shouldn't be that difficult. After all, we are only toggling a SortOrder between Ascending and Descending. Ah well..

View PostJohnESP, on 22 February 2015 - 01:16 AM, said:

That was very easy given your helpful information!

Thanks!

No problem.

Does this mean that you managed to toggle between Ascending and Descending before I did !! ;)
Was This Post Helpful? 0
  • +
  • -

#6 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 06:43 PM

I managed to sort in asc/desc in a much simpler fashion. (The example I found above is more detailed though, as it includes the facility to sort by numbers and dates, and sub-items.)

Pass a SortOrder (sortIt) to the constructor.
    Private Sub ColumnClick(sender As Object, e As ColumnClickEventArgs)
        ' Set the ListViewItemSorter property to a new ListViewItemComparer object.
        Dim sortIt As SortOrder
        If listView1.Sorting = SortOrder.Descending Then
            listView1.Sorting = SortOrder.Ascending
            sortIt = SortOrder.Ascending
        Else
            listView1.Sorting = SortOrder.Descending
            sortIt = SortOrder.Descending
        End If
        Me.listView1.ListViewItemSorter = New ListViewItemComparer(e.Column, sortIt)
        listView1.Sort()
    End Sub

Store this passed value:
Class ListViewItemComparer : Implements IComparer
    Private _sortBy As SortOrder
    Private col As Integer

    Public Sub New()
        col = 0
    End Sub

    Public Sub New(ByVal column As Integer, ByVal sortBy As SortOrder)
        col = column
        _sortBy = sortBy
    End Sub

Do the comparing, but then invert the integer result depending on the SortOrder:
    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _
       Implements IComparer.Compare
        Dim result As Integer = [String].Compare(CType(x, ListViewItem).SubItems(col).Text, CType(y, ListViewItem).SubItems(col).Text)
        If _sortBy = SortOrder.Descending Then
            Return -result
        Else
            Return result
        End If
    End Function

Simples!

There is a quirk though. Clicking a different column will typically sort first in Descending order. It should be easy to fix though, just remember which column was previously clicked.

This post has been edited by andrewsw: 21 February 2015 - 07:09 PM

Was This Post Helpful? 1
  • +
  • -

#7 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 06:53 PM

Fixing the quirk.

Remember the last column sorted, start with sorting by the first column:
Public Class Form1
    Private listView1 As ListView
    Private lastCol As Integer = 0
'...

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.listView1.ListViewItemSorter = New ListViewItemComparer(0, SortOrder.Ascending)
        listView1.Sort()
    End Sub

(listView1.Sorting will be Ascending by default)

In ColumnClick check if the clicked column-number has changed.
If it has, switch everything to Descending.. it will then be immediately changed back to Ascending. That is, clicking a different column will first sort in Ascending order: this is the behaviour we normally expect.
    Private Sub ColumnClick(sender As Object, e As ColumnClickEventArgs)
        ' Set the ListViewItemSorter property to a new ListViewItemComparer object.
        Dim sortIt As SortOrder

        If e.Column <> lastCol Then
            'it's a different column..
            listView1.Sorting = SortOrder.Descending
            sortIt = SortOrder.Descending
            'use Descending.. they be immediately switched to Ascending
        End If
        'remember the last column
        lastCol = e.Column
        If listView1.Sorting = SortOrder.Descending Then
            listView1.Sorting = SortOrder.Ascending
            sortIt = SortOrder.Ascending
        Else
            listView1.Sorting = SortOrder.Descending
            sortIt = SortOrder.Descending
        End If
        Me.listView1.ListViewItemSorter = New ListViewItemComparer(e.Column, sortIt)
        listView1.Sort()
    End Sub

Sorted!

Added: The calls listView1.Sort() aren't actually needed, setting the Comparer causes the sort to happen anyway.

This post has been edited by andrewsw: 21 February 2015 - 07:04 PM

Was This Post Helpful? 1
  • +
  • -

#8 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:17 PM

Here is the solution zipped if anyone wants to play with it.

Attached File  ListViewSort.zip (13.98K)
Number of downloads: 603
Was This Post Helpful? 0
  • +
  • -

#9 IronRazer   User is offline

  • Custom Control Freak
  • member icon

Reputation: 1535
  • View blog
  • Posts: 3,864
  • Joined: 01-February 13

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:18 PM

Here is my take on a sorting class. It will sort in Ascending or Descending order and will also sort the columns according to the type of string data in them.

For example if column 1 has Names (String Data), Column 2 has Ages (Integer Data), and column 3 has Dates like "04/11/2015" then the sorter class will determine that and sort it according to the Date.

Public Class ListViewColumnSorter
    Implements System.Collections.IComparer

    Private _ColumnIndex As Integer
    Private _SortingOrder As SortOrder
    Private ItemComparer As CaseInsensitiveComparer

    Public Sub New()
        _ColumnIndex = 0
        _SortingOrder = SortOrder.None
        ItemComparer = New CaseInsensitiveComparer()
    End Sub

    Public Property ColumnIndex() As Integer
        Get
            Return _ColumnIndex
        End Get
        Set(ByVal Value As Integer)
            _ColumnIndex = Value
        End Set
    End Property

    Public Property SortingOrder() As SortOrder
        Get
            Return _SortingOrder
        End Get
        Set(ByVal Value As SortOrder)
            _SortingOrder = Value
        End Set
    End Property

    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
        Dim compareResult As Integer
        Dim LviStrX As String = DirectCast(x, ListViewItem).SubItems(_ColumnIndex).Text
        Dim LviStrY As String = DirectCast(y, ListViewItem).SubItems(_ColumnIndex).Text

        Dim numX, numY As Integer
        Dim dtX, dtY As Date

        If Integer.TryParse(LviStrX, numX) AndAlso Integer.TryParse(LviStrY, numY) Then
            compareResult = ItemComparer.Compare(numX, numY)
        ElseIf Date.TryParse(LviStrX, dtX) AndAlso Date.TryParse(LviStrY, dtY) Then
            compareResult = ItemComparer.Compare(dtX, dtY)
        Else
            compareResult = ItemComparer.Compare(LviStrX, LviStrY)
        End If

        If _SortingOrder = SortOrder.Ascending Then
            Return compareResult
        ElseIf _SortingOrder = SortOrder.Descending Then
            Return -compareResult
        Else
            Return 0
        End If
    End Function
End Class




Then in the form you can use it like shown in the ColumnClick event. I used a listview with 3 columns added to it in this example. The first time the header is clicked it will sort in Ascending order and the second time it sorts in Descending order. It will switch back and forth on each click after that.
Public Class Form1
    Dim r As New Random

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim names() As String = {"Andrew", "Roy", "John"}
        Dim ages() As String = {"40", "13", "48"}
        For i As Integer = 0 To names.Length - 1
            Dim lvi As ListViewItem = ListView1.Items.Add(names(i))
            lvi.SubItems.Add(ages(i))
            lvi.SubItems.Add(Now.AddDays(r.Next(0, 50)).ToShortDateString)
        Next
        For Each c As ColumnHeader In ListView1.Columns
            c.Tag = SortOrder.None
        Next
    End Sub

    Private Sub ListView1_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles ListView1.ColumnClick
        Dim iSortOrder As SortOrder = CType(ListView1.Columns(e.Column).Tag, SortOrder)
        Dim lvcs As New ListViewColumnSorter
        If iSortOrder = SortOrder.Ascending Then
            ListView1.Columns(e.Column).Tag = SortOrder.Descending
            lvcs.SortingOrder = SortOrder.Descending
        Else
            ListView1.Columns(e.Column).Tag = SortOrder.Ascending
            lvcs.SortingOrder = SortOrder.Ascending
        End If
        lvcs.ColumnIndex = e.Column
        ListView1.ListViewItemSorter = lvcs
    End Sub
End Class



Attached Image
Was This Post Helpful? 2
  • +
  • -

#10 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:24 PM

I was considering adding TryParse to my Compare as well ;)

Using Tag to store Asc/Desc was something I considered as well.. great minds think alike. (I did the same thing before, years ago, in Javascript, attaching a property to an element/the column.)
Was This Post Helpful? 0
  • +
  • -

#11 JohnESP   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 24
  • Joined: 09-February 15

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:36 PM

There are virtually endless ways of creating solutions.

Those are more advanced than mine however. lol
Was This Post Helpful? 0
  • +
  • -

#12 IronRazer   User is offline

  • Custom Control Freak
  • member icon

Reputation: 1535
  • View blog
  • Posts: 3,864
  • Joined: 01-February 13

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:37 PM

It was one i put together for a post i helped on a week or two ago about doing this so it was right on hand.

I just added the Tag property part because it didn`t feel right going off a class scoped array or list to keep track of the order of each column. Especially having the possibility that the columns could be reordered by the user if the AllowColumnReorder is set True.

I didn`t try it with long dates or dates in a different format so there is room for improvement i`m sure. It would be nice to add checking for Decimal or Double types too.
Was This Post Helpful? 0
  • +
  • -

#13 andrewsw   User is offline

  • quantum multiprover
  • member icon

Reputation: 6776
  • View blog
  • Posts: 27,942
  • Joined: 12-December 12

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:44 PM

Off topic

IronRazer, what do you use to create your animated gif?
Was This Post Helpful? 0
  • +
  • -

#14 IronRazer   User is offline

  • Custom Control Freak
  • member icon

Reputation: 1535
  • View blog
  • Posts: 3,864
  • Joined: 01-February 13

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:44 PM

Quote

There are virtually endless ways of creating solutions.
Those are more advanced than mine however. lol


Yea, there is more than one way. That`s one nice thing about everyone participating, you usually get a variety of suggestions. :)
Was This Post Helpful? 0
  • +
  • -

#15 IronRazer   User is offline

  • Custom Control Freak
  • member icon

Reputation: 1535
  • View blog
  • Posts: 3,864
  • Joined: 01-February 13

Re: Simple Listview sorting by columns

Posted 21 February 2015 - 07:54 PM

@ andrewsw,

It`s a program i have been writing for about a year or so now. I think i might be getting close to putting it out to the public in the near future. It still has a few things i am adding and debugging. There is a program "licecap" that makes them that you can download free.
Was This Post Helpful? 1
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2