こんにちは、働くC#プログラマーのさんさめです。
WPFでTextBoxを扱うとき、
クリックした段階で全選択して欲しいことはよくあります。
例えば、以下のユースケースが考えられます。
- 数値入力など、
そこを選択する以上は必ず新規入力がしたい場合- 最初から全選択されていればすぐ入力を開始できる
- テキストをコピーしたい場合
- 最初から全選択されていればすぐCtrl+Cを押せる
ところが、WPFのTextBoxには
クリック時に全選択するという機能はありません。
だからこそ、このちょっと気の利いた機能を実装することで、
「あ、使いやすいなこのアプリケーション」
と思ってもらえる可能性が高まります。
というわけで、
本記事では添付プロパティを使って
マウスクリック時に全選択させる挙動を
上乗せする方法について解説します。
添付プロパティを使ってマウスクリック時に全選択
さっそくですが、以下のようなクラスを作ります。
やっていることは以下の通りです。
- フォーカス取得時に全選択するかどうかの添付プロパティを定義
- 添付プロパティが設定されたときに2種のイベントを購読
- イベント内でフォーカス取得時に全選択するように
public static class TextBoxAttachment
{
// 1.添付プロパティの定義
public static bool GetIsSelectAllOnGotFocus(DependencyObject obj)
{
return (bool)obj.GetValue(IsSelectAllOnGotFocusProperty);
}
public static void SetIsSelectAllOnGotFocus(DependencyObject obj, bool value)
{
obj.SetValue(IsSelectAllOnGotFocusProperty, value);
}
// Using a DependencyProperty as the backing store for GotFocusBehavior. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectAllOnGotFocusProperty =
DependencyProperty.RegisterAttached("IsSelectAllOnGotFocus", typeof(bool), typeof(TextBoxAttachment), new PropertyMetadata(false, (d, e) =>
{
if (!(d is TextBox tb)) { return; }
if (!(e.NewValue is bool isSelectAll)) { return; }
tb.GotFocus -= OnTextBoxGotFocus;
tb.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDown;
if (isSelectAll)
{
// 2.イベント購読
tb.GotFocus += OnTextBoxGotFocus;
tb.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
}));
private static void OnTextBoxGotFocus(object sender, RoutedEventArgs e)
{
if (!(sender is TextBox tb)) { return; }
var isSelectAllOnGotFocus = GetIsSelectAllOnGotFocus(tb);
// フォーカス取得時に全選択する
if (isSelectAllOnGotFocus)
{
tb.SelectAll();
}
}
private static void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!(sender is TextBox tb)) { return; }
// おまじない(後述)
if (tb.IsFocused) { return; }
tb.Focus();
e.Handled = true;
}
}
利用法はとっても簡単です。
次のようにTextBoxに設定します。
<TextBox Text="500"
local:TextBoxAttachment.IsSelectAllOnGotFocus="True"/>
これだけで、
クリック時やTabキーによるフォーカス移動時に、
TextBoxのテキストが全選択されるようになります。
マウスクリック時に全選択の挙動を確認してみる
実際に設定してみて、
挙動を確認してみましょう。
クリック時、Tabキーによるフォーカス移動時に、
テキストが全選択されています。
単にGotFocus時にSelectAllするだけではうまくいかない
さて、コードを注意深く読んだ方は、
次のような疑問を持ったかもしれません。
さっきのコードサンプル、
GotFocusのイベント購読だけで十分なのでは?
サンプルコードでは、GotFocusイベントだけでなく、
PreviewMouseLeftButtonDownイベントも購読していました。
private static void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!(sender is TextBox tb)) { return; }
// おまじない(後述)
if (tb.IsFocused) { return; }
tb.Focus();
e.Handled = true;
}
これ、実は省いてしまうと、
「クリック時に一瞬全選択されてすぐ剥がれる」
という挙動になってしまうのです。
(※Tabキーによるフォーカス移動時はうまくいきます)
せっかくなので、
この処理を外した例を見てみましょう。
ご覧の通り、
Tabキーによるフォーカス移動では
問題なく動作しますが、
クリック時にはうまくいっていません。
Dispatcher.BeginInvokeを使って、
一周遅らせてからSelectAllをすればいいんじゃない?
Dispatcher.BeginInvokeも試してみましたが、
マウスボタンを押してからゆ~っくり離すと、
やっぱり剥がれてしまうんですね。
どうやらマウスのボタンを離した時に
キャレットを動かす処理が行われてしまっているみたいです。
というわけで、
PreviewMouseLeftButtonDownイベントを購読することで、
マウスのボタンを押した段階でフォーカスを取得させて、
ハンドリングしたことにしてしまう。
というおまじないが必要になるのでした。
まとめ
まとめです。
- WPFのTextBoxには、クリック時に全選択する機能はない
- 添付プロパティによって
クリック時に全選択する方法を紹介 - ついでに、Tabキーのときも全選択される
- GotFocusイベントを購読するだけでは
うまくいかなかった例を紹介
最後までお読みいただきありがとうございました。
関連記事
クリック時に全選択されて
テンポよく新規入力ができるようになっても、
Enterキーでサクサク確定できないと
良いテンポが途切れユーザー体験は悪くなります。
しかし、そんな機能はやっぱりTextBoxにはありません。
【WPF】Enterキーで確定できるTextBoxにする添付プロパティ
を使ってユーザー体験を向上させましょう。
構成がほとんど同じや
コメント