-
テキストボックスでの入力で、入力される文字を数字のみとしたい場合の方法を示します。
文字の入力を行うとテキストボックスの KeyPress イベントが発生します。 この時に入力された文字をチェックし、数字以外であればその文字を捨てる処理を行います。 尚、 KeyPress イベントでは BackSpaceキー入力の場合も発生しますので、 このキーは有効にする必要があります。KeyPress イベントで数字チェックを行う
Public Class frmTextBox Private Sub TextBox1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TextBox1.KeyPress 'キーが [0]~[9] または [BackSpace] 以外の場合イベントをキャンセル If Not (("0"c <= e.KeyChar And e.KeyChar <= "9"c) Or e.KeyChar = ControlChars.Back) Then 'コントロールの既定の処理を省略する場合は true e.Handled = True End If End Sub Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown '[Delete] キーを無効にする場合 If e.KeyCode = Keys.Delete Then e.Handled = True End If End Sub End ClassBackSpaceキーなどの制御用のキーは、 KeyPress イベントが発生しません。 そのため、KeyDown イベントでキーコードによりそれぞれの処理を行います。
(上のプログラムでは、Deleteキーを無効にしています。)
KeyPress イベントの処理でほぼ良いのですが、一つ問題があります。 それは、クリップボードからのコピペを行う場合にはKeyPress イベントが発生しないので、 対応を行わないと数字以外でも貼り付けられてしまいます。
テキストボックスのイベントには、クリップボードからの貼り付け時のイベントが在りませんので、 テキストボックスの大元のウインドウ・メッセージ処理の中でそれを行う必要があります。
普通にテキストボックスをフォームに張り付ける場合には、 ウインドウ・メッセージ処理を変更することはできません。 テキストボックスを継承したクラスの中でウインドウ・メッセージ処理を オーバーライドすることで可能になります。
以下にそのテキストボックスを拡張したクラスのソースを示します。KeyPress イベントで数字チェックを行う
'''
''' 数字とバックスペース以外の入力を無効にしたTextBox ''' Public Class ClsNumTextBox Inherits TextBox 'クリップボード貼付メッセージ Const WM_PASTE As Integer = &H302 '''''' WndProc メソッド (override) ''' ''' <param name="m">メッセージ</param> Protected Overrides Sub WndProc(ByRef m As Message) Select Case m.Msg Case WM_PASTE Dim iDataObj As IDataObject = Clipboard.GetDataObject() 'クリップボードの中身があるか、かつ文字列に変換できるか?? If (iDataObj IsNot Nothing) And (iDataObj.GetDataPresent(DataFormats.Text) = True) Then '文字列に変換 Dim strClip As String = DirectCast(iDataObj.GetData(DataFormats.Text), String) '文字列が数字のみか調べる If Me.ChkClipString(strClip) = False Then 'エラーの場合はここで抜ける Return End If End If End Select 'メッセージのデフォルト処理を呼出す MyBase.WndProc(m) End Sub '''''' コンストラクタ ''' Public Sub New() MyBase.New() 'IMEを無効にする MyBase.ImeMode = ImeMode.Disable End Sub '''''' KeyPressイベントの処理 ''' Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs) MyBase.OnKeyPress(e) 'キーが [0]~[9] または [BackSpace] 以外の場合イベントをキャンセル If Not (("0"c <= e.KeyChar And e.KeyChar <= "9"c) Or e.KeyChar = ControlChars.Back) Then e.Handled = True End If End Sub '''''' 文字列のチェック関数 ''' ''' <param name="strClip">指定文字列</param> '''True:OK, False:NG Private Function ChkClipString(ByVal strClip As String) As Boolean ChkClipString = False If System.Text.RegularExpressions.Regex.IsMatch(strClip, "^[0-9]+$") Then Return True End If End Function End Classこのソースを別のファイルで作成し、一度コンパイルします。 そうすると、ツールボックスの中に ClsNumTextBox が現れますので、 以下の様にこれをフォーム上に貼り付けます。
結果これを実行しますと、以下の様になります。 クリップボードからの貼り付けで、数字以外は表示されないはずです。
関連する記事
⇒テキストボックスの入力を数字のみにする方法その2(電卓の様な入力)
⇒テキストボックス拡張クラスにプロパティを追加する方法
⇒フォーム上のコントロールで[Enter]キー押下で次のコントロールにフォーカス移動する
⇒フォーム上のコントロールのイベント処理の一括関連付け:[AddHandler,DirectCast]
⇒コントロールの同じイベント処理に複数の関連付けをテスト:[AddHandler,DirectCast]
⇒テキストボックスの Leave イベントでのエラー処理でフォーカス強制移動する方法について
PR -
VB.NETの場合、フォームを表示する場合にはプロジェクトプロパティの中の設定でスタートアップオブジェクトを 対象となるフォームを選択して実行すれば、そのフォームが最初に表示されます。 以下の図は、スタートアップオブジェクトをForm1に設定したところを示します。
簡単なプログラムであればこの方法で良いのですが、いきなりフォームが表示されては困ることもあります。 フォームが表示される前に行いたい処理がいろいろあったりします。 そこで、プログラムの開始位置であるエントリポイントを変更することで、 フォームの表示前に処理が出来る様になります。
以下のソースを見て下さい。Main関数を定義したモジュールファイルを作成します。 先ほどのスタートアップオブジェクトをこのMain関数に設定することで プログラムのエントリポイントがMain関数になります。 つまり、プログラムはMain関数から開始されることになります。エントリポイントをSub Mainにする
Module MdlMain <STAThread()> _ Sub Main() '===== '初期処理... '===== 'フォーム表示 Dim frmForm1 As New Form1 Application.Run(frmForm1) End Sub End Moduleもしフォーム表示前の処理を行いたい場合には、Application.Runの前までに記述します。 私が組んだプログラムでは、以下の様な処理を行ったりしました。
- プログラムの二重起動を抑制する。
- プログラム起動の引数の取得とチェックを行う。
- ログ出力の初期処理を行う。
- データベースのオープン処理を行い、エラーの場合は処理を中断する。
尚、以下の図はプロジェクトプロパティの中の設定でスタートアップオブジェクトを Sub Mainに設定したところを示します。
VB.NETでは、Mainメソッド(またはプロシージャ)がエントリポイントです。ところが、Windowsフォームアプリケーションプロジェクトのどこを探してもMainメソッドがありません。エントリポイントとなるMainメソッドは、コンパイラによって自動的に作成されます。 補足:Visual Studio 2005以降では、「アプリケーションフレームワークを有効にする」を無効にしないと、本当のエントリポイントを変更することはできません。「アプリケーションフレームワークを有効にする」を有効にしたときのエントリポイントは、MyApplicationクラスのMainメソッドとなるようです。
モジュールでファイルではなく、クラスファイルに以下の様にShared Sub Main関数を宣言しても同じことができます。エントリポイントをクラスのSub Mainにする
Public Class ClsMain <STAThread()> _ Shared Sub Main() Application.Run(New Form1) End Sub End Class関連する記事
⇒フォームの位置設定を行う処理 :[Screen.PrimaryScreen.WorkingArea]
⇒フォームクラスにプロパティ宣言し外からアクセスする方法
⇒親フォームのコントロールを子フォームからアクセスする方法
⇒フォームが閉じるのをキャンセルする方法:[FormClosingEventArgs,Cancel]
⇒フォームの表示をモジュールのMain関数から行う(エントリポイントの変更)
⇒フォームがKeyDownなどのキーイベントを受取り、ファンクションキー処理をする
⇒最初のフォームActivatedイベントでの処理(1回のみしか処理しない様にする)
⇒初回のフォームActivatedイベント発生させるフォーム継承について
-
指定されたバイト配列を、指定値で埋める関数です。バイト配列などは通常は使用しないのですが、制御関係などでは使用することもあり、 初期値として、バイト配列を設定する必要があります。
関数として以下に2種類の関数を例に示していますが、最初は単純に配列1個ずつに指定バイト値を設定しているだけです。 関数にするほどでもないのですが、この関数を元にしてInteger型の配列やDouble型の配列の初期化用の関数をオーバーライドすることができます。
2つ目の関数は、System.Linq の Enumerable を用いた方法を示します。 Enumerable.Repeat で IEnumerable なオブジェクトを生成し、更にそれをArray化して、コピーを行っています。 (こちらの方が、見やすいかもしれません。)バイト配列を設定バイト値で埋める
''' ----------------------------------------------------------------------------- '''''' バイト配列を設定値で埋める ''' ''' <param name="desArr">対象バイト配列</param> ''' <param name="fillByte">バイト値</param> '''単純に配列を1個ずつ処理 ''' ----------------------------------------------------------------------------- Public Sub FillByteArr(ByVal desArr As Byte(), ByVal fillByte As Byte) If desArr.Length > 0 Then Dim idx As Integer For idx = 0 To desArr.Length - 1 desArr(idx) = fillByte Next End If End Sub ''' ----------------------------------------------------------------------------- '''''' バイト配列を設定値で埋める・その2 ''' ''' <param name="desArr">対象バイト配列</param> ''' <param name="fillByte">バイト値</param> '''System.Linq の Enumerable を用いた方法 ''' ----------------------------------------------------------------------------- Public Sub FillByteArr2(ByVal desArr As Byte(), ByVal fillByte As Byte) If desArr.Length > 0 Then Dim arr As IEnumerable(Of Byte) = Enumerable.Repeat(fillByte, desArr.Length) Array.Copy(arr.ToArray(), desArr, desArr.Length) End If End Sub '***** '仮にテストプログラム '***** Private Sub FillByte_Click(sender As Object, e As EventArgs) Handles FillByte.Click Try '配列宣言 Dim byteArr(10) As Byte '配列を埋める FillByteArr2(byteArr, &H55) '結果の表示 Dim str As String = "" For i = 0 To byteArr.Length - 1 str &= String.Format("byteArr[{0}] = {1}", i, byteArr(i).ToString) & vbCrLf Next MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub関連する記事
⇒左詰でバイト配列のコピーを行う
⇒右詰でバイト配列のコピーを行う
-
データ型の中で 日付型(Date) はよく使用するもののひとつです。 VB.NETの中に日付のデータ型として、 Date と DateTime がありますが、 どの様な違いがあるのか疑問に思われるでしょうが、以下のMSDNの説明によれば同じものの様です。
⇒データ型の概要 (Visual Basic)
日付型として同じであれば、どちらを使ってもよいのですが、プログラムの中では一つに統一した方が良いと思います。 (私としては DateTime で良いと思います)
以下のソースは抜粋ですが、どちらも同じことをを処理しています。日付型データの使い方1
Dim strWK As String = "" Dim dateWK As Date 'Date型 dateWK = New Date(2018, 1, 20, 15, 17, 30) strWK = dateWK.ToString("yyyy/MM/dd HH:mm:ss") '日付型を書式で変換 Console.WriteLine("dateWK.ToString(""yyyy/MM/dd HH:mm:ss"") = " & strWK) 'DateTime型 Dim dateTimeWK As DateTime dateTimeWK = New DateTime(2018, 1, 20, 15, 17, 30) strWK = dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss") Console.WriteLine("dateTimeWK.ToString(""yyyy/MM/dd HH:mm:ss"") = " & strWK)ソースの中で、ToString を使用して日付型データから書式に従って文字列に変換しています。 今回の例では書式を"yyyy/MM/dd HH:mm:ss"を使っていますが、通常はこの様な感じで十分かと思います。
尚、この書式には各種ありますが代表的なものを以下に示します。書式指定子 説明 使用例 yy 0埋め下2桁の年 (00 ~ 99) 2009-06-01 13:45:30 ⇒ "09" yyyy 0埋め4桁の年 (0001 ~ 9999) 2009-06-01 13:45:30 ⇒ "2009" M 月 (1 ~ 12) 2009-06-01 13:45:30 ⇒ "6" MM 月:1桁の場合先頭0付 (01 ~ 12) 2009-06-01 13:45:30 ⇒ "06" d 月の日にち (1 ~ 31) 2009-06-01 13:45:30 ⇒ "1" dd 月の日にち:1桁の場合先頭0付 (01 ~ 31) 2009-06-01 13:45:30 ⇒ "01" h 12時間形式の時間 (1 ~ 12) 2009-06-01 13:45:30 ⇒ "1" hh 12時間形式の時間:1桁の場合先頭0付 (01 ~ 12) 2009-06-01 13:45:30 ⇒ "1" H 24時間形式の時間 (0 ~ 23) 2009-06-01 03:45:30 ⇒ "3" HH 24時間形式の時間:1桁の場合先頭0付 (00 ~ 23) 2009-06-01 13:45:30 ⇒ "13" mm 分:1桁の場合先頭0付 (00 ~ 59) 2009-06-01 13:05:30 ⇒ "05" ss 秒:1桁の場合先頭0付 (00 ~ 59) 2009-06-01 13:05:04 ⇒ "04" ddd 曜日の省略名 2009-06-01 13:45:30 ⇒ "月" dddd 曜日の完全名 2009-06-01 13:45:30 ⇒ "月曜日"
次に日付型の計算について説明します。 いろんなところで日付に対して翌日、前日、前月、翌月、前年、翌年などの計算が必要になりますが、 日付型のメソッドとしてそれらを計算できるものが揃っています。 私がよく使うものを以下に示します。
メソッド 書式 説明 AddDays AddDays (value As Double) As DateTime
value 引数は、正または負のどちらの場合もあります指定された日数を加算した新しい DateTime を返します AddMonths AddMonths (value As Integer) As DateTime
value 引数は、正または負のどちらの場合もあります指定された月数を加算した新しい DateTime を返します AddYears AddYears (value As Integer) As DateTime
value 引数は、正または負のどちらの場合もあります指定された年数を加算した新しい DateTime を返します
日付型データの使い方2
'日付の設定 dateTimeWK = New DateTime(2018, 1, 25) '日数+1 dateTimeWK = dateTimeWK.AddDays(1) Console.WriteLine("日数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '日数-1 dateTimeWK = dateTimeWK.AddDays(-1) Console.WriteLine("日数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '月数+1 dateTimeWK = dateTimeWK.AddMonths(1) Console.WriteLine("月数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '月数-1 dateTimeWK = dateTimeWK.AddMonths(-1) Console.WriteLine("月数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '年数+1 dateTimeWK = dateTimeWK.AddYears(1) Console.WriteLine("年数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '年数-1 dateTimeWK = dateTimeWK.AddYears(-1) Console.WriteLine("年数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss"))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK.ToString("yyyy/MM/dd HH:mm:ss") = 2018/01/20 15:17:30 dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss") = 2018/01/20 15:17:30 日数+1 = 2018/01/26 00:00:00 日数-1 = 2018/01/25 00:00:00 月数+1 = 2018/02/25 00:00:00 月数-1 = 2018/01/25 00:00:00 年数+1 = 2019/01/25 00:00:00 年数-1 = 2018/01/25 00:00:00
関連する記事
⇒日付型データの使い方(月末、年末の日付取得)
⇒日付型データの比較について
⇒文字列から数値型への変換(parse - tryparse)
⇒オブジェクト型から数値型への変換(TryParse)
-
スレッドタイマとしては以下のページに説明をしましたが、
⇒スレッドタイマの使い方
この時のタイマイベントの処理は、単に時刻を表示するだけの処理で時間がほとんど掛からないものでした。 しかし、実際のプログラムではそのようなことはなく、処理時間が結構かかるものが在ると思います。
そこで今回は、タイマイベントの処理の中に時間が掛かる処理のシミュレーションとして Sleep関数で3秒待たせることにしました。 この関数の呼び出しの前後で時刻表示を入れましたので、何時の時点での処理なのかが分かります。 実際のプログラムは以下の様になります。スレッドタイマの使い方(イベント処理が時間が掛かる場合)
Imports System.Threading Imports System.Threading.Thread Public Class frmTimerTh2 'スレッドタイマ Private mtimer As System.Threading.Timer '''''' フォームロードイベント ''' Private Sub frmTimerThMul_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) 'テキストボックスに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) '3秒の待ち Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒Start...5秒の待ち"}) Sleep(3 * 1000) Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒End.....5秒の待ち"}) End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Me.TextBox1.Text &= strText & vbCrLf End Sub End Classこれを実行させると、以下の様な表示なります。
最初、4個の「Start...5秒の待ち」が続いてその後で「End.....5秒の待ち」が追いかけてくる感じになっています。
これは、スレッドタイマのイベントの時間間隔が1秒なので、先に発生したイベントを待たずに 次々とイベントが発生させていることが分かります。 つまり、各1秒毎のイベントは別々に実行していることになります。
3秒待ちの処理が重なって処理されては困ることになります。 そこで、以下のプログラムの様に、イベント処理中のフラグを用いて処理をスキップさせてみました。スレッドタイマの使い方(イベント処理が時間が掛かる場合:改良版)
Imports System.Threading Imports System.Threading.Thread Public Class frmTimerTh2 'スレッドタイマ Private mtimer As System.Threading.Timer '''''' フォームロードイベント ''' Private Sub frmTimerThMul_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) '処理中フラグ Static fProcessing As Boolean = False '処理中フラグOFFのチェック If fProcessing = False Then '処理中フラグON fProcessing = True 'テキストボックスに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) '3秒の待ち Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒Start...5秒の待ち"}) Sleep(3 * 1000) Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒End.....5秒の待ち"}) '処理中フラグOFF fProcessing = False End If End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Me.TextBox1.Text &= strText & vbCrLf End Sub End Classこのプログラムの実行は以下の様になり、イベントの多重呼び出しは無くなりました。
関連する記事
⇒Timerコントロールの使い方 :[Timer,Interval]
⇒複数のTimerコントロールの使い方 :[Timer,Interval]
⇒スレッドタイマの使い方 :[System.Threading.Timer,Delegate,Invoke]