Обработка сообщений DTN_FORMAT и DTN_FORMATQUERY элемента управления DateTimePicker для использования полей обратного вызова в строке формата в .NET - PullRequest
1 голос
/ 10 января 2012

Мне нужно использовать возможность файлов обратного вызова элемента управления DateTimePicker в .NET, что описано здесь в C ++: пример .

Но реализовать WmFormat немного сложнометод в .NET.Все работает нормально, поле обратного вызова захватывает сообщение WmKeyDown и изменяет значение даты, но элемент управления не рисует поле обратного вызова.Вот моя реализация:

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
...