DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

ToolStripとToolStripItem(ツールバー、メニュー、ステータスバーとその項目)の位置を保存、復元する

注意:ここで紹介している方法は、.NET Framework 2.0以降でのみ使用できます。

ToolStripItem(ツールバー、メニュー、ステータスバーの項目)の位置をユーザーが変えられるようにする」で紹介している方法を使うと、ユーザーが自由にToolStripの項目(ToolStripItem)の位置を変更できるようになります。また、「ToolStripとToolStripContainerを使って、ツールバーの位置をユーザーが変更できるようにする」で紹介している方法を使うと、ユーザーが自由にToolStripの位置を変更できるようになります。このような場合、ユーザーによって変更されたToolStripItemやToolStripの位置を保存、復元する必要が生じます。ここでは、その方法を紹介します。

SaveSettings、LoadSettingsメソッドを使う

ToolStripManager.SaveSettingsメソッドを使うと、簡単にToolStripとToolStripItemの位置を保存することができます。保存した内容は、ToolStripManager.LoadSettingsメソッドで復元することができます。

ToolStripManager.SaveSettingsで保存される情報は、ToolStripが配置されているToolStripPanel(ToolStripContainerの上下左右の場所)、ToolStripのサイズと位置、表示か非表示かという情報と、ToolStrip内のToolStripItemの順番です。

フォームのLoadイベントでToolStripとToolStripItemの位置を復元し、FormClosedイベントで保存する例を以下に示します。このコードは、ToolStripのあるフォームのクラスに記述されているものとします。なおLoadイベントでフォームのサイズを復元している場合は、フォームのサイズを復元した後にLoadSettingsを呼び出した方が良いでしょう。

VB.NET
コードを隠すコードを選択
'フォームのLoadイベントハンドラ
Private Sub Form1_Load(sender As Object, e As EventArgs) _
        Handles MyBase.Load
    'ToolStripとToolStripItemの位置を復元
    ToolStripManager.LoadSettings(Me)
End Sub

'フォームのFormClosedイベントハンドラ
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) _
        Handles MyBase.FormClosed
    'ToolStripとToolStripItemの位置を保存
    ToolStripManager.SaveSettings(Me)
End Sub
C#
コードを隠すコードを選択
//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, EventArgs e)
{
    //ToolStripとToolStripItemの位置を復元
    ToolStripManager.LoadSettings(this);
}

//フォームのFormClosedイベントハンドラ
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
    //ToolStripとToolStripItemの位置を保存
    ToolStripManager.SaveSettings(this);

}
補足:ToolStripManager.SaveSettingsで保存される設定は、「%USERPROFILE%\AppData\Local\(アプリケーションの名前)」(Windows XP以前では「%USERPROFILE%\Local Settings\Application Data\(アプリケーションの名前)」)フォルダの奥の奥にある「user.config」というファイルにXMLで保存されます。詳しくは、「Where does ToolStripManager.SaveSettings save to?」をご覧ください。

SaveSettings、LoadSettingsメソッドの注意点

これらのメソッドを使う際には、幾つか注意しなければならない点があります。

上記のようなコードを書いているフォームに新たなToolStripを追加すると、そのToolStripは表示されません。ToolStripManager.LoadSettingsメソッドにより、新しく追加されたToolStripのVisibleがFalseにされてしまいます。(MSDNには削除されるとありますが、削除はされていません。)よってこのような場合は、LoadSettingsの後で新しく追加されたToolStripのVisibleをTrueとするか、新しいToolStripを追加してからLoadSettingsを呼び出すまでの間に、SaveSettingsで保存するようにしてください。

また、Visual Studioのデザイナを使わずにToolStripを作成し、フォームに追加した場合、そのToolStripの設定がSaveSettingsで保存されない可能性があります。正しく保存されるようにするには、ToolStripのNameプロパティにユニークな名前を設定する必要があるようです。

さらには、一番大きく、しかも厄介な問題として、2つ以上のToolStripが同じToolStripPanelにあると、ほとんどの場合、LoadSettingsメソッドはToolStripの位置を正常に復元することができません。LoadSettingsメソッドを複数回呼び出すことにより復元できる場合もありますが、何回呼び出しても復元できないケースもありました。よって現時点(.NET Framework 2.0)では、ToolStripの位置を復元する目的では、ToolStripManager.LoadSettingsメソッドはほとんど使い物になりません。

ToolStripの位置を正しく保存、復元できるようにするための改良

ToolStripの位置を正常に復元できないという問題は、LoadSettingsメソッドが正しい順番でToolStripの位置を復元していないことが原因のようです。そこで試しに正しい順番でToolStripの位置を復元するコードを書いてみたところ、正しく復元されるようになりました(私が試した範囲内ですが)。

このコードは、次のような方針に基づいて書かれています。

まず設定の保存では、ToolStripPanelにあるToolStripの位置を、そのToolStripの順番通りに保存します。つまり横長のToolStripPanelであれば、一番上の行の左端のToolStripから順番に、その順番も保存されるようにして、その位置を保存します。(保存は、独自に行っています。ToolStripManager.SaveSettingsが保存したものを使った方が良いかもしれませんが、面倒なので。)

設定の復元では、まずはじめに現在ToolStripPanel内にあるすべてのコントロールを削除してから、順番通りにToolStripを元の位置に追加していきます。

このコードはToolStripの位置を保存、復元するだけで、サイズや表示・非表示等の復元は行っていないため、それ以外の設定を復元するために、ToolStripManager.SaveSettingsとLoadSettingsメソッドも呼び出す必要があります。(ここで示すコードには、組み込まれています。このコードをちょっと改良すれば、その必要もなくなりそうですが。)

また、すべてのToolStripPanelのNameプロパティが設定されている必要があります。つまり、ToolStripContainerコントロールの上下左右のToolStripPanelのNameプロパティも設定されている必要があります(デフォルトでは設定されていません)。

使い方は、ToolStripManagerクラスと同じように、ToolStripManager2.SaveSettingsで保存し、ToolStripManager2.LoadSettingsで復元します。ちょっといい加減かもしれませんので、あまり信用しない方が良いでしょう。(コードは、C#のみです。)

もっと良い方法をご存知の方は、ぜひご連絡ください。

VB.NET
コードを隠すコードを選択
[VB.NET]
VB.NETのコードは現在準備中です。
Convert C# to VB.NET により、
下記のC#のコードをVB.NETに変換したものを参考にしてください。
C#
コードを隠すコードを選択
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

public class ToolStripManager2
{
    internal class ToolStripSettings
        : System.Configuration.ApplicationSettingsBase
    {
        /// <summary>
        /// ToolStripPanelにあるToolStripを列に分けて保存する
        /// </summary>
        [System.Configuration.UserScopedSetting,
        System.Configuration.DefaultSettingValue("")]
        public List<List<ToolStripInfo>> Rows
        {
            get
            {
                return (List<List<ToolStripInfo>>)this["Rows"];
            }
            set
            {
                this["Rows"] = value;
            }
        }

        public ToolStripSettings(string settingsKey)
            : base(settingsKey)
        {
        }
    }

    /// <summary>
    /// 保存するToolStripの情報
    /// </summary>
    public class ToolStripInfo : IComparable<ToolStripInfo>
    {
        public string Name = "";
        public Point Location = Point.Empty;

        public ToolStripInfo(ToolStrip ts)
        {
            this.Name = ts.Name;
            this.Location = ts.Location;
        }

        public ToolStripInfo()
        {
        }

        public int CompareTo(ToolStripInfo other)
        {
            if (this.Location.X == other.Location.X)
            {
                return this.Location.Y - other.Location.Y;
            }
            return this.Location.X - other.Location.X;
        }
    }

    /// <summary>
    /// sourceFormに配置されたToolStripPanel内のToolStripの位置を保存する
    /// </summary>
    /// <param name="sourceForm"></param>
    public static void SaveSettings(Form sourceForm)
    {
        ToolStripManager.SaveSettings(sourceForm);
        ToolStripManager2.InternalSaveSettings(sourceForm);
    }

    /// <summary>
    /// sourceFormに配置されたToolStripPanel内のToolStripの位置を復元する
    /// </summary>
    /// <param name="sourceForm"></param>
    public static void LoadSettings(Form sourceForm)
    {
        ToolStripManager.LoadSettings(sourceForm);
        ToolStripManager2.InternalLoadSettings(sourceForm);
    }

    internal static void InternalSaveSettings(Control owner)
    {
        //owner内のToolStripPanelを探す
        List<Control> toolStripPanels = new List<Control>();
        FindControls(typeof(ToolStripPanel), owner.Controls,
            ref toolStripPanels);

        foreach (ToolStripPanel tsp in toolStripPanels)
        {
            if (string.IsNullOrEmpty(tsp.Name)) continue;

            List<List<ToolStripInfo>> rowsList =
                new List<List<ToolStripInfo>>();
            foreach (ToolStripPanelRow r in tsp.Rows)
            {
                //ToolStripPanelの列内のToolStripの情報を収集
                List<ToolStripInfo> toolStripNames =
                    new List<ToolStripInfo>();
                foreach (Control con in r.Controls)
                {
                    if (con is ToolStrip &&
                        !string.IsNullOrEmpty(con.Name))
                    {
                        toolStripNames.Add(
                            new ToolStripInfo((ToolStrip)con));
                    }
                }
                //列内の順番を並び替え
                toolStripNames.Sort();
                rowsList.Add(toolStripNames);
            }

            //ToolStripPanelごとに保存する
            string skey = owner.GetType().FullName + "." + tsp.Name;
            ToolStripSettings settings = new ToolStripSettings(skey);
            settings.Rows = rowsList;
            settings.Save();
        }
    }

    internal static void InternalLoadSettings(Control owner)
    {
        //owner内のToolStripPanelを探す
        List<Control> toolStripPanels = new List<Control>();
        FindControls(typeof(ToolStripPanel), owner.Controls,
            ref toolStripPanels);

        foreach (ToolStripPanel tsp in toolStripPanels)
        {
            if (string.IsNullOrEmpty(tsp.Name)) continue;

            //ToolStripPanelの情報を読み込む
            string skey = owner.GetType().FullName + "." + tsp.Name;
            ToolStripSettings settings = new ToolStripSettings(skey);
            List<List<ToolStripInfo>> rowsList = settings.Rows;

            //ToolStripPanel内のToolStripを一時的にすべて削除する
            Dictionary<string, ToolStrip> toolstrips =
                new Dictionary<string, ToolStrip>();
            foreach (Control c in tsp.Controls)
            {
                toolstrips.Add(c.Name, (ToolStrip)c);
            }
            tsp.Controls.Clear();

            for (int i = 0; i < rowsList.Count; i++)
            {
                foreach (ToolStripInfo info in rowsList[i])
                {
                    //位置を設定するToolStripを探す
                    ToolStrip ts = null;
                    if (toolstrips.ContainsKey(info.Name))
                    {
                        ts = toolstrips[info.Name];
                    }
                    else
                    {
                        Control[] tss =
                            owner.Controls.Find(info.Name, true);
                        if ((tss != null) && (tss.Length == 1)
                            && (tss[0] is ToolStrip))
                        {
                            ts = (ToolStrip)tss[0];
                        }
                    }
                    //ToolStripの位置を変更する
                    if (ts != null)
                    {
                        tsp.Join(ts, info.Location);
                    }
                }
            }
        }
    }

    internal static void FindControls(Type findType, 
        Control.ControlCollection conts, ref List<Control> foundList)
    {
        foreach (Control c in conts)
        {
            if (findType.IsAssignableFrom(c.GetType()))
            {
                foundList.Add(c);
            }
            if (c.Controls.Count > 0)
            {
                FindControls(findType, c.Controls, ref foundList);
            }
        }
    }
}
  • 履歴:
  • 2012/02/08 設定が保存される場所の補足を追加。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。