WPFのTextBoxのEnterキーの挙動の変更♯1

TextBoxは、Enterキーを押したときの特別な挙動は定義されていません。しかし、TextBoxでEnterキーを押したときの挙動をカスタマイズしたいことがあります。

WPFのTextBox

WPFのTextBoxコントロールは、ユーザーによる文字入力をできるようにするコントロールです。ユーザーが入力した文字列はTextBox.Textプロパティに設定されます。アプリでは、入力された文字列をプログラムで受け取るために、TextBox.Textプロパティにプログラムのプロパティをデータバインディングします。

TextBox.Textプロパティのデータバインディング

TextBoxのTextBox.Textプロパティに、データバインディングしたとき、入力値がバインディング先のデータソースに反映されるタイミングは、既定ではTextBoxコントロールがフォーカスを失ったときです。

このデータソースに反映されるタイミングは、データバインディングするときのUpdateSourceTriggerプロパティで制御できます。 UpdateSourceTriggerに指定できる値は、以下の表に示すような System.Windows.Data.UpdateSourceTrigger 列挙型の値となります。

列挙値名挙動
Default0バインディング ターゲット プロパティの既定の UpdateSourceTrigger 値。 ほとんどの依存関係プロパティの既定値は PropertyChanged です。ただし、Text プロパティの既定値は LostFocus です。
Explicit3UpdateSource() メソッドを呼び出すときにのみ、バインディング ソースを更新します。
LostFocus2バインディング ターゲット要素がフォーカスを失うたびに、バインド ソースを更新します。
PropertyChanged1バインディング ターゲット プロパティが変更されるたびに、バインディング ソースを即時更新します。
System.Windows.Data.UpdateSourceTrigger 列挙型

データバインディングにおいてこのUpdateSourceTriggerプロパティを指定するときは、XAML上では以下のように記述します。

<TextBox Text="{Binding TextValue,UpdateSourceTrigger=LostFocus}"/>

ほとんどの依存関係プロパティでデータバインディングするときの UpdateSourceTrigger の既定値は、PropertyChangedです。

しかし、TextBoxコントロールのTextBox.Textプロパティでは、この既定値はLostFocusです。そのためTextBoxコントロールでは、コントロールがフォーカスを失ったときに、入力値がバインディングのデータソースに反映されます。

TextBoxコントロールのTextBox.Textプロパティにデータバインディングするとき、この UpdateSourceTriggerプロパティをPropertyChangedに設定すると、文字を一文字入力するつど、データバインディングのデータソースに値が反映されます。

文字を一文字入力するつどに、データバインディングのデータソースに値が反映されるのは、多くの用途で、過剰な更新となります。なぜなら、文字などのデータ入力において、一文字の入力は、データ入力の過程のスナップショットに過ぎず、入力が終わったことを示すものではないからです。

データの入力が終わったと判断するタイミングとして、他のコントロールにフォーカスが移った時とするのは、多くの場合において適切です。そのため、TextBoxコントロールのTextBox.TextプロパティのデータバインディングのUpdateSourceTriggerの既定値がLostFocusになっていることは理解できます。

Enterキーを押したときの挙動

TextBoxコントロールがフォーカスが失うことが条件のため、TextBoxコントロールでEnterキーを押したときには、入力値はデータバインディングのデータソースに反映されません。

データ入力するようなアプリにおいて、改行を含まない一行の文字入力を想定したとき、ユーザーはEnterキーを押したときに、入力値が確定することを期待することがあります。

TextBoxコントロールにおいて、Enterキーを押したときの挙動を制御するものとして、TextBox.AcceptsReturnプロパティがあります。

このプロパティの仕様を確認すると以下の通りです。

Enter キーを押すと現在のカーソル位置に新しい行が挿入される場合は true。それ以外の場合は、Enter キーは無視されます。 既定値は TextBox の場合は falseRichTextBox の場合は trueです。

このプロパティがtrueのときはEnterキーは文字列入力の改行として使われますが、falseのときは単純に無視されます。

これは、TextBoxコントロールが一行入力のときはEnterキーの入力が活用されていないことを意味します。

1行入力のときのEnterキーの入力を有効活用する

TextBoxコントロールでは、一行入力のときにEnterキーが有効活用されていないことがわかりました。

そこでTextBoxコントロールにおいて一行入力のときのEnterキーを有効活用することを考えます。

Enterキーを入力したときの挙動として考えられるのは以下のようなものです。

  • フォーカスを次のコントロールに移動する
  • データバインディングのデータソースに入力値を反映する
  • 入力値のテキストを全選択する

これらの挙動を任意のTextBoxコントロールに選択的に適用できると便利です。

TextBoxコントロールから派生したカスタムTextBoxコントロールを作成することで、これらの振る舞いを実現することができます。しかしこの場合、既存のTextBoxをすべてカスタムTextBoxに置き換える必要があり不便です。

WPFには、既存のコントロールに後から機能を追加するために利用できる添付プロパティという仕組みが用意されています。

そこで、ScrollViewerの機能拡張をした時と同じように、今回のTextBoxコントロールの機能拡張でも、添付プロパティで実現することを考えます。

TextBoxのEnterキーの挙動を制御する添付プロパティ

TextBoxのEnterキーの挙動を制御できる添付プロパティを作成します。

添付プロパティは、TextBoxコントロールに設定して利用するものとします。他のコントロールに設定した場合は、設定は無視されるものとします。

添付プロパティの名前は、TextBox.AcceptsReturnプロパティの名前に合わせて、Enterキー(Return)が入力されたときの挙動という意味で、ReturnBehaviorとします。

<TextBox 
    nsAttachedProps:TextBoxProperties.ReturnBehavior="UpdateSource"/>
...

Enterキーの挙動の種類(ReturnBehaviorMode)

今回、実現する添付プロパティでは、Enterキーの挙動として複数用意します。

  • MoveFocus: フォーカスを次のコントロールに移動する
  • UpdateSource: 入力値をデータバインディングのデータソースに反映する
  • SelectAll: 入力値のテキストを全選択する
  • UpdateSourceAndMoveFocus: 入力値をデータバインディングのデータソースに反映した後、フォーカスを次のコントロールに移動する
  • UpdateSourceAndSelectAll: 入力値をデータバインディングのデータソースに反映した後、入力値のテキストを全選択する

上記は挙動の基本機能の定義(MoveFocus / UpdateSource / SelectAll)と組み合わせの定義(UpdateSourceAndMoveFocus / UpdateSourceAndSelectAll)であり、すべてが利用されるとは想定ていません。

SelectAllは単独で利用することに有用性がなく、通常は単独で使用することは想定されません。

UpdateSourceAndMoveFocus (MoveFocusとUpdateSourceの組み合わせ)も利用されることは想定しません。なぜなら、TextBoxコントロールがフォーカスを失った場合は、データバインディングの UpdateSourceTriggerの機能でデータソースは更新されるからです。

したがって、利用されると想定される値は、以下となります。

  • MoveFocus: フォーカスを次のコントロールに移動する
  • UpdateSource: 入力値をデータバインディングのデータソースに反映する
  • UpdateSourceAndSelectAll: 入力値をデータバインディングのデータソースに反映した後、入力値のテキストを全選択する

具体的には以下のような列挙値を添付プロパティに設定できるようにします。

[Flags]
public enum ReturnBehaviorMode
{
   None = 0,
   MoveFocus = 1,
   UpdateSource = 2,
   SelectAll = 4,

   // combination
   UpdateSourceAndMoveFocus = 3,
   UpdateSourceAndSelectAll = 6
}

添付プロパティの実装

添付プロパティの実装では、添付プロパティが設定されたTextBoxコントロールのEnterキーのの挙動を制御するようにします。

この添付プロパティを実現するために必要な要素は以下の通りとなります。

  • 添付プロパティの定義とアクセス関数
  • 添付プロパティが変化したときの処理
  • PreviewKeyDownイベントハンドラー

これらは、 TextBoxProperties クラスに定義するものとします。そのため、添付プロパティの名前はTextBoxProperties.ReturnBehaviorとなります。

添付プロパティの定義とアクセス関数

ReturnBehaviorMode列挙値を設定する添付プロパティReturnBehaviorを定義します。また、コードから添付プロパティの値にアクセスするための関数GetReturnBehavior() / SetReturnBehavior()も用意します。

具体的には以下のようなコードになります。

/// <summary>
/// Using a DependencyProperty as the backing store for ReturnBehavior.
/// This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ReturnBehaviorProperty =
    DependencyProperty.RegisterAttached("ReturnBehavior", typeof(ReturnBehaviorMode), typeof(TextBoxProperties),
        new PropertyMetadata(ReturnBehaviorMode.None, OnReturnBehaviorChanged));

public static ReturnBehaviorMode GetReturnBehavior(DependencyObject d)
{
    return (ReturnBehaviorMode)d.GetValue(ReturnBehaviorProperty);
}

public static void SetReturnBehavior(DependencyObject d, ReturnBehaviorMode value)
{
    d.SetValue(ReturnBehaviorProperty, value);
}

添付プロパティの値が変化したときに呼び出される OnReturnBehaviorChanged()は、後ほど定義します。

添付プロパティが変化したときの処理

添付プロパティはTextBoxコントロールに設定することを想定します。そのため、TextBox以外に設定された場合、および、値がReturnBehaviorMode以外の場合は、無視します。TextBoxコントロールに設定されたら、PreviewKeyDownイベントのイベントハンドラーOnPreviewKeyDownを設定します。

添付プロパティの値が変更されたときの実装は、具体的には以下のようなコードになります。

static void OnReturnBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (!(e.NewValue is ReturnBehaviorMode mode)) { return; }
    if (!(d is TextBox tb)) { return; }

    if (mode != ReturnBehaviorMode.None)
    {
        tb.PreviewKeyDown += OnPreviewKeyDown;
    }
    else
    {
        tb.PreviewKeyDown -= OnPreviewKeyDown;
    }
}

TextBoxコントロールでキーが押されたときに呼び出される OnPreviewKeyDown()は、次回の投稿で説明します。


今回の投稿では、TextBoxコントロールにおいてEnterキーを有効活用するための添付プロパティの設計の前半でした。次回の投稿で、残り設計とこの添付プロパティを実装したnugetライブラリーについて説明したいと思います。

“WPFのTextBoxのEnterキーの挙動の変更♯1” への1件の返信

コメントを残す