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 TextBox
:
Sub 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