TextBoxの拡張コントロールを作る

Windowsフォームアプリ用の日付や数値に特化したTextBoxを作ること
を考えます。
これは、TextBoxを、
入力開始時、日付の / や数値の3桁区切りの , を取って
入力を容易にしたり、日付や数値をメンバ変数に持って
テキストへの表示フォーマットを付加するもので、
TextBoxクラスを継承して、TextBoxの拡張コントロール
にします(クラス名を、DateBox、NumBoxとする)

ユーザコントロールやカスタムコントロールとの違い
拡張コントロールは、System.Windows.Forms下の基本Control
を継承して作ります(今回の場合はTextBox)
元のTextBoxのプロパティは、DateBoxやNumBoxのクラス変数
をつければ直接アクセスできます。
(スタブのプロパティは不要)
カスタムコントロールは、System.Windows.Forms.Control
を継承して作ります。
プロパティは、一から全部作らないといけないです。
ユーザコントロールは、基本Controlを複数組み合わせた、
1つのクラスであり、
System.Windows.Forms.UserControl を継承して作ります。
個々の基本Controlは、独立に内部のクラス変数でアクセス
するので、スタブのプロパティが必要になります。
詳しくは:
https://learn.microsoft.com/ja-jp/dotnet/desktop/winforms/controls-design/overview

1.DateBox、NumBoxでの New()

VisualStudioのVBでは、拡張コントロールは、
Windowsフォームコントロールライブラリで作ります。
(フォームコントロールライブラリ名は、Kf_ExControlとする)
DateBoxの場合、TextBoxクラスを以下のように継承することにします。
(NumBoxでも同様)

Imports System.Windows.Forms

Public Class DateBox
    	Inherits TextBoxSub New()
        MyBase.New()
	    :
    End Sub

これで、元のTextBoxのプロパティは、DateBoxやNumBoxのクラス変数
をつければ直接アクセスできます。
[Windowsフォームアプリでの記述]
参照:Kf_ExControl.dll

Public DateBox1 = Kf_ExControl.DateBox
DateBox1 = New()
        :    
DateBox1.Text = "2025/1/1"    ’ MybaseのText     
Date_1 = DateAdd("d", -1, DateBox1.Value)

2.Textプロパティだけで値の保持と表現との両方は無理

値(Date型や指定の数値型)を持つメンバ変数(m_value)と
表示Formatを持つメンバ変数(m_Format)を設け
Valueプロパティ、Formatプロパティ とします。
(Textプロパティのメンバ変数は設けず、MyBase.Text とします)
TextプロパティとValueプロパティのそれぞれへの代入時は、
互いを変換して対応させます。
Text入力があった場合は、その後のLostfocus時および、
Enterキー入力時、変換して対応させ(そのためのFlgを設けます)
以下のようになります。

Public Overloads Property Text As String
    Get
        Return MyBase.Text
    End Get
    Set(value As String)
        MyBase.Text = value

        Dim s1  As String = Trim(MyBase.Text)
        if s1 = "" or s1 = "/  /" Then
            m_Value = #1/1/1901#
        ElseIf IsDateText(s1) = False Then
            m_Value = Nothing
        Else
            m_Value = ConvTextToValue(MyBase.Text)	' m_Valueと対応すみ
        End If
    End Set
End Property
Public Property Value As Object
    Get
        Return m_Value
    End Get
    Set(v1 As Object)
        m_Value = v1
        If IsNothing(v1) Then
            MyBase.Text = ""
        Else
            MyBase.Text = ConvValueToText(m_Value, m_Format)	' Textと対応すみ
        End If
    End Set
End Property

Public Sub DateBox_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyDown
   If e.KeyCode = 13 Then
       If IsNothing(m_Value) Then		' Enterキー時、m_Valueを対応させる
	        ' 下の LostFocus()と同じ処理
       End If
   Else
       m_Value = Nothing	' LostFocus時とEnterキー時に対応させる
   End If
End Sub

3.Textの表示(初期値と入力時の形式)

Textの初期値は、
NumBoxではTextを「指定の型」とは無関係に「空」とします。
DateBoxではTextを、VB6のMaskEditコントロールに合わせて、
「 / / 」として、Enter(Gotfocus)時に「空」にします。
Enter時(Gotfocus時)にTextがある場合、
DateBoxでは、/ をカットし
月・日を2桁にして表示して入力しやすくします。
この時、yyyymmddという形か、yymmddという形か(20yyを仮定)
m_Formatの先頭にGやgがあれば「アルファベットS,H,R の後yymmdd」
かにします。
(Text入力の時には、/ は不要ですが、入れれば、0を省略できる)
NumBoxでは、¥や , をカットして入力しやすくします。

4.Textが「空」に対応する m_Valueの値

DateBoxの場合、#1/1/1901#
NumBoxの場合、0
(ただし、m_value を先に0にした時のTextの表示は、m_Formatに依存)

5.m_text に、m_Valueを対応させる処理

m_text と m_valueは、キー入力で一致しなくなるので
Leave時(Lostfocus)とEnterキーの入力の時、対応させる。
ただし、利用者ができるだけ意識しないですむよう、
現在では使うことが少ないKeyDownとLostfocusで処理します。
(キー入力があればm_valueをNothingにしてFlgの代わりにます)    

Private Sub DateBox_Lostfocus(sender As Object, e As EventArgs) Handles MyBase.Lostfocus
    Dim s1 As String
    Dim L  as Integer

    s1 = trim(MyBase.Text)
    If IsNothing(m_Value) = False Then
        MyBase.Text = ConvValueToText(m_Value, m_Format)    
        Exit Sub
    End If
    if s1 = "" or s1 = "/  /" Then
        m_Value = #1/1/1901#
        MyBase.Text = "    /  /"
    ElseIf IsDateText(s1) = False Then
        m_Value = Nothing
    Else
        m_Value = ConvTextToValue(MyBase.Text)    ' Textが /m/d なら 0m/0d にする
    End If
End Sub
Public Function IsDateText(ByVal p_Text As String) As Boolean
    IsDateText = False
    Dim s1 As String

    If IsDate(p_Text) Then
        If IsNumeric(p_Text) = False Then Return True
    End If
    If IsNumeric(p_Text) Then
        If VB6.Len(p_Text) = 6 Then
	        s1 = VB6.Left(p_Text,2) & "/" & VB6.Mid(p_Text,3,2) & "/" & VB6.Mid(p_Text,5,2)
	        If IsDate(s1) Then Return True
        ElseIf VB6.Len(p_Text) = 8 And VB6.Left(p_Text,2) = "20" Then
	        s1 = VB6.Left(p_Text,4) & "/" & VB6.Mid(p_Text,5,2) & "/" & VB6.Mid(p_Text,7,2)
	        If IsDate(s1) Then Return True
        End If
    ElseIf VB6.Len(p_Text) = 7 And IsNumeric(VB6.Mid(p_Text,2,6) ) Then
	    s1 = VB6.Left(p_Text,3) & "/" & VB6.Mid(p_Text,4,2) & "/" & VB6.Mid(p_Text,6,2)
	    If IsDate(s1) Then Return True
    End If
End Function

6.VB6から移行する場合のFormat指定

安全性を考えて、Format指定は全く変えないことにします。
V6のFormatを意味するのプロパティ(VB6FormatFlg)を設け、
テキストへの変換 ConvValueToText(m_Value, m_Format)で
VB6.Format() とToString(Format指定)を切り替えます。
m_VB6FormatFlg の初期値は、Formatプロパティの内容で判定します。
(VB6FormatFlgプロパティを直接与えてもよい)

Imports VB6 = Microsoft.VisualBasic
      :
Public Overloads Property Format As String
    Get
        Return m_Format
    End Get
    Set(value As String)
        m_Format = value
        If IsNothing(m_cultur.DateTimeFormat.Calendar) Then m_cultur.DateTimeFormat.Calendar = New JapaneseCalendar()

        m_VB6FormatFlg = False
        If Instr(m_Format, "e")>0 Or Instr(m_Format, "m")>0 Or Instr(m_Format, "GGG")>0 Then
	        m_VB6FormatFlg = True
    	    Try ' Test
	            Dim s1 As String = VB6.Format(#2024/12/1#, m_Format)
	        Catch e As Exception
    	        m_Format &= "?"	
	        End Try 
        Else
    	    Try ' Test
	           Dim d1 As Date = #2024/12/1#
	           Dim s1 As String = d1.ToString(m_Format, m_cultur)
	        Catch e As Exception
    	        m_Format &= "?"	
	        End Try 
        End If
    End Set
End Property
Private Function ConvValueToText(ByRef p_DateVal As Object, ByRef p_Format As String) As String
    ConvValueToText = ""
    If IsNothing(p_DateVal) OrElse Instr(p_Format,"?")>0 Then
	    Exit Function
    End if
    If p_DateVal = #1/1/1901# Then
	    Return  "    /  /"
    End If
    Try
       If m_VB6FormatFlg Then
           ConvValueToText = VB6.Format(p_DateVal, p_Format)
       Else
           ConvValueToText = DirectCast(p_DateVal, Date).ToString(p_Format, m_cultur)
       End If
    Catch e As Exception
       ConvValueToText = p_DateVal.ToString()
    End Try 
End Function

いいなと思ったら応援しよう!