2016-09-04 18 views
0

VB.NET 2010ツールボックスで提供されている通常のDateTimePickerを使用すると、私のプログラムでこのような状況が頻繁に発生します。 、私はこのコントロール31 7 2016年に入力したいと私は、少なくとも簡単ではないか、予想したように、することはできません今DateTimePicker内部検証

Public Class Form1 

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 

     Me.DateTimePicker1.Format = DateTimePickerFormat.Custom 
     Me.DateTimePicker1.CustomFormat = "dd.MM.yyyy." 
     Me.DateTimePicker1.Value = "01.09.2016." 
    End Sub 
End Class 

はこれを参照してください。
31.09が有効な日付として存在しないためですが、この時点で私はまだ希望の日付を入力し終わらないためです。

ここでは、dtpのこの内部検証を無効にする方法や、特定の状況で必要な(記述する)機能を得るための方法はありますか?

+1

誰もがこの質問を指摘しなければなりません。 –

+0

この「利点」は、ここでは表示された形式で日付を使用するネイティブな方法があるので、ここではかなり使用できません。 – user1697111

+0

私は知っている、ここでは同じ日付形式を使用し、同じ "問題"があります。 –

答えて

2

WinFormsを使用しているため、WinForm DateTimePickerコントロールは、ネイティブの共通DateTimePickerコントロールのラッパーに過ぎません。アプリケーションが入力文字列を解析できるように、派生コントロールを作成できます。これは、コントロールのDTS_APPCANPARSEスタイルを設定することによって行われます。このスタイルは、コントロールのCreateParamsプロパティをオーバーライドして設定します。

DTS_APPCANPARSEスタイルが設定されている場合、ネイティブコントロールはDTN_USERSTRING通知をコントロールに送信します。このメッセージは、コントロールのWndProcメソッドで受信されました。

以下に示すコードの大部分は、使用されるネイティブ構造の定義です。この例で使用されている解析関数(TryParse_NMDATETIMESTRING)は、そのカルチャ設定がこの形式をサポートしているため、Thread cultureを "de-DE"に変更することによってDateTime.TryParseメソッドを "dd.MM.yyyy"の形式に再利用します。任意の構文解析ロジックを定義できます。

Imports System.Runtime.InteropServices 
Imports System.Globalization 

Public Class DateTimePickerCustomParse : Inherits DateTimePicker 

    Protected Overrides ReadOnly Property CreateParams As CreateParams 
     Get 
      Const DTS_APPCANPARSE As Int32 = &H10 
      Dim cp As CreateParams = MyBase.CreateParams 
      cp.Style = cp.Style Or DTS_APPCANPARSE 
      Return cp 
     End Get 
    End Property 

#Region "Native Structures" 
    Structure NMDATETIMESTRING 
     Public nmhdr As NMHDR 
     Public pszUserString As IntPtr 
     Public st As SYSTEMTIME 
     Public dwFlags As GDT 
    End Structure 

    Public Enum GDT 
     GDT_ERROR = -1 
     GDT_VALID = 0 
     GDT_NONE = 1 
    End Enum 

    <StructLayout(LayoutKind.Sequential)> _ 
    Public Structure SYSTEMTIME 
     Public wYear As Short 
     Public wMonth As Short 
     Public wDayOfWeek As Short 
     Public wDay As Short 
     Public wHour As Short 
     Public wMinute As Short 
     Public wSecond As Short 
     Public wMilliseconds As Short 
    End Structure 

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto, Pack:=1)> _ 
    Public Class NMHDR 
     Public hwndFrom As IntPtr = IntPtr.Zero 
     Public idFrom As Integer = 0 
     Public code As Integer = 0 
    End Class 
#End Region 

    Private Shared Function TryParse_NMDATETIMESTRING(ByRef nmDTS As NMDATETIMESTRING) As Boolean 
     Dim ret As Boolean 
     Dim enteredDate As String = Marshal.PtrToStringUni(nmDTS.pszUserString) 
     Dim savedThreadCulture As CultureInfo = Threading.Thread.CurrentThread.CurrentCulture 
     Try 
      Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("de-DE") 
      Dim dt As DateTime 
      If DateTime.TryParse(enteredDate, dt) Then 
       nmDTS.dwFlags = GDT.GDT_VALID 
       nmDTS.st = DateTimeToSYSTEMTIME(dt) 
       ret = True 
      Else 
       nmDTS.dwFlags = GDT.GDT_ERROR 
      End If 

     Finally 
      Threading.Thread.CurrentThread.CurrentCulture = savedThreadCulture 
     End Try 

     Return ret 
    End Function 


    Private Shared Function DateTimeToSYSTEMTIME(dt As DateTime) As SYSTEMTIME 
     Dim ret As New SYSTEMTIME 
     ret.wYear = CShort(dt.Year) 
     ret.wDay = CShort(dt.Day) 
     ret.wMonth = CShort(dt.Month) 
     ret.wDayOfWeek = CShort(dt.DayOfWeek) 
     ret.wHour = CShort(dt.Hour) 
     ret.wMinute = CShort(dt.Minute) 
     ret.wSecond = CShort(dt.Second) 
     ret.wMilliseconds = CShort(dt.Millisecond) 
     Return ret 
    End Function 

    Protected Overrides Sub WndProc(ByRef m As Message) 
     Const WM_NOTIFY As Int32 = &H4E 
     Const WM_REFLECT_NOTIFY As Int32 = WM_NOTIFY + &H2000 
     Const DTN_FIRST As Int32 = -740 
     Const DTN_USERSTRINGW As Int32 = (DTN_FIRST - 5) 

     If m.Msg = WM_REFLECT_NOTIFY OrElse m.Msg = WM_NOTIFY Then 
      Dim hdr As New NMHDR 
      Marshal.PtrToStructure(m.LParam, hdr) 
      If hdr.code = DTN_USERSTRINGW Then 
       Dim nmDTS As NMDATETIMESTRING = Marshal.PtrToStructure(Of NMDATETIMESTRING)(m.LParam) 
       If TryParse_NMDATETIMESTRING(nmDTS) Then 
        Marshal.StructureToPtr(nmDTS, m.LParam, True) 
        Exit Sub 
       End If 
      End If 
     End If 
     MyBase.WndProc(m) 
    End Sub 

End Class 

編集: ありDTS_APPCANPARSEスタイルを使用して約特に厄介な事があり、それはそれはコントロールを残すTabキーの使用を排除します。これは、datetimepicker class - with dts_appcanparse style - can't tab outに記載されています。フォーカスを失うか、Enterキーを押すと、入力されたテキストが検証のために送信されます。ただし、Enterキーでは、DTPがキーボードを離れることなく集中しています。さらに、Tabをコントロールに追加すると、標準のDTPのように動作します。 F2キーを押すと、編集モードになり、テキスト全体が選択されます。上で定義した制御でこれらの問題に対処するには、以下を追加することができます。

あなたはこれで WndProc方法を交換する必要があります
Public Property MoveNextOnEnterKey As Boolean = True 
Public Property SelectAllOnEnter As Boolean = True 

Protected Overrides Sub OnEnter(e As EventArgs) 
    MyBase.OnEnter(e) 
    If SelectAllOnEnter Then SendKeys.Send("{F2}") 
End Sub 

Private Sub MoveNext() 
    Me.Parent.SelectNextControl(Me, True, True, True, True) 
End Sub 

:DDMMYYYY` `上` MMDDYYYY`を使用する利点が何であるかを尋ね

Protected Overrides Sub WndProc(ByRef m As Message) 
    Const WM_NOTIFY As Int32 = &H4E 
    Const WM_REFLECT_NOTIFY As Int32 = WM_NOTIFY + &H2000 
    Const DTN_FIRST As Int32 = -740 
    Const DTN_USERSTRINGW As Int32 = (DTN_FIRST - 5) 

    If m.Msg = WM_REFLECT_NOTIFY OrElse m.Msg = WM_NOTIFY Then 
     Dim hdr As New NMHDR 
     Marshal.PtrToStructure(m.LParam, hdr) 
     If hdr.code = DTN_USERSTRINGW Then 
      Dim nmDTS As NMDATETIMESTRING = Marshal.PtrToStructure(Of NMDATETIMESTRING)(m.LParam) 
      If TryParse_NMDATETIMESTRING(nmDTS) Then 
       Marshal.StructureToPtr(nmDTS, m.LParam, True) 
       If MoveNextOnEnterKey Then 
        Me.BeginInvoke(New Action(AddressOf MoveNext)) 
       End If 
       Exit Sub 
      End If 
     End If 
    End If 
    MyBase.WndProc(m) 
End Sub 
+0

これは興味深いようです。私は新しい、サブクラス化されたコントロールでこのコードを試していますが、私はそれを使用する方法を知らない。 Dim Hdr As NMHDR = DirectCast(Marshal.PtrToStructure(m.LParam、GetType(NMHDR))、NMHDR)。解析を無効にすると、dtpは一般的なテキストボックスのように動作します。 dtpが31.09.2016を受け入れる方法をあなたの例に追加できますか?有効な日付として?結局のところ、_Validateまたは_Leaveイベントハンドラの検証を行うことができます。 – user1697111

+0

@ user1697111 - 有効な日付ではないため、有効な日付である「31.09.2016」を決して受け入れません。あなたの質問は、基本DTPがタイプするごとに各コンポーネント(日、月、年)を検証するので、ユーザが入力できるようにする方法でした。これにより、ユーザーは無効な日付全体を入力し、文字列全体を検証する必要があります。検証は 'TryParse_NMDATETIMESTRING'メソッドで提供されます。検証ロジックを変更するには、そのメソッドのロジックを変更します。しかし、現在のところ、あなたが求めたものを正確に許可するように設定されています。 – TnTinMn

+0

はい、私はそれで何かできることを信じています。良いアンサーに感謝します。 – user1697111