2011年6月2日木曜日

フォーカスを奪わないウィンドウ

まず、ソフトウェアキーボードのウィンドウは他ウィンドウからフォーカスを奪わないようにしなければなりません。
ソフトウェアキーボードのデザインは多種多様にあるけど、 私が想定している動作は、自作ソフトウェアキーボードからフォーカス(キャレット)を持つコントロールへ仮想キーコード、もしくは文字コードを送信するパターンになります。
これらを前提としてブログを書き進めていきますので、ご了承下さい。

私が調べたところ、上記を実装する手段は大まかに次の通り。
  1. ウィンドウがアクティブにならないようにする
  2. フォーカス遷移を記録しておいて、コード送信時にフォーカスを以前のウィンドウに戻す
  3. フォーカスメッセージをフックして、受け取らない(元フォーカスがあったウィンドウに再送)
 何通りか試してみたけど、やはり一番上手く想定通りの動作をするのは1.のやり方でした。
2.も「ソフトウェアキーボード上で操作して情報を元のウィンドウに送る」と言う動作自体は問題ありませんでした。
ただ、タスクバーやウィンドウの外観がアクティブウィンドウが変わる度に変化(フィードバック)するので見かけ上気持ち悪いんだよね。
3.は上手くやればできるのかもしれないけど、(私の力量では)安定性が低かったり、グローバルフックは将来的に公開するときに色々と面倒そう、と感じたので却下。

無駄な文を長々と書いてしまったけど、私は次のようにして1.を実装することにしました。

[DllImport("user32.dll")]
static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);

private const int GWL_EXSTYLE = -20;
private const int WS_EX_NOACTIVATE = 0x08000000;

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);

    WindowInteropHelper helper = new WindowInteropHelper(this);
    SetWindowLong(helper.Handle, GWL_EXSTYLE, GetWindowLong(helper.Handle, GWL_EXSTYLE) | WS_EX_NOACTIVATE);
}

(ソフトウェアキーボードの)メインウィンドウにSetWindowLong()でWS_EX_NOACTIVATEを設定することで、アクティブにならないウィンドウの作成が可能。
なおWPFで実装しているため、ウィンドウハンドルを取得する部分がその前段となります。
ちなみにタスクバーやAlt-tabで選択した場合にはアクティブになるので、扱いには一工夫必要かな。

このようなブログを書くのは始めてなので、折角見ていただいてもよく分からない説明になっているかも知れません…。
もし詳しく説明してくれって人がいたらコメントでもして下さい。
#それ以前のページビューだけどねw

0 件のコメント:

コメントを投稿