WPF でウィンドウ位置とサイズを保存・復元しよう

デスクトップ アプリで、ウィンドウの位置を保存したいという要望はちらほら来ます。 ただし、真面目に実装しようとすると、細かい挙動まで実装するのが大変面倒です。 最大化して終了したら、最大化する直前のウィンドウの位置とサイズも保存しておかなければならなかったり。

何かベスト プラクティスはないかなーと思って調べたところ、MSDN に該当するコードがあったので、やってみました。

元ネタはこちら。 http://msdn.microsoft.com/ja-jp/library/vstudio/aa972163(v=vs.90).aspx

結局 P/Invoke だった!

いかな WPF といえど所詮は Windows の民。P/Invoke の運命からは逃れられないのだ…

という冗談は置いておいて、SetWindowPlacement 関数GetWindowPlacement 関数を使います。 以下、ちょっと長いですが、そのための下準備コードです。

WINDOWPLACEMENT 構造体 に、ウィンドウの位置・サイズ・状態を格納します。 normalPosition が通常状態のウィンドウの位置とサイズ、showCmd がウィンドウの状態 (最小化・最大化・通常 など) です。

どこに保存すべきか?

これがまた悩ましいのですが、もっとも簡単な方法は Settings.settings を使ってしまう方法です。 MSDN のサンプルもこれを使っています。

ただ、それでは面白くないので、今回はもう少しだけ工夫して作ります。 ウィンドウ位置の保存方法を外部から設定できるようにしましょう。

ウィンドウ位置の保存と復元のためのインターフェイスとして、IWindowSettings を用意します。 ウィンドウの位置・サイズ・状態を持つ Placement プロパティのほか、保存のための Save メソッド、復元のための Reload メソッドを定義しました。 また、その既定実装として、ApplicationSettingsBase を使ったクラスも用意しました。

ApplicationSettingsBase は Settings.settings と同じく (というか、その Settings クラスは ApplicationSettingsBase を継承しています)、%UserProfile%\AppData\Local\ に設定を自動的に保存するための機構です。 アセンブリのバージョンが変わるとパスが変わるため、通常はバージョンアップ時に設定を引き継げない、等の若干の使いにくさがありますが、特に意識しなければこれで事足ります。

もし、レジストリに保存しなければならない、指定したフォルダーに保存しなければならない、など ApplicationSettingsBase の挙動では要件を満たせない場合は、IWindowSettings を実装したクラスを別途用意しましょう。

設定自動保存ウィンドウ

以上を踏まえ、ウィンドウの位置を自動的に保存・復元できるウィンドウを作ります。

WindowSettings 依存関係プロパティは、ウィンドウ位置の保存・復元手段を外部から与えるためのものです。 何も指定しなければ (= null)、ApplicationSettingsBase を使った既定実装になります。

あとは、P/Invoke 時はお馴染みの SourceInitiazlized イベント (ウィンドウ ハンドルが取れるようになるタイミング) で設定情報を復元し、SetWindowPlacement 関数でウィンドウの位置を設定します。

WINDOWPLACEMENT の length メンバーには、必ず sizeof(WINDOWPLACEMENT) が設定されている必要があります (それ以外の場合は失敗します)。flags は 0 で構いません。 showCmd には、復元した値が SW_SHOWMINIMIZED (最小化状態) であった場合、つまり前回の起動時に最小化されたまま終了した場合は、SW_SHOWNORMAL (通常状態) で表示するための処理を加えています。

同様に、ウィンドウが閉じられる直前で、かつキャンセルされていない場合に、GetWindowPlacement 関数で現在のウィンドウの状態を取得し、保存します。

ね? 簡単でしょう?

サンプル コード

上記コードを使用したサンプル コードを用意しました。 ビルドして、起動 -> サイズ&位置変更 -> 終了 を繰り返してみてください。 サイズと位置、最大化等の状態も保存・復元されます。

RestorableWindowSample ver.1.0.zip

設定ファイルは、以下のように %UserProfile%\AppData\Local\RestorableWindowSample に保存されるはずです。

user.config

なお、この方法は、MSDN で公開されているだけでなく、モダンなデスクトップ アプリ実装のためのライブラリである MahApps.MetroMetroRadiance でも使われています。

最大化や最小化直前のウィンドウ位置とサイズは Window.RestoreBounds プロパティでも取得できます。 しかし、SetWindowPlacement 関数に任せておけば、復元した座標ではウィンドウが完全に画面の外に出てしまうような異常ケース (モニターが切断されたときとか) でも自動調整してくれたりと、上手いことやってくれます。 それ専門の関数に任せておいた方が楽ですし、標準で確実な挙動になるでしょう。

以上、ご参考までに。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です