views:

420

answers:

3

I have a two-column ListView linked to a Datapager. The ListView lists all files of a particular type in a particular directory, so the data source is an ArrayList of type FileInfo. Consequently, I had to create a Comparer class to use in the ArrayList.Sort method.

FileInfoComparer.vb:

Imports System.IO

Friend Class FileInfoDateComparer
 Implements IComparer

 Public Overridable Overloads Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
  Dim oX As FileInfo = CType(x, FileInfo)
  Dim oY As FileInfo = CType(y, FileInfo)
  Return oX.LastWriteTime.CompareTo(oY.LastWriteTime)
 End Function
End Class

Friend Class FileInfoNameComparer
 Implements IComparer

 Public Overridable Overloads Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
  Dim oX As FileInfo = CType(x, FileInfo)
  Dim oY As FileInfo = CType(y, FileInfo)
  Return oX.Name.CompareTo(oY.Name)
 End Function
End Class

GenerateSortedArray sub:

 Private Sub GenerateSortedArray(ByVal SortColumn As String, ByVal SortDirection As String)
  Dim dirInfo As New DirectoryInfo(Server.MapPath(AppSettings.Item("ContentDir")))
  Dim FileArrayList As New ArrayList(dirInfo.GetFiles("*.msg", SearchOption.TopDirectoryOnly))

  With FileArrayList
   .TrimToSize()
   Select Case SortColumn
    Case "Name"
     .Sort(New FileInfoNameComparer)
    Case Else
     .Sort(New FileInfoDateComparer)
   End Select
  End With

ListView_Sorting:

  Session("SortColumn") = e.SortExpression
  Session("SortDirection") = e.SortDirection

  Call GenerateSortedArray(e.SortExpression, DBNull.Value.ToString)

All this works well, however, I don't know how to reverse the sort. As you can see from the code, I've determined the Sort Direction, but I don't know how to use it to reverse the sort.

Any thoughts?

+1  A: 

Have you Comparer classes include a Boolean member variable named something like SortAscending, and allow this value to be specified through the constructor.

For example, your FileInfoDateComparer class would look like the following (untested) code:

Class FileInfoDateComparer
 Implements IComparer

  Private _SortAscending As Boolean = True

  Public Sub New(sortAscending As Boolean)
    _SortAscending = sortAscending
  End Sub

  ...
End Class

With that in place all that remains is to "swap" the oX and oY values before calling CompareTo IF SortAscending = False.

Public Overridable Overloads Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
  Dim oX As FileInfo = CType(x, FileInfo)
  Dim oY As FileInfo = CType(y, FileInfo)

  If _SortAscending = False Then
    Dim temp As FileInfo = oX
    oX = oY
    oY = temp
  End If

  Return oX.LastWriteTime.CompareTo(oY.LastWriteTime)
End Function

That oughta do it! FYI, the above code was entered off the cuff, so there may be a syntax error in there, but the concept is sound - I've used it many times myself in past projects.

One final comment - if you are using .NET 3.5+ you can use LINQ to Objects syntax instead of having to create your own Comparer class, if you so desire...

Happy Programming...

Scott Mitchell
Ditto my comments to Ruben, I came across a clear Comparer example that made sense. LINQ is a complete unknown at the moment, though obviously is on my To-Do list (along with so much else!).
CJM
CJM, check out this article for a gentle introduction into LINQ (with VB and C# examples): http://www.4guysfromrolla.com/articles/021109-1.aspxFYI, here's how easy and terse LINQ makes everything: dirInfo.GetFiles("*.msg", SearchOption.TopDirectoryOnly).OrderBy(Function(f) f.LastWriteTime)
Scott Mitchell
+1  A: 

You can send SortDirectory through your FileInfoDateComparer; receiving an "DESC" value you can multiply return value by -1;

// sorry, C# version:

class FileInfoDateComparer
{
    private bool ascendingOrder = true;
    public FileInfoDateComparer(bool ascendingOrder)
    {
        this.sortOrder = sortOrder;
    }

    ... int Compare(object x, object y)
    {
        //...
        return
            oX.LastWriteTime.CompareTo(oY.LastWriteTime) * 
            (ascendingOrder ? 1: -1);
    }

}

You said nothing about your framework version, but think about using Reverse() Linq extension method.

Rubens Farias
I'm using 3.5 so LINQ is on the cards, but it's just one step further into the unknown at the moment - but I appreciate your suggestion.
CJM
+1  A: 

I have usually done something like this:

Friend Class FileInfoDateComparer
 Implements IComparer

    Private _sortOrder As System.Windows.Forms.SortOrder

    Public Sub New(ByVal sortOrder As System.Windows.Forms.SortOrder)
        _sortOrder = sortOrder
    End Sub

    Public Overridable Overloads Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
       Dim oX As FileInfo = CType(x, FileInfo)
       Dim oY As FileInfo = CType(y, FileInfo)
       Dim result As Integer = oX.LastWriteTime.CompareTo(oY.LastWriteTime)
       If _sortOrder = System.Windows.Forms.SortOrder.Descending Then
           ' we want the reverse sort order, so we "reverse" the result '
           result = result * -1
       End If
       Return result
 End Function
End Class

If you don't want to use the System.Windows.Forms.SortOrder, you can roll your own SortOrder enum. I usually prefer enums over booleans since you will get a clear statement in your code what the value means. Compare these two:

Dim comparer As New FileInfoDateComparer(True)
Dim comparer As New FileInfoDateComparer(SortOrder.Descending)
Fredrik Mörk
I actually used SqlClient.SortOrder (it is a web app after all!), but you get the big prize for suggesting enumeration. Simple but neat.
CJM