【WPF】Enterキーで確定できるTextBoxにする添付プロパティ

C#

こんにちは、働くC#プログラマーのさんさめです。

WPFのTextBoxには、
1つ、とても直感的じゃない挙動があります。

Enterキーを押してもバインディング先に変更通知が飛ばない
のです。

これを知らずにTextBoxを利用していると、
知らない間に使い勝手が悪い認定を
されてしまったりします。

さんさめ
さんさめ

「Enter押したのに反応しなかったんだけど!」
って必ず言われます。対策必須です。

逆に言えば、Enterで確定できるようにしておけば、
「直感的に使えるツールだなぁ」
という評価に一歩前進できます。

本記事では、TextBoxでEnterキーを押したときに、
バインディングを解決させる方法について解説します。

スポンサーリンク

TextBoxがEnterキーを押しても無反応な理由

MVVMでTextBoxを使うのであれば、

<TextBox Text="{Binding Name}"/>

と書きますよね。

これ自体は何も間違っていないのですが、
これでは、TextBoxがバインディング先に変更通知を飛ばすのは、
Tabキーやマウス操作などによってフォーカスを失ったときです。
Enterキーではフォーカスが失われないため反応しません

実際に挙動を見てみましょう。

ボタンを押したりTabキーでフォーカスを移動させたりすると変更が反映される

Enterキーには無反応です。

この変更通知のタイミングは、
UpdateSourceTriggerプロパティによって変えられます。

たとえば、以下のように設定すると
文字を打つたびに変更通知が行われます。

<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>

使い方によっては十分ですが、
これでは逆に変更通知が飛び過ぎで困ることもあります。

そこで、添付プロパティを活用して、
Enterキー押下時に変更通知を飛ばすようにしてみましょう。

添付プロパティを使ってEnterで確定できるTextBoxにする

以下のようなクラスを作ります。

やっていることは、以下の通りです。

  1. 動作モードであるenumの定義
  2. enumを添付プロパティとして設定するための定義
  3. 添付プロパティが設定されたときに
    PreviewKeyDownイベントを購読
  4. 押されたキーがEnterだったら、
    Bindingを取得しUpdateSource(変更通知)を呼び出す
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace TextBoxBehaviorSample
{
    // 1.enumで動作モードの定義
    public enum EnterBehaviorMode
    {
        None,
        UpdateSource,
        UpdateSourceAndSelectAll
    }
    public static class TextBoxAttachment
    {
        // 2.enumを添付プロパティとして設定するための定義
        public static EnterBehaviorMode GetEnterDownBehavior(DependencyObject obj)
        {
            return (EnterBehaviorMode)obj.GetValue(EnterDownBehaviorProperty);
        }

        public static void SetEnterDownBehavior(DependencyObject obj, EnterBehaviorMode value)
        {
            obj.SetValue(EnterDownBehaviorProperty, value);
        }

        // Using a DependencyProperty as the backing store for EnterDownBehavior.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EnterDownBehaviorProperty =
            DependencyProperty.RegisterAttached("EnterDownBehavior", typeof(EnterBehaviorMode), typeof(TextBoxAttachment), new PropertyMetadata(EnterBehaviorMode.None, (d, e) =>
            {
                if (!(d is TextBox tb)) { return; }
                if (!(e.NewValue is EnterBehaviorMode mode)) { return; }

                // 3.添付プロパティが設定されたときにイベント購読
                if (mode != EnterBehaviorMode.None)
                {
                    tb.PreviewKeyDown += OnTextBoxKeyDown;
                }
                else
                {
                    tb.PreviewKeyDown -= OnTextBoxKeyDown;
                }
            }));

        private static void OnTextBoxKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            // 4.Enterキーが押されたときに
            if (e.Key != System.Windows.Input.Key.Enter)
            {
                return;
            }
            if (!(sender is TextBox tb)) { return; }

            var mode = GetEnterDownBehavior(tb);

            if (mode == EnterBehaviorMode.None) { return; }

            // Bindingを取得してUpdateSource呼出し
            var be = BindingOperations.GetBindingExpression(tb, TextBox.TextProperty);
            be?.UpdateSource();

            // おまけ。入力中の文字を全選択するモード
            if (mode == EnterBehaviorMode.UpdateSourceAndSelectAll)
            {
                tb.SelectAll();
            }

            e.Handled = true;
        }
    }
}

利用法はとっても簡単です。
次のようにTextBoxに設定します。

<TextBox Text="{Binding Name}"
         local:TextBoxAttachment.EnterDownBehavior="UpdateSource"/>

これだけで、Enterキーを押したタイミング
通知が飛ぶようになります。

さんさめ
さんさめ

単にEnterに反応させたいだけなら、
添付ビヘイビアの利用も有り

Enterキーで確定の挙動を確認してみる

それでは、先ほどのアプリケーションに
この添付プロパティを設定してみて、
挙動を確認してみましょう。

いい感じにEnterを押したときに、
「入力した文字」の部分に反映されていますね。

おまけ。変更通知とともに入力した文字を全選択するモード

ちなみに、上記のクラスではおまけで、
Enterキーで確定するだけでなく
全選択するモードも作ってみました。

<TextBox Text="{Binding Rate}"
         local:TextBoxAttachment.EnterDownBehavior="UpdateSourceAndSelectAll"/>

…え?全選択されると何がうれしいの?

やっぱりそう思いますよね。
というわけで、
動作がイメージできるサンプルを用意しました。

このように、数値のシミュレーションなど、
ユーザーが様々な入力パターンを試したい時には、
入力→Enter→次の入力→Enter→…
とテンポよく入力できると便利なのです。

毎回前の値を消すのって結構手間ですからね。
そんな時のためのモードです。
だまされたと思って一度試してみるとその良さに気づけます。

まとめ

まとめです。

  • WPFのTextBoxは標準ではEnterキーを押しても無反応
  • UpdateSourceTriggerを変えれば
    文字入力されるたびに変更通知は飛ばせる
  • 添付プロパティによって
    Enterキーで変更通知を飛ばす方法を紹介
  • Enterのたびに通知とともに
    全選択し直す挙動も使い時を選べば便利

最後までお読みいただきありがとうございました。

関連記事

Enterのたびに全選択する挙動を実装すると、
他のコントロールから
TextBoxにフォーカスが移った時も
全選択して欲しくなります。

【WPF】TextBoxをクリックしたときに全選択させる
では、マウスクリック時やTabキーによるフォーカス取得時に、
テキストを全選択する方法について解説しています。

コメント

タイトルとURLをコピーしました