1

Issue definition

I use the OpenNetCF TimeZoneCollection class to display in a ComboBox all the available time zones.

    Dim tzc As New TimeZoneCollection
    Dim TheIndex As Integer
    Dim MyTimeZoneInfo As New TimeZoneInformation

    DateTimeHelper.GetTimeZoneInformation(MyTimeZoneInfo)

    tzc.Initialize()
    For Each tzi As TimeZoneInformation In tzc
        TheIndex = ComboBox1.Items.Add(tzi)
        If tzi.StandardName = MyTimeZoneInfo.StandardName Then
            ComboBox1.SelectedIndex = TheIndex
        End If
    Next

But they are not sorted:

enter image description here

How could I sort the list?

Alphabetical order is fine, time shift order is better.

Replicate this issue on your side (need VS and a CE device)

  1. Create an empty Smart Device project (Visual Basic)
  2. Download OpenNetCF Community Edition (free)
  3. Add OpenNETCF.WindowsCE.dll as Reference (right click on the project -> Add Reference)
  4. Open Form1, add a combobox, and paste code below:

    Imports OpenNETCF.WindowsCE Public Class Form1

    Private Sub Form1_Activated(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Activated
        Dim tzc As New TimeZoneCollection
        Dim TheIndex As Integer
        Dim MyTimeZoneInfo As New TimeZoneInformation
    
        DateTimeHelper.GetTimeZoneInformation(MyTimeZoneInfo)
    
        tzc.Initialize()
        For Each tzi As TimeZoneInformation In tzc
            TheIndex = ComboBox1.Items.Add(tzi)
            If tzi.StandardName = MyTimeZoneInfo.StandardName Then
                ComboBox1.SelectedIndex = TheIndex
            End If
        Next
    End Sub
    

    End Class

RawBean
  • 442
  • 5
  • 20

3 Answers3

1

I realize my problem was a bit easy to solve. So, I found a simple way, in using the built-in Sort() method of the ArrayList.

what I do:

  1. copy the DisplayName in an ArrayList of String
  2. Sort it
  3. Use it to re-index the collection of TimeZoneInformation.

My code:

    Dim tzc As New TimeZoneCollection ' the raw collection'
    Dim ar_s As New ArrayList() ' the array used to sort alphabetically the DisplayName'
    Dim tzc_s As New TimeZoneCollection ' the sorted collection'
    Dim TheIndex As Integer
    Dim MyTimeZoneInfo As New TimeZoneInformation

    DateTimeHelper.GetTimeZoneInformation(MyTimeZoneInfo)

    tzc.Initialize()
    ' copy the display name in an array to sort them'
    For Each tzi As TimeZoneInformation In tzc
        ar_s.Add(tzi.DisplayName)
    Next
    ar_s.Sort()
    ' populated tzc_s, a sorted collection of TimeZoneInformation'
    For i As Integer = 0 To ar_s.Count - 1
        For Each tzi As TimeZoneInformation In tzc
            If ar_s(i) = tzi.DisplayName Then
                tzc_s.Add(tzi)
                Continue For
            End If
        Next
    Next
    ' Bind the sorted ArrayList to the ComboBox'
    For Each tzi As TimeZoneInformation In tzc_s
        TheIndex = ComboBox1.Items.Add(tzi)
        If tzi.StandardName = MyTimeZoneInfo.StandardName Then
            ComboBox1.SelectedIndex = TheIndex
        End If
    Next
RawBean
  • 442
  • 5
  • 20
  • I got it working but different: I subclass TimeZoneCollection, which has a bug, btw, in extracting standard offset. Then I added an IComparer that sorts by Offset and by DisplayName. As here is no upload option, I did not upload the full project. And, sorry, cannot access the code before 10 days. Please remind me on 13. oct. – josef Oct 03 '14 at 05:56
  • @josef, it's fine don't upload the full project I got a solution working in VB inspired fron your idea. I posted it as a new answer, and it's working fine. Thank you. – RawBean Oct 03 '14 at 14:39
1

I would subclass the TimeZoneCollection and add a Sort method as you alreday found but more or less implement by hand. I was not bale to verify the following as I do not have a compact framework here:

In the subclassed TimeZoneCollection add a sort method and a IComparable class. Within that class you can define whatever sort order you wich (by names, by GMT-offset...):

...
public class myTimeZoneCollection:TimeZoneCollection{
...
      public class myTZIComparer : IComparer  {
      // return -1 for a is before b
      // return +1 for a is after b
      // return 0 if a is same order as b
      int IComparer.Compare( Object a, Object b )  {
       {
          TZData c1=(TZData)a;
          TZData c2=(TZData)b;
          if (c1.GMTOffset > c2.GMTOffset)
                    return -1;//1;          //this will result in reverse offset order
          if (c1.GMTOffset < c2.GMTOffset)
                    return 1;//-1;
          else
             return 0;
       }
      }
      ...
    public void sort(){
        // Sorts the values of the ArrayList using the reverse case-insensitive comparer.
        IComparer myComparer = new myTZIComparer();
        this.Sort( myComparer );
    }
 ...
}

And yes, I am sorry, this is in C# but must be do also in VB.

And, use the sorted my TimeZoneCollection to add the elements to the combobox. There is no additional work needed to get the list into the combobox in a custom sort order. Just do foreach and add.

Here is the promised full VB solution with the OpenNetCF fix:

myTimeZoneCollection.vb:

Option Strict On
Option Explicit On

Imports OpenNETCF.WindowsCE

Public Class myTimeZoneCollection
    Inherits TimeZoneCollection
    Dim tzc As New TimeZoneCollection
    Public Sub New()
    End Sub
    Overloads Function Initialize() As TimeZoneCollection
        tzc.Initialize()
        Dim myComparer = New myTZIComparer()
        tzc.Sort(myComparer)
        Return tzc
    End Function
    Shared Function getOffsetFromDisplayName(ByVal tzi As TimeZoneInformation) As Integer
        ' known forms
        ' GMT   = no offset
        ' GMT+6 = 6 hours offset
        ' GMT-12 = -6 hours offset
        ' GMT+4:30  = 4 hours and 30 minutes offset
        ' GMT-4:30  = - 4 hours and 30 minutes offset
        ' all these end with a space! followed by the name of the time zone
        'System.Diagnostics.Debug.WriteLine("getOffsetFromDisplayName: tzi=" & tzi.ToString())
        'extract offset
        If (tzi.DisplayName = "GMT") Then
            Return 0
        End If
        Dim subStr As String
        subStr = tzi.DisplayName.Substring(0, tzi.DisplayName.IndexOf(" "c)) 'GMT+x or GMT-x or GMT+x:yy or GMT-x:yy
        If (subStr = "GMT") Then
            Return 0
        End If
        subStr = subStr.Substring(3) 'cut GMT from begin
        'now check if this is with a minute value
        Dim hoursOffset, minutesOffset, idxOfColon, idxM As Integer : idxOfColon = 0 : idxM = 0
        idxOfColon = subStr.IndexOf(":"c)
        If (idxOfColon = -1) Then 'no : found
            hoursOffset = System.Int32.Parse(subStr)
            minutesOffset = hoursOffset * 60
        Else
            Dim sH, sM As String
            sH = subStr.Substring(0, subStr.Length - idxOfColon - 1)
            sM = subStr.Substring(idxOfColon + 1)
            hoursOffset = System.Int32.Parse(sH)
            minutesOffset = System.Int32.Parse(sM)
            If (hoursOffset > 0) Then
                minutesOffset = minutesOffset + hoursOffset * 60
            Else
                minutesOffset = hoursOffset * 60 - minutesOffset
            End If
        End If
        Return minutesOffset
    End Function
    Class myTZIComparer
        Implements IComparer
        '// return -1 for a is before b
        '// return +1 for a is after b
        '// return 0 if a is same order as b
        Public Function Compare(ByVal a As Object, ByVal b As Object) As Integer Implements IComparer.Compare
            Dim c1 As TimeZoneInformation = CType(a, TimeZoneInformation)
            Dim c2 As TimeZoneInformation = CType(b, TimeZoneInformation)
            Dim offset1, offset2 As Integer
            offset1 = getOffsetFromDisplayName(c1)
            offset2 = getOffsetFromDisplayName(c2)

            If (offset1 > offset2) Then
                Return -1 '//1;          //this will result in reverse offset order
            ElseIf (offset1 < offset2) Then
                Return 1 '//-1;
            Else 'offsets equal, sort by name
                If (c1.DisplayName < c2.DisplayName) Then
                    Return -1
                ElseIf (c1.DisplayName > c2.DisplayName) Then
                    Return 1
                Else
                    Return 0
                End If
            End If
        End Function
    End Class
End Class

By changing or adding another myTZIComparer you can define the order of the entries. The OpenNetCF code is wrong for the new timezone names

' GMT+4:30  = 4 hours and 30 minutes offset
' GMT-4:30  = - 4 hours and 30 minutes offset

as it does only look for full hour offsets. So I needed to develop a new 'parser' to get the bias data.

In your code with the listbox:

Public Sub fillList()
    ComboBox1.Items.Clear()

    Dim tzc As New TimeZoneCollection
    Dim TheIndex As Integer
    Dim MyTimeZoneInfo As New TimeZoneInformation

    DateTimeHelper.GetTimeZoneInformation(MyTimeZoneInfo)

    tzc.Initialize()
    For Each tzi As TimeZoneInformation In tzc
        TheIndex = ComboBox1.Items.Add(tzi)
        If tzi.StandardName = MyTimeZoneInfo.StandardName Then
            ComboBox1.SelectedIndex = TheIndex
        End If
    Next
End Sub

The above fills the list in the order the items are.

Public Sub fillListGMT()
    ComboBox1.Items.Clear()
    Dim tzc As New myTimeZoneCollection 'subclassed one
    Dim TheIndex As Integer
    Dim MyTimeZoneInfo As New TimeZoneInformation

    DateTimeHelper.GetTimeZoneInformation(MyTimeZoneInfo)

    Dim tzc1 As New TimeZoneCollection
    tzc1.Clear()
    tzc1 = tzc.Initialize()
    For Each tzi As TimeZoneInformation In tzc1
        TheIndex = ComboBox1.Items.Add(tzi)
        If tzi.StandardName = MyTimeZoneInfo.StandardName Then
            ComboBox1.SelectedIndex = TheIndex
        End If
    Next
End Sub

The code above fills the list ordered by GMT offset.

josef
  • 5,951
  • 1
  • 13
  • 24
  • That's exactly what I wanted to do first, using the IComparer interface, but I failed. I didn't implement a subclass, which looks to be mandatory. Anyway, I translate it to VB and changed some minor stuff, and it works. Sjall I edit your answer with my validated code? Then I would accept it as the answer. – RawBean Oct 02 '14 at 11:04
0

This answer is based on @Josef's idea (written in C#), and translated to VB.

This is the customized class:

Public Class myTimeZoneCollection Inherits TimeZoneCollection

Public Class myTZIComparer
    Implements IComparer
    ' return -1 for a is before b
    ' return +1 for a is after b
    ' return 0 if a is same order as b '
    Public Function Compare(ByVal a As Object, ByVal b As Object) As Integer _
        Implements IComparer.Compare
        Dim c1 As TimeZoneInformation = CType(a, TimeZoneInformation)
        Dim c2 As TimeZoneInformation = CType(b, TimeZoneInformation)
        If (c1.Bias > c2.Bias) Then
            Return -1 ' 1; //this will result in reverse offset order'
        End If
        If (c1.Bias < c2.Bias) Then
            Return 1 ' -1;'
        Else ' sort by name '
            If (c1.DisplayName < c2.DisplayName) Then
                Return -1
            End If
            If (c1.DisplayName > c2.DisplayName) Then
                Return 1
            Else
                Return 0
            End If
        End If
    End Function
End Class

Public Sub MySort()
    ' Sorts the values of the ArrayList using the specific sort Comparer (by Bias)'
    Dim myComparer As IComparer = New myTZIComparer()
    Me.Sort(myComparer)
End Sub

End Class

And this is the usage in the form

Private Sub Form1_Activated(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Activated
    Dim tzc As New myTimeZoneCollection ' this is the customized class
    Dim TheIndex As Integer ' this index is used to select the activated time zone of the system
    Dim MyTimeZoneInfo As New TimeZoneInformation

    DateTimeHelper.GetTimeZoneInformation(MyTimeZoneInfo)

    tzc.Initialize()
    tzc.MySort()
    ' Clear the item list otherwise it is increased at each Activated event.. '
    ComboBox1.Items.Clear()
    For Each tzi As TimeZoneInformation In tzc
        TheIndex = ComboBox1.Items.Add(tzi)
        If tzi.StandardName = MyTimeZoneInfo.StandardName Then
            ComboBox1.SelectedIndex = TheIndex
        End If
    Next
End Sub

I validated this code on a device, it works fine.

RawBean
  • 442
  • 5
  • 20