言語フィルタ:
概要
テキストボックスにテキスト変更中イベントを作成する方法を紹介します。
このイベントを利用すると、入力した文字が描画される前にキャンセルできるようになります。数字のみ受け付けるようにするなど、入力制限をする場合に使用します。
対象コントロール
- System.Windows.Forms.TextBox
解説
テキスト変更中のイベントで使用する TextChangingEventArgs クラスを作成します。このクラスは CancelEventArgs クラスを継承することで Cancel プロパティが使えるようになります。また、変更後のテキストを示す NewText プロパティを追加します。
Public Class TextChangingEventArgs
Inherits System.ComponentModel.CancelEventArgs
Public Sub New()
Me._NewText = String.Empty
End Sub
Public Sub New(ByVal text As String)
Me._NewText = text
End Sub
Private _NewText As String
Public ReadOnly Property NewText() As String
Get
Return Me._NewText
End Get
End Property
End Class
class TextChangingEventArgs : System.ComponentModel.CancelEventArgs
{
public TextChangingEventArgs()
{
this._NewText = string.Empty;
}
public TextChangingEventArgs(string text)
{
this._NewText = text;
}
private string _NewText;
public string NewText
{
get { return this._NewText; }
}
}
テキスト変更中を示すイベントを TextChanging イベントとして定義します。このイベントをテキストが変更される直前に発生させます。変更内容をキャンセルする場合は TextChangingEventArgs クラスの Cancel プロパティに True を設定します。
Public Event TextChanging(ByVal sender As Object, ByVal e As TextChangingEventArgs)
Protected Overridable Sub OnTextChanging(ByVal e As TextChangingEventArgs)
RaiseEvent TextChanging(Me, e)
End Sub
public delegate void TextChangingEventHandler(object sender, TextChangingEventArgs e);
public event TextChangingEventHandler TextChanging;
protected virtual void OnTextChanging(TextChangingEventArgs e)
{
if (TextChanging != null)
{
TextChanging(this, e);
}
}
テキストが変更される直前のタイミングは、次のメッセージが送られてきた時です。WM_CHAR, WM_PASTE, WM_KEYDOWN。各メッセージを処理するために WndProc メソッドをオーバーライドします。
WM_CHAR メッセージは文字を入力した時に送られてきます。WM_CHAR メッセージの WParam に入力した文字の文字コードが入っています。この文字コードから擬似的に入力後のテキストを作成し、それを TextChangingEventArgs クラスの NewText プロパティに設定し TextChanging イベントを発生させます。
TextChanging イベントの受信側で TextChangingEventArgs クラスの Cancel プロパティに True を設定した場合は、基本クラスの WndProc メソッドを呼ばないことで、入力された文字をキャンセルすることが出来ます。
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_CHAR As Integer = &H102
Select Case m.Msg
Case WM_CHAR
Dim e As New TextChangingEventArgs(Me.CreateNewText(ChrW(m.WParam.ToInt32)))
Me.OnTextChanging(e)
If e.Cancel = True Then
Return
End If
End Select
MyBase.WndProc(m)
End Sub
Private Function CreateNewText(ByVal inputText As String) As String
Return Me.CreateNewText(inputText, False)
End Function
Private Function CreateNewText(ByVal inputText As String, ByVal deleteKey As Boolean) As String
Dim newText As String = Me.Text
newText = newText.Remove(Me.SelectionStart, Me.SelectionLength)
' Delete キーを入力
If deleteKey Then
If Me.SelectionLength = 0 AndAlso Me.SelectionStart < Me.TextLength Then
newText = newText.Remove(Me.SelectionStart, 1)
End If
Return newText
End If
If Me.Multiline = False Then
Dim crIndex As Integer = inputText.IndexOf(ControlChars.Cr)
If crIndex > 0 Then
inputText = inputText.Remove(crIndex)
End If
Dim lrIndex As Integer = inputText.IndexOf(ControlChars.Lf)
If lrIndex > 0 Then
inputText = inputText.Remove(lrIndex)
End If
End If
If inputText = String.Empty Then
Return newText
End If
' BackSpace キーを入力
If Convert.ToInt32(inputText.Chars(0)) = Keys.Back Then
If Me.SelectionLength = 0 AndAlso Me.SelectionStart > 0 Then
newText = newText.Remove(Me.SelectionStart - 1, 1)
End If
Else
newText = newText.Insert(Me.SelectionStart, inputText)
End If
Return newText
End Function
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_CHAR = 0x102;
switch (m.Msg)
{
case WM_CHAR:
{
TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(Convert.ToString((char)(m.WParam.ToInt32()))));
this.OnTextChanging(e);
if (e.Cancel == true)
{
return;
}
}
break;
}
base.WndProc(ref m);
}
private string CreateNewText(string inputText)
{
return this.CreateNewText(inputText, false);
}
private string CreateNewText(string inputText, bool deleteKey)
{
string newText = this.Text;
newText = newText.Remove(this.SelectionStart, this.SelectionLength);
// Delete キーを入力
if (deleteKey)
{
if (this.SelectionLength == 0 && this.SelectionStart < this.TextLength)
{
newText = newText.Remove(this.SelectionStart, 1);
}
return newText;
}
if (this.Multiline == false)
{
int crIndex = inputText.IndexOf("\r");
if (crIndex > 0)
{
inputText = inputText.Remove(crIndex);
}
int lrIndex = inputText.IndexOf("\n");
if (lrIndex > 0)
{
inputText = inputText.Remove(lrIndex);
}
}
if (inputText == string.Empty)
{
return newText;
}
// BackSpace キーを入力
if (Convert.ToInt32(inputText[0]) == (int)Keys.Back)
{
if (this.SelectionLength == 0 && this.SelectionStart > 0)
{
newText = newText.Remove(this.SelectionStart - 1, 1);
}
}
else
{
newText = newText.Insert(this.SelectionStart, inputText);
}
return newText;
}
WM_PASTE メッセージは貼り付け操作をした時に送られてきます。WM_PASTE メッセージには貼り付けた文字列が含まれていないため、クリップボードからテキストを取得します。後は WM_CHAR メッセージの処理と同様に、擬似的に貼り付け後のテキストを作成し TextChanging イベントを発生させます。
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_PASTE As Integer = &H302
Select Case m.Msg
Case WM_PASTE
Dim clipText As String = CType(Clipboard.GetDataObject().GetData(DataFormats.Text), String)
If clipText IsNot Nothing Then
Dim e As New TextChangingEventArgs(Me.CreateNewText(clipText))
Me.OnTextChanging(e)
If e.Cancel = True Then
Return
End If
End If
End Select
MyBase.WndProc(m)
End Sub
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_PASTE = 0x302;
switch (m.Msg)
{
case WM_PASTE:
string clipText = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
if (clipText != null)
{
TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(clipText));
this.OnTextChanging(e);
if (e.Cancel == true)
{
return;
}
}
break;
}
base.WndProc(ref m);
}
WM_KEYDOWN メッセージはキーを押した時に送られてきます。テキストを Delete キーで削除した時だけは WM_CHAR メッセージが送られてきません。そのため Delete キーを入力されたタイミングだけを WM_KEYDOWN メッセージで処理をします。
WM_KEYDOWN メッセージの WParam に Delete キーを示す値 (46) が入っている時が Delete キーを入力した時です。後は WM_CHAR メッセージの処理と同様に、擬似的に Delete キーを入力後のテキストを作成し TextChanging イベントを発生させます。WM_KEYDOWN メッセージで Delete キーの入力をキャンセルする場合はWM_KEYDOWN メッセージ の Result プロパティに True (1) を設定します。
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_KEYDOWN As Integer = &H100
Select Case m.Msg
Case WM_KEYDOWN
If m.WParam.ToInt32 = Keys.Delete Then
Dim e As New TextChangingEventArgs(Me.CreateNewText(String.Empty, True))
Me.OnTextChanging(e)
If e.Cancel = True Then
m.Result = New IntPtr(1) ' True
Return
End If
End If
End Select
MyBase.WndProc(m)
End Sub
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_KEYDOWN = 0x100;
switch (m.Msg)
{
case WM_KEYDOWN:
if (m.WParam.ToInt32() == (int)Keys.Delete)
{
TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(string.Empty, true));
this.OnTextChanging(e);
if (e.Cancel == true)
{
m.Result = new IntPtr(1); // true
return;
}
}
break;
}
base.WndProc(ref m);
}
ソースコード
TextBox コントロールにテキストの変更中イベントを作成するサンプルを紹介します。
ソースコードは解説で紹介した内容をまとめたものになっています。
TextChanging イベントの利用例では数字以外の文字を受け付けないようにしています。
Imports System
Imports System.ComponentModel
Imports System.Windows.Forms
Imports Microsoft.VisualBasic
Namespace Extentions
Public Class ChangingTextBox
Inherits System.Windows.Forms.TextBox
<Category("プロパティ変更")> _
<Description("Text プロパティの値がコントロールで変更される直前に発生します。")> _
Public Event TextChanging(ByVal sender As Object, ByVal e As TextChangingEventArgs)
Protected Overridable Sub OnTextChanging(ByVal e As TextChangingEventArgs)
RaiseEvent TextChanging(Me, e)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_KEYDOWN As Integer = &H100
Const WM_CHAR As Integer = &H102
Const WM_PASTE As Integer = &H302
Select Case m.Msg
Case WM_KEYDOWN
If m.WParam.ToInt32 = Keys.Delete Then
Dim e As New TextChangingEventArgs(Me.CreateNewText(String.Empty, True))
Me.OnTextChanging(e)
If e.Cancel = True Then
m.Result = New IntPtr(1) ' True
Return
End If
End If
Case WM_CHAR
Dim e As New TextChangingEventArgs(Me.CreateNewText(ChrW(m.WParam.ToInt32)))
Me.OnTextChanging(e)
If e.Cancel = True Then
Return
End If
Case WM_PASTE
Dim clipText As String = CType(Clipboard.GetDataObject().GetData(DataFormats.Text), String)
If clipText IsNot Nothing Then
Dim e As New TextChangingEventArgs(Me.CreateNewText(clipText))
Me.OnTextChanging(e)
If e.Cancel = True Then
Return
End If
End If
End Select
MyBase.WndProc(m)
End Sub
Private Function CreateNewText(ByVal inputText As String) As String
Return Me.CreateNewText(inputText, False)
End Function
Private Function CreateNewText(ByVal inputText As String, ByVal deleteKey As Boolean) As String
Dim newText As String = Me.Text
newText = newText.Remove(Me.SelectionStart, Me.SelectionLength)
' Delete キーを入力
If deleteKey Then
If Me.SelectionLength = 0 AndAlso Me.SelectionStart < Me.TextLength Then
newText = newText.Remove(Me.SelectionStart, 1)
End If
Return newText
End If
If Me.Multiline = False Then
Dim crIndex As Integer = inputText.IndexOf(ControlChars.Cr)
If crIndex > 0 Then
inputText = inputText.Remove(crIndex)
End If
Dim lrIndex As Integer = inputText.IndexOf(ControlChars.Lf)
If lrIndex > 0 Then
inputText = inputText.Remove(lrIndex)
End If
End If
If inputText = String.Empty Then
Return newText
End If
' BackSpace キーを入力
If Convert.ToInt32(inputText.Chars(0)) = Keys.Back Then
If Me.SelectionLength = 0 AndAlso Me.SelectionStart > 0 Then
newText = newText.Remove(Me.SelectionStart - 1, 1)
End If
Else
newText = newText.Insert(Me.SelectionStart, inputText)
End If
Return newText
End Function
End Class
End Namespace
Namespace Extentions
Public Class TextChangingEventArgs
Inherits System.ComponentModel.CancelEventArgs
Public Sub New()
Me._NewText = String.Empty
End Sub
Public Sub New(ByVal text As String)
Me._NewText = text
End Sub
Private _NewText As String
Public ReadOnly Property NewText() As String
Get
Return Me._NewText
End Get
End Property
End Class
End Namespace
Private Sub ChangingTextBox1_TextChanging(ByVal sender As System.Object, ByVal e As Extentions.TextChangingEventArgs) Handles ChangingTextBox1.TextChanging
Dim r As New System.Text.RegularExpressions.Regex("[^0-9]")
If r.Match(e.NewText).Success = True Then
e.Cancel = True
End If
End Sub
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace Extentions
{
class ChangingTextBox : System.Windows.Forms.TextBox
{
public delegate void TextChangingEventHandler(object sender, TextChangingEventArgs e);
[Category("プロパティ変更")]
[Description("Text プロパティの値がコントロールで変更される直前に発生します。")]
public event TextChangingEventHandler TextChanging;
protected virtual void OnTextChanging(TextChangingEventArgs e)
{
if (TextChanging != null)
{
TextChanging(this, e);
}
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_KEYDOWN = 0x100;
const int WM_CHAR = 0x102;
const int WM_PASTE = 0x302;
switch (m.Msg)
{
case WM_KEYDOWN:
if (m.WParam.ToInt32() == (int)Keys.Delete)
{
TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(string.Empty, true));
this.OnTextChanging(e);
if (e.Cancel == true)
{
m.Result = new IntPtr(1); // true
return;
}
}
break;
case WM_CHAR:
{
TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(Convert.ToString((char)(m.WParam.ToInt32()))));
this.OnTextChanging(e);
if (e.Cancel == true)
{
return;
}
}
break;
case WM_PASTE:
string clipText = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
if (clipText != null)
{
TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(clipText));
this.OnTextChanging(e);
if (e.Cancel == true)
{
return;
}
}
break;
}
base.WndProc(ref m);
}
private string CreateNewText(string inputText)
{
return this.CreateNewText(inputText, false);
}
private string CreateNewText(string inputText, bool deleteKey)
{
string newText = this.Text;
newText = newText.Remove(this.SelectionStart, this.SelectionLength);
// Delete キーを入力
if (deleteKey)
{
if (this.SelectionLength == 0 && this.SelectionStart < this.TextLength)
{
newText = newText.Remove(this.SelectionStart, 1);
}
return newText;
}
if (this.Multiline == false)
{
int crIndex = inputText.IndexOf("\r");
if (crIndex > 0)
{
inputText = inputText.Remove(crIndex);
}
int lrIndex = inputText.IndexOf("\n");
if (lrIndex > 0)
{
inputText = inputText.Remove(lrIndex);
}
}
if (inputText == string.Empty)
{
return newText;
}
// BackSpace キーを入力
if (Convert.ToInt32(inputText[0]) == (int)Keys.Back)
{
if (this.SelectionLength == 0 && this.SelectionStart > 0)
{
newText = newText.Remove(this.SelectionStart - 1, 1);
}
}
else
{
newText = newText.Insert(this.SelectionStart, inputText);
}
return newText;
}
}
}
namespace Extentions
{
class TextChangingEventArgs : System.ComponentModel.CancelEventArgs
{
public TextChangingEventArgs()
{
this._NewText = string.Empty;
}
public TextChangingEventArgs(string text)
{
this._NewText = text;
}
private string _NewText;
public string NewText
{
get { return this._NewText; }
}
}
}
private void changingTextBox1_TextChanging(object sender, Extentions.TextChangingEventArgs e)
{
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("[^0-9]");
if (r.Match(e.NewText).Success == true)
{
e.Cancel = true;
}
}