こんにちは、働くC#プログラマーのさんさめです。
アプリケーションのよくある仕様の一つに、
ダイアログ的にウィンドウを出して、
ユーザーに何か入力させて「OK」ボタンを押させる。
というものがあります。
例えば、以下のようなものです。
- ログイン(パスワード入力)フォーム
- データ新規作成ダイアログ
このような仕様を実現するとき、
WPFであれば、Window.ShowDialogを使って
モーダルウィンドウ(閉じるまで元のUIに処理が返ってこない)
として実装することが多いでしょう。
その時気を付けるべき点として、
ただモーダルウィンドウを実装しても、
その中の入力してほしいコントロールに
自動でフォーカスが合ったりはしません。
しかし、ユーザー目線からすると、
いちいち入力すべき箇所にマウスクリックやTabキーで
フォーカスを当てなければいけないというのは、
地味に面倒な作業です。
というわけで、本記事では、
WPFにおいて
ウィンドウが開いたときに任意のコントロールに
フォーカスを当てる方法について解説します。
フォーカスを合わせる方法その1:FocusManager.FocusedElementを使う
もっともお手軽な方法です。
xamlだけで全てが解決します。
FocusManager.FocusedElement添付プロパティを用います。
これはその名の通り、
任意のコントロールにフォーカスを設定することができる
プロパティです。
フォーカスを設定するときの使い方は以下の通りです。
- フォーカスしたいコントロールにx:Nameで名前を付ける
- FocusManager.FocusedElementを
使って1.で名前をつけたコントロールを設定する
極小のサンプルはこちらです。
<Window x:Class="FocusManagerSample.SignInWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FocusManager.FocusedElement="{Binding ElementName=MyTextBox}">
<!-- 2.FocusManager.FocusedElementにBindingを駆使してコントールを指定 -->
<DockPanel>
<!-- 1.フォーカスしたいコントロールに名前を付ける -->
<TextBox x:Name="MyTextBox"/>
</DockPanel>
</Window>
これで、Windowが開いた瞬間(=フォーカスが当たった瞬間)に、
TextBoxにフォーカスが当たります。
冒頭のサンプル画像にこれを適用した場合の挙動は
以下のようになります。
(gifアニメです)
ダイアログが開いた直後からキャレットが点滅してます。
ちゃんとフォーカスが当たっているようですね。
フォーカスを合わせる方法その2:コードビハインドでFocusを呼ぶ
また、コードビハインドでFocusメソッドを呼ぶことでも、
同じ挙動が実現できます。
コントロールに名前を付けるところまでは一緒ですが、
こちらはコードビハインドでフォーカスを当てます。
以下はビハインドのサンプルコードです。
public partial class SignInWindow : Window
{
public SignInWindow()
{
InitializeComponent();
MyTextBox.Focus();
}
}
この方法でも、
やはりウィンドウを開いた直後からフォーカスをあてることができます。
方法その2を使う必要ある?→全選択させておきたい時に使います
さて、ほとんどの場合はその1の方法を使えば充分です。
ですが、私さんさめはあえて、
方法その2を使っているケースがあります。
それは、
【WPF】TextBoxをクリックしたときに全選択させる
で紹介している、
フォーカス時に全選択するTextBoxと併用したい場合です。
上記記事の「フォーカス時全選択TextBox」を
FocusManager.FocusedElementで指定すると、
以下のように、
全選択されてない状態でフォーカスが当たってしまいます。
この現象は、xaml上でTextを書かずに、
Bindingを利用している場合のみ発生します。
フォーカスよりも後のタイミングで、
Bindingの解決が行われてしまっているのです。
このような場合は、方法2の
コードビハインドでフォーカスを当てる必要があります。
(実際には、以下のサンプルコードのように
Dispatcher.BeginInvokeで一周遅らせることも必要です)
public SignInWindow()
{
InitializeComponent();
// コードビハインドでフォーカス、かつ
// Dispatcher.BeginInvokeでタイミングをBinding解決より後にずらしている
Dispatcher.BeginInvoke((Action)(() => MyTextBox.Focus()));
}
これで、以下のように求めていた挙動になります。
ウィンドウが表示された直後から全選択されており、
すぐに入力開始ができるようになりました。
まとめ
まとめです。
- モーダルウィンドウを出すときは、
ユーザーがすぐに必要な操作ができるか要チェック - ウィンドウが開くと同時に
必要な個所にフォーカスが当たっていると
気が利いている - ウィンドウが開いたときにすぐにフォーカスを当てるには
FocusedManager.FocusedElementプロパティを使う - 全選択TextBoxと併用したいのであれば
コードビハインドでのフォーカスを用いる
最後までお読みいただきありがとうございました。
関連記事
入力を終えてEnterキーを押したら、
それはもうOKボタンを押したことになって欲しい
と思うのがユーザーの心情です。
WPFではButtonにIsDefaultプロパティを設定することで、
簡単にEnterキーにキーアサインすることができます。
Dispatcher.BeginInvokeは、Actionにキャストすることで、
ラムダ式を引数に使うことができます。
TextBoxにフォーカスが当たった時に
全選択するような挙動になっていると、
ユーザーはマウスクリック直後から
すぐに入力開始することができ、操作性が向上します。
コメント