- Handling DTN_FORMAT and DTN_FORMATQUERY messages of the DateTimePicker control to use callback fields in format string in .NET
- Private Sub WmFormat(ByRef m As Message)
- Dim nmdatetimeformat As FWEx.Win32API.NMDATETIMEFORMAT = m.GetLParam(GetType(FWEx.Win32API.NMDATETIMEFORMAT))
- Dim format As String = Marshal.PtrToStringAuto(nmdatetimeformat.pszFormat)
- Dim time As Date = SysTimeToDateTime(nmdatetimeformat.st)
- If format = "XX" Then
- Dim week As Integer = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(time, Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)
- ' Here is the problem. The week number does not shown in custom field.
- nmdatetimeformat.pszDisplay = Marshal.StringToHGlobalUni(week.ToString)
- ' I've tried more copy methods without luck
- ' nmdatetimeformat.pszDisplay = Marshal.StringToBSTR(week.ToString)
- ' Marshal.Copy(week.ToString.ToCharArray(), 0, nmdatetimeformat.pszDisplay, week.ToString.Length)
- Marshal.StructureToPtr(nmdatetimeformat, m.LParam, True)
- End If
- m.Result = IntPtr.Zero
- End Sub
- Imports System
- Imports System.ComponentModel
- Imports System.Drawing
- Imports System.Runtime.InteropServices
- Imports System.Windows.Forms
- Imports System.Globalization
- Public Class DateTimePickerEx
- Inherits DateTimePicker
- Private Const WM_REFLECT As Integer = &H2000
- Private Sub WmFormat(ByRef m As Message)
- Dim nmdatetimeformat As NMDATETIMEFORMAT = m.GetLParam(GetType(NMDATETIMEFORMAT))
- Dim format As String = Marshal.PtrToStringAuto(nmdatetimeformat.pszFormat)
- Dim time As Date = SysTimeToDateTime(nmdatetimeformat.st)
- If format = "XX" Then
- Dim week As Integer = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(time, Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)
- ' Here is the problem. The week number does not shown in custom field.
- nmdatetimeformat.pszDisplay = Marshal.StringToHGlobalUni(week.ToString)
- ' I've tried more copy methods without luck
- ' nmdatetimeformat.pszDisplay = Marshal.StringToBSTR(week.ToString)
- ' Marshal.Copy(week.ToString.ToCharArray(), 0, nmdatetimeformat.pszDisplay, week.ToString.Length)
- Marshal.StructureToPtr(nmdatetimeformat, m.LParam, True)
- End If
- m.Result = IntPtr.Zero
- End Sub
- Private Sub WmFormatQuery(ByRef m As Message)
- Dim nmdatetimeformatquery As NMDATETIMEFORMATQUERY = m.GetLParam(GetType(NMDATETIMEFORMATQUERY))
- Dim format As String = Marshal.PtrToStringAuto(nmdatetimeformatquery.pszFormat)
- Dim dst As IntPtr = (m.LParam.ToInt64() + Marshal.OffsetOf(GetType(NMDATETIMEFORMATQUERY), "szMax").ToInt64())
- 'Dim dc As IntPtr = GetDC(Me.Handle)
- Dim textSize As Size = TextRenderer.MeasureText(Me.CreateGraphics, "52", Me.Font, New Size(20, 13), TextFormatFlags.NoPadding)
- nmdatetimeformatquery.szMax.cx = textSize.Width
- nmdatetimeformatquery.szMax.cy = textSize.Height
- Marshal.StructureToPtr(nmdatetimeformatquery, m.LParam, True)
- 'Marshal.Copy(New Long() {nmdatetimeformatquery.szMax.cx, nmdatetimeformatquery.szMax.cy}, 0, dst, 2)
- m.Result = IntPtr.Zero
- End Sub
- Private Sub WmKeyDown(ByRef m As Message)
- Dim nmdatetimewmkeydown As NMDATETIMEWMKEYDOWN = m.GetLParam(GetType(NMDATETIMEWMKEYDOWN))
- Dim format As String = Marshal.PtrToStringAuto(nmdatetimewmkeydown.pszFormat)
- Dim curDat As Date = MyBase.Value
- Select Case format
- Case "XX"
- Select Case DirectCast(nmdatetimewmkeydown.nVirtKey, VirtualKeys)
- Case VirtualKeys.VK_DOWN, VirtualKeys.VK_SUBTRACT
- curDat = curDat.AddDays(-7)
- Case VirtualKeys.VK_UP, VirtualKeys.VK_ADD
- curDat = curDat.AddDays(7)
- Case Else
- Exit Sub
- End Select
- Case "X"
- End Select
- nmdatetimewmkeydown.st.Day = curDat.Day
- nmdatetimewmkeydown.st.Month = curDat.Month
- nmdatetimewmkeydown.st.Year = curDat.Year
- Marshal.StructureToPtr(nmdatetimewmkeydown, m.LParam, True)
- m.Result = IntPtr.Zero
- End Sub
- Private Sub WmDateTimeChange(ByRef m As Message)
- End Sub
- Private Sub WmNotifyDeltaPos(ByRef m As Message)
- Dim nmupdown As NMUPDOWN = m.GetLParam(GetType(NMUPDOWN))
- Dim vKey As VirtualKeys
- If nmupdown.iDelta < 0 Then
- vKey = VirtualKeys.VK_UP
- nmupdown.iDelta = Math.Abs(nmupdown.iDelta)
- ElseIf nmupdown.iDelta > 0 Then
- vKey = VirtualKeys.VK_DOWN
- Else
- Exit Sub
- End If
- For i As Integer = 0 To nmupdown.iDelta - 1
- PostMessage(IntPtr.Zero, WindowsMessages.WM_KEYDOWN, vKey, &H1)
- PostMessage(IntPtr.Zero, WindowsMessages.WM_KEYUP, vKey, &H10000001)
- Next
- End Sub
- Private Sub WmDropDown(ByRef m As Message)
- End Sub
- Private Function WmReflectCommand(ByRef m As Message) As Boolean
- If m.HWnd = Me.Handle Then
- Dim code As Long = NMHDR.FromMessage(m).code
- If DirectCast(m.Msg, WindowsMessages) And WM_REFLECT Then
- Select Case code
- Case -756
- Me.WmFormat(m)
- Return True
- Case DateTimePickerNotifications.DTN_FORMAT
- Me.WmFormat(m)
- Return True
- Case DateTimePickerNotifications.DTN_FORMATQUERY
- Me.WmFormatQuery(m)
- Return True
- Case DateTimePickerNotifications.DTN_WMKEYDOWN
- Me.WmKeyDown(m)
- Return True
- Case -759 'DateTimePickerNotifications.DTN_DATETIMECHANGE
- 'Me.WmDateTimeChange(m)
- 'Return True
- Case -754
- 'Me.WmDropDown(m)
- ' Return True
- Case Is < 0
- Return False
- End Select
- Else
- Select Case code
- Case UpDownNotifications.UDN_DELTAPOS
- Me.WmNotifyDeltaPos(m)
- Return True
- End Select
- End If
- Return False
- End If
- End Function
- Private Function SysTimeToDateTime(st As SYSTEMTIME) As Date
- Return New Date(st.Year, st.Month, st.Day, st.Hour, st.Minute, st.Second)
- End Function
- <System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")>
- Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
- If m.Msg <> 71 AndAlso m.Msg <> 513 Then
- Select Case DirectCast(m.Msg, WindowsMessages)
- Case WindowsMessages.WM_NOTIFY + WM_REFLECT, WindowsMessages.WM_NOTIFY
- If Not Me.WmReflectCommand(m) Then
- Exit Select
- End If
- Exit Sub
- End Select
- End If
- MyBase.WndProc(m)
- End Sub
- <StructLayout(LayoutKind.Sequential)>
- Public Structure NMDATETIMEFORMAT
- ''' <summary>
- ''' NMHDR structure that contains information about the message.
- ''' </summary>
- Public nmhdr As NMHDR
- ''' <summary>
- ''' Pointer to the null-terminated substring that defines a DTP control callback field.
- ''' The substring comprises one or more X characters, followed by a NULL character.
- ''' </summary>
- Public pszFormat As IntPtr
- ''' <summary>
- ''' SYSTEMTIME structure that contains information about the current system date and time.
- ''' </summary>
- Public st As SYSTEMTIME
- ''' <summary>
- ''' Pointer to a null-terminated string.
- ''' By default, this pointer is set to point at szDisplay by the DTP control.
- ''' It is legal to set this member to point at an existing string.
- ''' If so, the application is not required to place a string into the szDisplay member.
- ''' </summary>
- Public pszDisplay As IntPtr
- ''' <summary>
- ''' Specifies the 64-character buffer that is to receive the null-terminated string that the DTP control will display.
- ''' It is not necessary that the application fill the entire buffer.
- ''' </summary>
- <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=64)>
- Public szDisplay As String
- End Structure
- ''' <summary>
- ''' This structure is used with the DTN_FORMATQUERY notification message.
- ''' </summary>
- ''' <remarks>
- ''' The NMDATETIMEFORMATQUERY structure contains information about a date and time picker (DTP) control callback field.
- ''' It contains a substring (taken from the controls format string) that defines a callback field.
- ''' The structure receives the maximum allowable size of the text that will be displayed in the callback field.
- ''' </remarks>
- <StructLayout(LayoutKind.Sequential)>
- Public Structure NMDATETIMEFORMATQUERY
- ''' <summary>
- ''' NMHDR structure that contains information about this notification message.
- ''' </summary>
- Public nmhdr As NMHDR
- ''' <summary>
- ''' Pointer to a null-terminated substring that defines a DTP control callback field.
- ''' The substring is one or more X characters followed by a NULL.
- ''' </summary>
- Public pszFormat As IntPtr
- ''' <summary>
- ''' SIZE structure that must be filled with the maximum size of the text to be displayed in the callback field.
- ''' </summary>
- Public szMax As SIZE
- End Structure
- ''' <summary>
- ''' This structure carries information used to describe and handle a DTN_WMKEYDOWN notification message.
- ''' </summary>
- <StructLayout(LayoutKind.Sequential)>
- Public Structure NMDATETIMEWMKEYDOWN
- ''' <summary>
- ''' NMHDR structure that contains information about the notification message.
- ''' </summary>
- Public nmhdr As NMHDR
- ''' <summary>
- ''' Virtual key code that represents the key that the user pressed.
- ''' </summary>
- Public nVirtKey As Integer
- ''' <summary>
- ''' Pointer to a null-terminated substring, taken from the format string, that defines the callback field.
- ''' The substring is one or more X characters, followed by a NULL.
- ''' </summary>
- Public pszFormat As IntPtr
- ''' <summary>
- ''' SYSTEMTIME structure containing the current date and time from the DTP control.
- ''' The owner of the control must modify the time information based on the users keystroke.
- ''' </summary>
- Public st As SYSTEMTIME
- End Structure
- ''' <summary>
- ''' The SIZE structure specifies the width and height of a rectangle.
- ''' </summary>
- ''' <remarks>
- ''' The rectangle dimensions stored in this structure may correspond to viewport extents,
- ''' window extents, text extents, bitmap dimensions, or the aspect-ratio filter for some extended functions.
- ''' </remarks>
- <StructLayout(LayoutKind.Sequential)>
- Public Structure SIZE
- ''' <summary>
- ''' Specifies the rectangle's width. The units depend on which function uses this.
- ''' </summary>
- Public cx As Long
- ''' <summary>
- ''' Specifies the rectangle's height. The units depend on which function uses this.
- ''' </summary>
- Public cy As Long
- End Structure
- ''' <summary>
- ''' This structure contains information about a message.
- ''' The pointer to this structure is specified as the lParam member of the WM_NOTIFY message.
- ''' </summary>
- <StructLayout(LayoutKind.Sequential)> _
- Public Structure NMHDR
- ''' <summary>
- ''' Window handle to the control sending a message.
- ''' </summary>
- Public hwndFrom As IntPtr
- ''' <summary>
- ''' Identifier of the control sending a message.
- ''' </summary>
- Public idFrom As IntPtr
- ''' <summary>
- ''' Notification code. This member can be a control-specific notification code or it can be one of the common notification codes. The following values are supported if you include mouse support in your device platform:
- ''' - NM_RCLICK
- ''' - NM_RDBCLICK
- ''' </summary>
- Public code As Integer
- Public Shared Function FromMessage(ByVal msg As System.Windows.Forms.Message) As NMHDR
- Return DirectCast(msg.GetLParam(GetType(NMHDR)), NMHDR)
- End Function
- End Structure
- ''' <summary>
- ''' Specifies a date and time, using individual members for the month, day, year, weekday, hour, minute, second, and millisecond.
- ''' The time is either in coordinated universal time (UTC) or local time, depending on the function that is being called.
- ''' </summary>
- ''' <remarks>
- ''' It is not recommended that you add and subtract values from the SYSTEMTIME structure to obtain relative times.
- ''' </remarks>
- Public Structure SYSTEMTIME
- ''' <summary>
- ''' The year. The valid values for this member are 1601 through 30827.
- ''' </summary>
- Public Year As Short
- ''' <summary>
- ''' The month.
- ''' </summary>
- Public Month As Short
- ''' <summary>
- ''' The day of the week. Sunday = 0.
- ''' </summary>
- Public DayOfWeek As Short
- ''' <summary>
- ''' The day of the month. The valid values for this member are 1 through 31.
- ''' </summary>
- Public Day As Short
- ''' <summary>
- ''' The hour. The valid values for this member are 0 through 23.
- ''' </summary>
- Public Hour As Short
- ''' <summary>
- ''' The minute. The valid values for this member are 0 through 59.
- ''' </summary>
- Public Minute As Short
- ''' <summary>
- ''' The second. The valid values for this member are 0 through 59.
- ''' </summary>
- Public Second As Short
- ''' <summary>
- ''' The millisecond. The valid values for this member are 0 through 999.
- ''' </summary>
- Public Milliseconds As Short
- End Structure
- ''' <summary>
- ''' Contains information specific to up-down control notification messages.
- ''' It is identical to and replaces the NM_UPDOWN structure.
- ''' </summary>
- ''' <remarks></remarks>
- Public Structure NMUPDOWN
- ''' <summary>
- ''' NMHDR structure that contains additional information about the notification.
- ''' </summary>
- Public hdr As NMHDR
- ''' <summary>
- ''' Signed integer value that represents the up-down control's current position.
- ''' </summary>
- Public iPos As Integer
- ''' <summary>
- ''' Signed integer value that represents the proposed change in the up-down control's position.
- ''' </summary>
- Public iDelta As Integer
- End Structure
- Public Enum DateTimePickerMessages
- DTM_FIRST = &H1000
- DTM_GETSYSTEMTIME = DTM_FIRST + 1
- DTM_SETSYSTEMTIME = DTM_FIRST + 2
- DTM_GETRANGE = DTM_FIRST + 3
- DTM_SETRANGE = DTM_FIRST + 4
- DTM_SETFORMAT = DTM_FIRST + 5
- DTM_SETMCCOLOR = DTM_FIRST + 6
- DTM_GETMCCOLOR = DTM_FIRST + 7
- DTM_GETMONTHCAL = DTM_FIRST + 8
- DTM_SETMCFONT = DTM_FIRST + 9
- DTM_GETMCFONT = DTM_FIRST + 10
- DTM_SETMCSTYLE = DTM_FIRST + 11
- DTM_GETMCSTYLE = DTM_FIRST + 12
- DTM_CLOSEMONTHCAL = DTM_FIRST + 13
- DTM_GETDATETIMEPICKERINFO = DTM_FIRST + 14
- DTM_GETIDEALSIZE = DTM_FIRST + 15
- End Enum
- Public Enum DateTimePickerNotifications
- DTN_FIRST = -740
- DTN_LAST = -745
- DTN_FIRST2 = -753
- DTN_LAST2 = -799
- DTN_DATETIMECHANGE = DTN_FIRST - 6
- DTN_USERSTRING = DTN_FIRST - 5
- DTN_WMKEYDOWN = DTN_FIRST - 4
- DTN_FORMAT = DTN_FIRST - 3
- DTN_FORMATQUERY = DTN_FIRST - 2
- DTN_DROPDOWN = DTN_FIRST - 1
- DTN_CLOSEUP = DTN_FIRST
- End Enum
- Public Enum UpDownNotifications
- UDN_FIRST = -721
- UDN_LAST = -729
- UDN_DELTAPOS = UDN_FIRST - 1
- End Enum
- Public Enum WindowsMessages
- ''' <summary>
- '''Sent by a common control to its parent window when an event has occurred or the control requires some information.
- ''' </summary>
- WM_NOTIFY = &H4E
- ''' <summary>
- '''The WM_KEYDOWN message is posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when the ALT key is not pressed.
- ''' </summary>
- WM_KEYDOWN = &H100
- ''' <summary>
- '''The WM_KEYUP message is posted to the window with the keyboard focus when a nonsystem key is released. A nonsystem key is a key that is pressed when the ALT key is not pressed or a keyboard key that is pressed when a window has the keyboard focus.
- ''' </summary>
- WM_KEYUP = &H101
- End Enum
- ''' <summary>
- ''' The VirtualKeys enumeration is also managed as System.Windows.Forms.Keys.
- ''' The bracketed [keynames] are from that managed Forms.Keys enumeration.
- ''' </summary>
- ''' <remarks></remarks>
- Public Enum VirtualKeys As Integer
- VK_UP = &H26 ' // [Up] = 038
- VK_DOWN = &H28 ' // [Down] = 040
- VK_ADD = &H6B ' // [Add] = 107
- VK_SUBTRACT = &H6D ' // [Subtract] = 109
- End Enum
- Public Declare Auto Function PostMessage Lib "user32" (ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean