WPFで数字しか入力できないテキストボックスを作成する

WPFでテキストボックスを数字しか入力できないようにする方法を紹介します。

まず数字以外の文字が入力されたときに無視するためにPreviewTextInputイベントをつかいます

private void PreviewTextInput(object sender,TextCompositionEventArgs e)
{
	int result = 0;
	if (int.TryParse(e.Text, out result))
	{}
	else
	{
		e.Handled = true;
	}
}

デフォルトではなぜか半角スペースがPreviewTextInputに引っかからず入力できてしまうのですが、
実はスペースキー(及びシフトを押しながらスペースキー)を押したときの処理はEditingCommands.Space,(EditingCommands.ShiftSpace)という
internalな非公開の定義済みコマンドで行われています。CommandManager.PreviewExecutedイベントで存在を確認することができます。

InputBindingsでこれらのキー入力で呼び出すコマンドをApplicationCommands.NotACommandに置き換え、無効化すると
半角スペースの入力がPreviewTextInputイベントで拾えるようになります。

textbox.InputBindings.Add(new KeyBinding(ApplicationCommands.NotACommand, Key.Space, ModifierKeys.None));
textbox.InputBindings.Add(new KeyBinding(ApplicationCommands.NotACommand, Key.Space, ModifierKeys.Shift));

IME有効時の入力や、確定済み文字列の再変換ではPreviewTextInputイベントより先にTextChangedイベントが発生してしまいます。
やろうと思えば対処することもできるかもしれませんが、面倒くさいので
IMEモードを無効にします。

InputMethod.SetIsInputMethodEnabled(textbox, false);

入力された文字列を選択した状態でコンテキストメニューを開くと再変換の候補が表示されるため、
選択すると数字以外の文字に変換されてしまう可能性があります。

コンテキストメニュー周りはAppキー(キーボード右下あたりのハンバーガーメニューマークが印字されてるキー)や
Shift+F10ショートカットではApplicationCommands.ContextMenuが発生するが右クリックでは発生しない
ContextMenuOpeningイベントも発生したりしなかったりと動作が不安定なため、
textbox.ContextMenunullを代入してコンテキストメニュー自体を無効化します。

textbox.ContextMenu = null;

最後にクリックボードからの貼り付け(ペースト)対策です。
こちらはCommandBindingを使って、ApplicationCommands.Pasteの中身を書き換え
クリックボードの中身が数字の時のみペーストするようにします。

textbox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, ExecutePaste));
private void ExecutePaste(object sender, ExecutedRoutedEventArgs e)
{
	TextBox textbox = (TextBox)sender;
	string text = Clipboard.GetText();

	int result = 0;
	if (int.TryParse(text, out result))
	{
		textbox.Paste();
	}
	else
	{
	}
}

これで、おそらく数字しか入力できなくなったはずです。

Styleなどでうまく使いまわせるようにしたかったのですが。
InputBindingsとCommandBindingsの設定がStyleでは難しそうだったので
Expression Blend SDKをつかってビヘイビアにしてみました。

[TypeConstraint(typeof(TextBox))]
public class NumericTextBoxBehavior : Behavior<TextBox>
{
	private KeyBinding SpaceKeyBinding { get; set; }

	private KeyBinding ShiftSpaceKeyBinding { get; set; }

	private CommandBinding PasteCommandBinding { get; set; }

	protected override void OnAttached()
	{

		this.AssociatedObject.ContextMenu = null;
		InputMethod.SetIsInputMethodEnabled(this.AssociatedObject, false);

		this.AssociatedObject.PreviewTextInput += PreviewTextInput;

		this.SpaceKeyBinding = new KeyBinding(ApplicationCommands.NotACommand, Key.Space, ModifierKeys.None);
		this.ShiftSpaceKeyBinding = new KeyBinding(ApplicationCommands.NotACommand, Key.Space, ModifierKeys.Shift);
		this.PasteCommandBinding = new CommandBinding(ApplicationCommands.Paste, ExecutePaste);

		AssociatedObject.InputBindings.Add(SpaceKeyBinding);
		AssociatedObject.InputBindings.Add(ShiftSpaceKeyBinding);
		AssociatedObject.CommandBindings.Add(PasteCommandBinding);

		base.OnAttached();
	}

	protected override void OnDetaching()
	{
		base.OnDetaching();

		AssociatedObject.InputBindings.Remove(SpaceKeyBinding);
		AssociatedObject.InputBindings.Remove(ShiftSpaceKeyBinding);
		AssociatedObject.CommandBindings.Remove(PasteCommandBinding);

		this.SpaceKeyBinding = null;
		this.ShiftSpaceKeyBinding = null;
		this.PasteCommandBinding = null;

		this.AssociatedObject.PreviewTextInput -= PreviewTextInput;
	}

	private void ExecutePaste(object sender, ExecutedRoutedEventArgs e)
	{
		TextBox textbox = (TextBox)sender;
		string text = Clipboard.GetText();

		int result = 0;
		if (int.TryParse(text, out result))
		{
		textbox.Paste();
		}
		else
		{
		}
	}

	private void PreviewTextInput(object sender, TextCompositionEventArgs e)
	{
		int result = 0;
		if (int.TryParse(e.Text, out result))
		{
		}
		else
		{
			e.Handled = true;
		}
	}
}

akatukisiden について

フリーランスプログラマ 使用言語はC++,C#とそのつなぎのC++/CLI 専門はクライアントアプリ開発
カテゴリー: C#, MVVM, XAML環境 タグ: , , , , , , , , , , , , , , , , , , , パーマリンク

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中