【WPF】ウィンドウを出した瞬間からキー入力可能にする方法

C#

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

アプリケーションのよくある仕様の一つに、

ダイアログ的にウィンドウを出して、
ユーザーに何か入力させて「OK」ボタンを押させる。

というものがあります。

例えば、以下のようなものです。

  • ログイン(パスワード入力)フォーム
  • データ新規作成ダイアログ

このような仕様を実現するとき、
WPFであれば、Window.ShowDialogを使って
モーダルウィンドウ(閉じるまで元のUIに処理が返ってこない)
として実装することが多いでしょう。

モーダルウィンドウの例

その時気を付けるべき点として、
ただモーダルウィンドウを実装しても、
その中の入力してほしいコントロールに
自動でフォーカスが合ったりはしません

しかし、ユーザー目線からすると、
いちいち入力すべき箇所にマウスクリックやTabキーで
フォーカスを当てなければいけないというのは、
地味に面倒な作業です。

いちいち入力前にフォーカス合わせるの…?

というわけで、本記事では、
WPFにおいて
ウィンドウが開いたときに任意のコントロールに
フォーカスを当てる方法について解説します。

スポンサーリンク

フォーカスを合わせる方法その1:FocusManager.FocusedElementを使う

もっともお手軽な方法です。
xamlだけで全てが解決します。

FocusManager.FocusedElement添付プロパティを用います。

これはその名の通り、
任意のコントロールにフォーカスを設定することができる
プロパティです。

フォーカスを設定するときの使い方は以下の通りです。

  1. フォーカスしたいコントロールにx:Nameで名前を付ける
  2. 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にフォーカスが当たった時に
全選択するような挙動になっていると、
ユーザーはマウスクリック直後から
すぐに入力開始することができ、操作性が向上します。

コメント

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