ふにゃるんv2

もとは、http://d.hatena.ne.jp/Wacky/

最大化/最小化問題にも耐える、Windowsアプリケーションの位置やサイズを保存するには?

.NETのApplicationSettings機能って便利でして、前回のアプリケーション終了時の位置とフォームサイズを、ちょこちょこ設定するだけで行ってくれるようになります。


この機能、非常に便利でして、ちょっとしたツールを.NETで作る際、いつもこの機能を利用しています。


例えば、起動直後、以下の位置&サイズでアプリが起動したとしますね。
2
2 posted by (C)wacky
この位置に移動&拡大して終了したとします。
3
3 posted by (C)wacky
次に起動すると、同じ位置&サイズでアプリを起動してくれる訳です。


この機能を行う為に必要なのは、プロパティ設定画面で ちょこちょこ弄る程度で済む訳ですから、使わない手はありません。
(正確には、プロパティ弄るだけでOKなのは、VB.NETで、C#の場合、FormClosedイベントに'Properties.Settings.Default.Save();'の1行を記述する必要があります)
1
1 posted by (C)wacky

ところが

ところが、この機能、一点欠点がありまして、これを使って最大化/最小化を行うと、次の回復時に変な動きになります。

例えば、「最大化」した後、「普通」に戻します。すると、以下のようになってしまいます。
4
4 posted by (C)wacky
他にも、

  • 「最大化」した状態で閉じると、次に起動した時「普通」が「最大化」状態
  • 「最小化」した常態で閉じると、次に起動した時「普通」が「最小化」状態

になったりと、「ちょっと待て」と言いたくなる挙動を起こしてくれます。


気になる人も結構いらっしゃるようで、幾つか対策案が提示されています。

やっている事は、前の値を独自変数で保持&意図しない状態の際復旧。というパターンです。

ApplicationSettingsBase.SettingChangingイベントを使ってみる対策

他に何か良い方法は無いかな〜と、MSDN Libraryを眺め回していたら、私的解決方法を思いつきました。
ApplicationSettingsBase.SettingChangingイベントを使う方法です。


早速ですが、以下にコードサンプルを示します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DBG = System.Diagnostics.Trace;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Properties.Settings.Default.Save();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Properties.Settings.Default.SettingChanging += new System.Configuration.SettingChangingEventHandler(Default_SettingChanging);
        }

        void Default_SettingChanging(object sender, System.Configuration.SettingChangingEventArgs e)
        {
            DBG.WriteLine(string.Format("SettingChanging {0}, {1}", e.SettingName, e.NewValue));
            if (this.WindowState != FormWindowState.Normal)
            {
                if ((e.SettingName == "MyClientSize") || (e.SettingName == "MyLocation"))
                {
                    DBG.WriteLine("MyClientSize cancel");
                    e.Cancel = true;
                }
            }
        }
    }
}


上のコードの肝は、Default_SettingChangingで、以下のように処理している箇所です。

  1. 現在のWindowStateがFormWindowState.Normalでない(即ちMaximizedかMinimized)
  2. 変更しようとしているプロパティ値は、MyLocation(LocationのApplicaitonSettings値)かMyClientSize(ClientSizeのApplicationSettings値)である
  3. 1.と2.を満たす場合、ApplicationSettingsの反映を阻害(Cancel = true)する

上記のコードを追加する事で、前項の「楽しくない挙動」から解放されます。(VS2005とVS2008で確認)


つまり、以下のケースに対応できます。
(Powered by.フォト蔵(見やすくする為に、ちょっとタグを弄らせてもらっています))

ケース 事例
「普通」→「最大化」→「普通」 「普通」3→「最大化」5→「普通」3
「最大化」→「閉じる」→「起動」 「最大化」5→「閉じる」→「起動」3
「最小化」→「閉じる」→「起動」 「最小化」6→「閉じる」→「起動」3

まとめ

ApplicationSetttingsを使って「Windowsアプリケーションの位置やサイズを保存する」場合、ApplicationSettingsBase.SettingChangingイベントを使ってみては、どうでしょう?