Kārtošana un IComparer

Mazliet par elementu kārtošanu. Nē, šis nebūs par ātriem kārtošanas algoritmiem, bet par saraksta sakārtošanu pēc paša noteiktas funkcijas.
Reizēm gadās situācija, ka ir dota Windows forma, uz kuras ListView kontrole. Kontrolē iekšā kaut kāds saraksts. Jānis Bērziņš, Pēteris Vītoliņš un visi pārējie. Vai arī “01.05.2004”, “03.02.2003” un citi. Kaut kādu ierakstu saraksts. Parasti gribas šādu sarakstu redzēt sakārtotu. Piemēram, augošā secībā pēc datumiem.
Tā tas gadījās arī man, vajadzēja sakārtot šādus Latvijā tik pierastā veidā noformatētus datumus (dd.MM.yyyy) augošā secībā. Skaidrs ir tas, ka, ja vienkārši ieslēgsim “Sort ascending” ListView kontrolei, datumi tiks sakārtoti apmēram šādā secībā – 01.01.2003; 01.02.2003; 02.01.2003, kas, protams, neatbilst mūsu vēlmei.
Ko darīt? Iejaukties elementu kārtošanas funkcijā.
Ja šādai kontrolei (kā ListView) ir ieslēgts Sort (asc/desc), tad pēc katra elementa pievienošanas tā sevī iekšienē izsauc Sort() funkciju. Sort funkcija pēc kaut kāda (mums nezināma) algoritma sakārto elementus pieprasītajā secībā. Taču skaidrs ir viens, ka neatkarīgi no izvēlētā kārtošanas algoritma (shell sort, quicksort, heapsort), Sort funkcijai būs nepieciešams salīdzināt divus elementus – “kas lielāks, zirgs vai 4nieks?”. Tātad, lai veidotu savu sakārtojumu ListView kontrolē, atliekt tikai piespiest tās kārtotāju izmantot kādu programmētāja definētu salīdzināšanas operāciju.
Šī vajadzība .NET arhitektūrā ir risināta ar IComparer interfeisa palīdzību. Interfeiss pieprasa implementēt vienu funkciju – Compare(x,y), kas atgriež negatīvu vērtību, ja x<y, pozitīvu vērtīby, ja x>y vai 0, ja x=y.
Jāatceras, ka Compare() tiks izsaukts tiem elementiem, ko mēģinām salīdzināt, nevis to labeliem, piemēram. Tas ir, ListView gadījumā mums jāveido funkcija, kas māk salīdzināt objektus ar tipu ListViewItem.
Izveidoju šādu klasi:

Public Class SimpleComparer
  Implements IComparer
  Public Function Compare(ByVal x As Object, ByVal y As Object) _
        As Integer
    Implements System.Collections.IComparer.Compare
    Dim s1 As String = CType(x, ListViewItem).SubItems(0).Text
    Dim s2 As String = CType(y, ListViewItem).SubItems(0).Text
    Dim d1 As Date = DateSerial( _
        CInt(Mid(s1, 7)), CInt(Mid(s1, 4, 2)), CInt(Mid(s1, 1, 2)))
    Dim d2 As Date = DateSerial( _
        CInt(Mid(s2, 7)), CInt(Mid(s2, 4, 2)), CInt(Mid(s2, 1, 2)))
    Return Date.Compare(d1, d2)
  End Function
End Class
Šeit SubItems(0) tiek lietots, lai norādītu kolonnu, pēc kuras jāsalīdzina. Kā zināms, ListViewItem var saturēt vairākas datu kolonnas – piemēram, to bieži var redzēt Windows Explorer logā, kad ieslēgts “Detail view”.
Tātad tagad mums ir savs salīdzinātājs. Diemžēl neviens to neizmanto : ( Ko darīt – rakstīt savu ListView klasi (mantotu no Windows.Forms.ListView), kas izmanto tieši šo salīdzinātāju. To darīju šādi:
Public Class MyListView
  Inherits Windows.Forms.ListView
  Private ocComparer As SimpleComparer
   Public Sub New()
    ocComparer = New SimpleComparer
    Me.ListViewItemSorter = ocComparer
    Me.Sorting = SortOrder.Ascending
   End Sub
End Class

Tagad ir izveidota ListView klase, kas elementu salīdzināšanai izmanto savu salīdzināšanas funkciju. Atliek vien to ielikt kādā savā formā un lietot uz nebēdu.