Mesoscopic Programming

タコさんプログラミング専門

Programming Tips #3 子ウィンドウのフォーカス復帰

フォーカスを必要とする子ウィンドウが複数個あるケース

フォーカスを必要とするウィンドウがドキュメントビューしかなかった時代は過ぎ去り、いまやツリービューもフォーカスを欲しているのだ。
しかしながらメインウィンドウが非アクティブになると同時にそれらのフォーカスは失われる。
さてメインウィンドウがアクティブ状態を取り戻したとき、最初にフォーカスを受け取るのはメインウィンドウである。
このフォーカスをいかに処理すべきか、それが問題だ。

フォーカス取得イベント

メインウィンドウがアクティブになると WM_SETFOCUS により、どこからかフォーカスを譲り受けたことを知ることができます。
しかしながらメインウィンドウ自身はフォーカスを必要としないため、頂いたフォーカスを適宜子ウィンドウに譲渡しなければなりません。

LRESULT App :: OnWmSetFocus()
{
    if ( hFocus == NULL || ! IsChild( hwnd, hFocus ) )
    {
        hFocus = docView.hwnd;
    }

    SetFocus( hFocus );

    return 0;
}

上記の例では初期化直後のフォーカスをドキュメントビューに渡しています。
2度目以降では最後にフォーカスを所持していた子ウィンドウにフォーカスを渡します。
その仕組みを以下に示します。

非アクティベートイベント

メインウィンドウがアクティブまたは非アクティブになると WM_ACTIVATE メッセージを受け取ります。
以下の処理では、非アクティブ状態を検知して直前のフォーカスを保存しています。

LRESULT App :: OnWmActivate()
{
    HWND hChild;

    if ( LOWORD( wparam ) == WA_INACTIVE )
    {
        hChild = GetFocus();

        if ( IsChild( hwnd, hChild ) )
        {
            hFocus = hChild;
        }
    }

    return 0;
}

念のため現在のフォーカス所持者が子ウィンドウであることを確認していますが、コントロールが受け取ったフォーカスをどのように扱っているか分からないため、もしかするとそれは子ウィンドウではないかも知れません。
したがってのちに不具合が発生した場合、このチェックをはずすことになるでしょう。

以上。