今回はMFCによるGUIアプリケーションの実装の注意点です。
「アプリのウィンドウの位置を保存して、次回起動時に同じ位置に表示させる」 という機能について調査した内容を簡単に説明します。。
ウィンドウ座標
ウィンドウの座標系は、ディスプレイデバイスの座標系に基づいています。 基礎知識のおさらいになりますが、位置はx座標とy座標で表され、原点は左上の隅です。 x座標は左から右に増加し、y座標は上から下に増加します。
ウィンドウ起動時にはSetWindowPos()関数に前回終了時の座標を指定して 狙った位置にウィンドウを表示させることが目標です。
ウィンドウの座標を取得する
ウィンドウの座標を取得する方法で最初に思いつくのが、GetWindowRect()です。 GetWindowRect()関数に、ウィンドウのハンドルを渡すと、 そのウィンドウの位置をRect構造体に取得することができます。
これを用いて、ウィンドウ終了時の座標を取得し、 次回起動時にこの座標を用いてウィンドウを表示させれば 前回位置に表示させることができそうです。
・ウィンドウ終了時
// ウィンドウ位置取得
RECT rcWnd; // ウィンドウの位置用
GetWindowRect(hWnd, &rcWnd);
// 取得した座標をiniファイルなどに保存しておく
(中略)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
・ウィンドウ表示時
// 保存している座標を取得しておく
RECT rcWnd; // ウィンドウの位置用
(中略)
// ウィンドウ位置復元
SetWindowPos(hWnd, nullptr, rcWnd.left, rcWnd.top, width, height, SWP_NOZORDER);
しかしこの実装には落とし穴があります。 それはGetWindowRect()が返すウィンドウの位置です。
GetWindowRect()は現在のウィンドウの位置を返すので、 ウィンドウを最大化していれば最大化された位置を返し、最小化していれば最小化した位置を返します。
例えばタスクバーなどで最小化した状態でウィンドウの位置を取得し、 この座標を保存して、次回に保存した位置を参照してウィンドウを表示すると、、 アプリが起動しているのにウィンドウが画面上に表示されず最小化された状態で起動します。 この動作がユーザーの混乱を招く恐れがあります。
続・ウィンドウの座標を取得する
ここでGetWindowRect()関数の代わりにGetWindowPlacement()関数を用います。 この関数はRect構造体ではなく WINDOWPLACEMENT構造体に、位置の値を設定します。 さらにGetWindowRect()関数と違うのが、 ウィンドウが最大化や最小化されているときには最大化、最小化される前のウィンドウの位置を返してくれます。
これなら先ほどのように最小化した状態でアプリを終了しても、 次回起動時にはちゃんとウィンドウを表示してくれます。
単純な実装でもユースケースなどを網羅して考えると、 ユーザービリティを損なうような想定外動作が隠れていたりします。 こういった部分を実装時にも注意しながら確認することが大切です。