Search code examples
.netvb.netwinformsdatetimepickerregional-settings

Format Time in a DateTimePicker as Region settings change


I want to format the time DateTimePicker to 24 hours.

I used the following code and the time is actually changed to 24 hours.
The problem now is that the date format takes an undesirable shape when using an Arabic Region for the device or an English region.
I want the direction of the date to not change no matter how different the region of the device.

I want the direction of the date to be Hours, Minutes and AM/PM as in the picture, regardless of the time zone.

enter image description here

My code:

If Thread.CurrentThread.CurrentCulture.DisplayName.Contains("arab".ToLower)  Then
    ROSYUPDATE_F.DateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom
    ROSYUPDATE_F.DateTimePicker.CustomFormat = "HH:mm tt" 
Else

    ROSYUPDATE_F.DateTimePicker.Format = System.Windows.Forms.DateTimePickerFormat.Custom
    ROSYUPDATE_F.DateTimePicker.CustomFormat = " tt mm:HH"
End If

Animation that changes the time from the desired format

enter image description here


Solution

  • When you change the Regional settings, Thread.CurrentThread.CurrentCulture or (CultureInfo.CurrentCulture) are not automatically updated.
    You can call CultureInfo.ClearCachedData() to reset the internal CultureData stored settings and reload fresh values.

    To update your UI when User settings change, you can handle notifications that the System sends when these settings have changed.

    A simple way to acknowledge these notifications is to handle the WM_SETTINGCHANGE message that is sent to all top-level Windows when something in the System settings has changed.
    These notifications are somewhat generic (read the Docs about it), but you can have a general idea of what has changed inspecting the WParam and LParam values.

    Override WndProc in your Form to handle WM_SETTINGCHANGE messages.
    When you get one, verify which settings have changed and act on this information.

    In this case, when Regional (User) settings change, WParam is set to 0 and LParam points to a string that reads "intl".
    Then, if the Language is RTL (or a non-specific Arabic language - what you get with CultureInfo.GetCultureInfo("AR")), set your Custom Format, otherwise you can set the default ShortTimePattern, or whatever.
    (Assume the DateTimePicker control is named DateTimePicker1)

    Private Const WM_SETTINGCHANGE As Integer = &H1A
    
    Protected Overrides Sub WndProc(ByRef m As Message)
        Select Case m.Msg
            Case WM_SETTINGCHANGE
                Dim lparmSetting = Marshal.PtrToStringAuto(m.LParam)
                If m.WParam.ToInt32() = 0 AndAlso lparmSetting.Equals("intl") Then
                    CultureInfo.CurrentCulture.ClearCachedData()
                    DateTimePicker1.CustomFormat = If(CultureInfo.CurrentCulture.TextInfo.IsRightToLeft,
                        "tt mm:hh", CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern)
                End If
        End Select
        MyBase.WndProc(m)
    End Sub
    

    Secondary:
    Since your Custom format is not exactly standard, you may have a different behavior when the Form is first loaded, depending on the current Regional Settings of the machine where your app is run.
    You can send a WM_SETTINGCHANGE message to yourself, with WParam and LParam set as described, so you update and format your DateTimePicker automatically on startup.

    In Form.Load (or OnLoad() or OnShown() or OnHandleCreated()), your can add:

    Imports System.Runtime.InteropServices
    
    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)
        SendMessage(Me.Handle, WM_SETTINGCHANGE, IntPtr.Zero, Marshal.StringToHGlobalAuto("intl"))
    End Sub
    

    SendMessage declaration:

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function SendMessage(hWnd As IntPtr, uMsg As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
    End Function