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.ContextMenuにnullを代入してコンテキストメニュー自体を無効化します。
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; } } }
いちいちここまでせなならんのか…
一体何人の人が同じような発明をしてるんだろう