*.NETプログラミング研究 第66号 [#k8ab7411]

今更ですが、今回から.NET Framework 2.0の新機能を幾つか紹介させていただきます。今回は、ToolStripとToolStripContainerコントロールについて説明します。

**.NET Tips [#qd352259]

***ToolStrip、ToolStripContainerコントロールとは? [#k22ca052]

ToolStripは、.NET Framework 2.0で新しく追加されたWindowsアプリケーションのコントロールです。これは、今までのToolBarコントロールの代わりに使用することのできるコントロールで、ToolBarよりも機能が大幅に増えています。Microsoft Office XPのツールバーのようなコントロールといえば分かりやすいでしょう。

ToolStripコントロールは、ToolStripContainerコントロールと共に使用すると、さらに便利です。Microsoft Office XPのツールバーは、ドラッグ&ドロップによりユーザーが自由に位置を変更することができますが、ToolStripContainerコントロールを使えば、これと同じことを実現できます(ただし、現在ツールバーのあるフォームから分離させて、新しいフォームにすることはできません)。

また、ToolStripから派生したMenuStripとStatusStripコントロールは、今までのMainMenuとStatusBarコントロールの代わりに使うことができ、もちろん機能がかなり増えています。しかもToolStripContainerコントロールを使えば、ToolStrip同様、ユーザーが好きな位置に移動できるようになります。

使ってみればすぐに分かるようなつまらない説明は省略し、とりあえず使ってみることにしましょう。本来ならばToolStripコントロールの説明をしてからToolStripContainerコントロールの説明に移るべきですが、それでは面白くないので、いきなりToolStripとToolStripContainerコントロールを共に使った例から紹介します。

-[[ToolStrip コントロール (Windows フォーム)>http://msdn2.microsoft.com/ja-jp/library/5daaw6hf.aspx]]

***ToolStripとToolStripContainerコントロールの基本的な使い方 [#t8603785]

Microsoft Visual Studio 2005のフォームデザイナを使って、ToolStripとToolStripContainerコントロールを使う基本的な方法を順を追って説明します。

+Windowsアプリケーションのプロジェクトを作成し、フォームにToolStripContainerコントロールを配置します(ここでは、名前を「ToolStripContainer1」とします)。ToolStripContainerコントロールは、ツールボックスの「メニューとツールバー」と「すべてのWindowsフォーム」にあります。
+ToolStripContainer1のDockプロパティをFillとします。(または、ToolStripContainerコントロールを配置した時に表示される「ToolStripContainerタスク」の「フォームの四辺にドッキング」をクリックしても同じことができます。)
+デフォルトでは、ToolStripContainer1の四辺のすべてにToolStripコントロールを配置できるようになっています。ToolStripの配置を許可しない辺を指定するには、TopToolStripPanelVisible、BottomToolStripPanelVisible、LeftToolStripPanelVisible、RightToolStripPanelVisibleの内適当なものをFalseにします。(ToolStripContainerコントロールを配置した時に表示される「ToolStripContainerタスク」の「パネルの表示状態」でチェックを外しても同じことができます。)
+ToolStripコントロールを配置するための場所を空けるために、ToolStripContainer1の上下左右に表示される内側に向いた矢印(3.で無効にした辺は表示されません)をクリックします。(はじめは、一番上が空いています。)空いた場所にToolStripコントロール(MenuStripやStatusStripコントロールも含む)を配置します。ToolStripコントロールを複数配置する場合は、ToolStripコントロール配置後、さらに矢印をクリックし、場所を空けます。なお、ToolStripContainerの上下左右のパネルには、ToolStripとその派生クラスしか配置できません。それ以外のコントロールを配置しようとすると、ToolStripContainerの中央のContentPanelに配置されてしまいます。
+配置したToolStripコントロールをクリックして、ボタンやコンボボックス等を追加します。

このように、VS2005のフォームデザイナを使えば、非常に簡単です。

これと同じことをコードで行う方法を次に示します。ここではこのコードがフォームのクラス内に書かれているものとし、OnLoadメソッドでToolStripContainerとToolStripの作成と配置を行っています。ToolStripはToolStripContainerの上部に1つだけ追加し、ToolStripにはボタンを1つだけ追加しています。また、ToolStripContainerの右側にはToolStripが移動できないようにしています(左と下には移動できる)。(なお、このコードを実行するには、「Save.bmp」が必要です。画像がない場合は、「表示する画像の設定」の部分を削除してください。)

#code(vbnet){{
Private toolStripContainer1 As ToolStripContainer
Private toolStrip1 As ToolStrip
Private toolStripButton1 As ToolStripButton

Protected Overrides Sub OnLoad(ByVal e As EventArgs)
    MyBase.OnLoad(e)

    'ToolStripContainerの作成
    toolStripContainer1 = New ToolStripContainer()
    'フォームいっぱいに広げる
    toolStripContainer1.Dock = DockStyle.Fill
    '右端だけドッキングできないようにする
    toolStripContainer1.RightToolStripPanelVisible = False

    'ToolStripの作成
    toolStrip1 = New ToolStrip()

    'ToolStripButtonの作成
    toolStripButton1 = New ToolStripButton()
    '画像と文字列を表示する
    toolStripButton1.DisplayStyle = _
        ToolStripItemDisplayStyle.ImageAndText
    '表示する画像の設定
    toolStripButton1.Image = New Bitmap("Save.bmp")
    toolStripButton1.ImageTransparentColor = _
        System.Drawing.Color.Magenta
    '表示する文字列の設定
    toolStripButton1.Text = "保存"
    'クリックされた時のイベントハンドラ
    AddHandler toolStripButton1.Click, _
        AddressOf toolStripButton1_Click

    'toolStrip1にアイテムを追加
    toolStrip1.Items.Add(toolStripButton1)

    'toolStripContainer1の上にtoolStrip1を追加
    toolStripContainer1.TopToolStripPanel.Join(toolStrip1)
    'または、
    'toolStripContainer1.TopToolStripPanel.Controls.Add(toolStrip1)
    'フォームにtoolStripContainer1を追加
    Me.Controls.Add(toolStripContainer1)
End Sub

Private Sub toolStripButton1_Click(ByVal sender As Object, _
    ByVal e As EventArgs)
    MessageBox.Show("toolStripButton1が押されたよ")
End Sub
}}

#code(csharp){{
private ToolStripContainer toolStripContainer1;
private ToolStrip toolStrip1;
private ToolStripButton toolStripButton1;

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

    //ToolStripContainerの作成
    toolStripContainer1 = new ToolStripContainer();
    //フォームいっぱいに広げる
    toolStripContainer1.Dock = DockStyle.Fill;
    //右端だけドッキングできないようにする
    toolStripContainer1.RightToolStripPanelVisible = false;

    //ToolStripの作成
    toolStrip1 = new ToolStrip();

    //ToolStripButtonの作成
    toolStripButton1 = new ToolStripButton();
    //画像と文字列を表示する
    toolStripButton1.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
    //表示する画像の設定
    toolStripButton1.Image = new Bitmap("Save.bmp");
    toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
    //表示する文字列の設定
    toolStripButton1.Text = "保存";
    //クリックされた時のイベントハンドラ
    toolStripButton1.Click += new EventHandler(toolStripButton1_Click);

    //toolStrip1にアイテムを追加
    toolStrip1.Items.Add(toolStripButton1);

    //toolStripContainer1の上にtoolStrip1を追加
    toolStripContainer1.TopToolStripPanel.Join(toolStrip1);
    //または、
    //toolStripContainer1.TopToolStripPanel.Controls.Add(toolStrip1);

    //フォームにtoolStripContainer1を追加
    this.Controls.Add(toolStripContainer1);
}

private void toolStripButton1_Click(object sender, EventArgs e)
{
    MessageBox.Show("toolStripButton1が押されたよ");
}
}}

***ToolStripContainerの代わりにToolStripPanelを使う [#n3b411d8]

ToolStripContainerコントロールは、上下左右に1つずつ計4つのToolStripPanelコントロールと、中央に1つのToolStripContentPanelコントロールを持ったコントロールです。ToolStripがToolStripContainerの自由な位置に移動できるのは、実はToolStripPanelコントロールのおかげです。ですので、例えばフォームの上部だけにToolStripを配置したい場合は、わざわざToolStripContainerを使わなくても、1つのToolStripPanelをフォームの上部に配置するだけで十分です。

さらにMDIの親フォームでは、ToolStripContainerを使用すると真ん中はToolStripContentPanelとなっているため、子フォームを表示する場所がなくなってしまいます。よってMDI親フォームではToolStripContainerではなく、ToolStripPanelを使用すべきです。

ToolStripPanelコントロールは、デフォルトでは、VS2005のツールボックスにありません。ツールボックスにToolStripPanelを表示するには、ツールボックスで右クリックしてコンテキストメニューを表示し、「アイテムの選択」を選択し、「ToolStripPanel」にチェックをつけて、OKをクリックします。

もちろんデザイナを使用せずに、自分でコードを書くことによってもToolStripPanelをフォームに配置できます。

以下にフォームの上部にToolStripPanelを1つだけ配置した例を示します。(このコードを実行するには、「Save.bmp」が必要です。画像がない場合は、「表示する画像の設定」の部分を削除してください。)

#code(vbnet){{
Private topToolStripPanel As ToolStripPanel
Private toolStrip1 As ToolStrip
Private toolStripButton1 As ToolStripButton

Protected Overrides Sub OnLoad(ByVal e As EventArgs)
    MyBase.OnLoad(e)

    'ToolStripPanelの作成
    topToolStripPanel = New ToolStripPanel()
    '水平方向にToolStripを並べるようにする
    topToolStripPanel.Orientation = Orientation.Horizontal
    '上部に配置する
    topToolStripPanel.Dock = DockStyle.Top

    'ToolStripの作成
    toolStrip1 = New ToolStrip()

    'ToolStripButtonの作成
    toolStripButton1 = New ToolStripButton()
    '画像と文字列を表示する
    toolStripButton1.DisplayStyle = _
        ToolStripItemDisplayStyle.ImageAndText
    '表示する画像の設定
    toolStripButton1.Image = New Bitmap("Save.bmp")
    toolStripButton1.ImageTransparentColor = _
        System.Drawing.Color.Magenta
    '表示する文字列の設定
    toolStripButton1.Text = "保存"
    'クリックされた時のイベントハンドラ
    AddHandler toolStripButton1.Click, _
        AddressOf toolStripButton1_Click

    'toolStrip1にアイテムを追加
    toolStrip1.Items.Add(toolStripButton1)

    'topToolStripPanelの上にtoolStrip1を追加
    topToolStripPanel.Join(toolStrip1)

    'フォームにtoolStripContainer1を追加
    Me.Controls.Add(topToolStripPanel)
End Sub

Private Sub toolStripButton1_Click(ByVal sender As Object, _
    ByVal e As EventArgs)
    MessageBox.Show("toolStripButton1が押されたよ")
End Sub
}}

#code(csharp){{
private ToolStripPanel topToolStripPanel;
private ToolStrip toolStrip1;
private ToolStripButton toolStripButton1;

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

    //ToolStripPanelの作成
    topToolStripPanel = new ToolStripPanel();
    //水平方向にToolStripを並べるようにする
    topToolStripPanel.Orientation = Orientation.Horizontal;
    //上部に配置する
    topToolStripPanel.Dock = DockStyle.Top;

    //ToolStripの作成
    toolStrip1 = new ToolStrip();

    //ToolStripButtonの作成
    toolStripButton1 = new ToolStripButton();
    //画像と文字列を表示する
    toolStripButton1.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
    //表示する画像の設定
    toolStripButton1.Image = new Bitmap("Save.bmp");
    toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
    //表示する文字列の設定
    toolStripButton1.Text = "保存";
    //クリックされた時のイベントハンドラ
    toolStripButton1.Click += new EventHandler(toolStripButton1_Click);

    //toolStrip1にアイテムを追加
    toolStrip1.Items.Add(toolStripButton1);

    //topToolStripPanelの上にtoolStrip1を追加
    topToolStripPanel.Join(toolStrip1);

    //フォームにtoolStripContainer1を追加
    this.Controls.Add(topToolStripPanel);
}

private void toolStripButton1_Click(object sender, EventArgs e)
{
    MessageBox.Show("toolStripButton1が押されたよ");
}
}}

-[[方法 : ToolStripPanel を MDI で使用する>http://msdn2.microsoft.com/ja-jp/library/ms229727.aspx]]
-[[方法 : ToolStripPanel コントロールを持つ MDI フォームを作成する>http://msdn2.microsoft.com/ja-jp/library/ms229722.aspx]]

***MenuStripやStatusStripを移動できるようにする [#s772e54c]

デフォルトでは、MenuStripやStatusStripコントロールにはコントロールを移動するためのグリップが表示されませんので、ユーザーがこれらのコントロールを自由な位置に移動させることができません。

このグリップを表示するには、GripStyleプロパティをVisibleにし、さらに、LayoutStyleプロパティをStackWithOverflowやHorizontalStackWithOverflowやVerticalStackWithOverflowとします。

***同じ行にToolStripが2つ並ばないようにする [#xf0214ba]

デフォルトでは、ToolStripPanelの同じ行に複数のToolStripを配置することができます。一行をまるまる1つのToolStripが占領し、別のToolStripが同じ行に配置されないようにするには、ToolStripのStretchプロパティをTrueにします。これにより、ToolStripはToolStripPanelの幅いっぱいに広がります。

逆にMenuStripやStatusStripはデフォルトでは同じ行に別のToolStripを配置できないようになっていますが、StretchプロパティをFalseにすることにより、これを可能にします。

***ToolStripPanelにあるToolStripの位置をプログラムで変更する [#vd3ff47a]

ToolStripPanelにあるToolStripの位置は、Locationプロパティで変更ができます。しかし、ToolStripPanelはToolStripを勝手に整列させるため、ToolStripの位置と大きさはLocationやSizeで指定した値と全く同じになるとは限りません。

ToolStripPanelがどのようにToolStripを整列れるさせるかは、実際にマウスでToolStripをつかんで移動させてみると分かります。つまり、ToolStripはToolStripPanel内のどこかの行に属することになるため、その中間の位置に配置できませんし(上か下のどちらかの行に配置されます)、ToolStripが一つもない行も許されず、そのような位置を指定しても行が詰められます(例えば、現在1行しかないのに3行目に移動させようとしても、2行目にしか移動しせん)。また、ToolStripPanelの幅以上の位置を指定しても、適当な位置に戻されてしまいます。

ToolStrip.Locationプロパティ以外では、ToolStripPanel.Joinメソッドを使ってもToolStripの位置を変更できます。JoinメソッドはToolStripPanelにToolStripを追加するためのメソッドですが、位置を変更するためにも使用できます。Joinメソッドでは、座標だけでなく、移動先の行を指定することもできますし、別のToolStripPanelに移動させるためにも使用できます。

***ToolStripとToolStripItemの位置を保存、復元する [#j30a7c58]

.NET Framework 2.0では、ToolStripの位置を保存、復元する簡単な方法が用意されています。

ToolStripManager.SaveSettingsメソッドでToolStripの位置を保存し、ToolStripManager.LoadSettingsメソッドで復元することができます。

ToolStripManager.SaveSettingsで保存される情報は、ToolStripがあるToolStripPanel、ToolStripのサイズと位置、表示しているか非表示か、ToolStrip内のToolStripItemの順番です。つまりLoadSettingsメソッドでは、これらが復元されます。

下の例では、フォームのOnLoadメソッドでToolStripの位置を復元し、OnClosedメソッドで保存しています。なおフォームのサイズを復元している場合は、フォームのサイズを復元した後にLoadSettingsを呼び出した方が良いでしょう。

#code(vbnet){{
Protected Overrides Sub OnLoad(ByVal e As EventArgs)
    MyBase.OnLoad(e)
    'ToolStripの位置を復元
    ToolStripManager.LoadSettings(Me)
End Sub

Protected Overrides Sub OnClosed(ByVal e As EventArgs)
    MyBase.OnClosed(e)
    'ToolStripの位置を保存
    ToolStripManager.SaveSettings(Me)
End Sub
}}

#code(csharp){{
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    //ToolStripの位置を復元
    ToolStripManager.LoadSettings(this);
}

protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);
    //ToolStripの位置を保存
    ToolStripManager.SaveSettings(this);
}
}}

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

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

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

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

この問題は、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#のみです。)

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

#code(csharp){{
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);
            }
        }
    }
}
}}

***特定のToolStripを指定したToolStripPanelにドッキングできないようにする [#l1b5fddb]

ToolStripは、同じフォーム内であれば、どこのToolStripPanelにも移動できてしまいます。例えば、上下のToolStripPanelには移動できるが、左右のToolStripPanelには移動できないようにできないものでしょうか?

基本的には、ToolStripPanelのControlAddedイベントで受け入れ可能なToolStripかを調べ、受け入れられなければToolStripPanelのControlsから削除し、元のToolStripPanelに戻すという方法になりそうです。しかし、ToolStripが元のToolStripPanelから削除される直前に発生するイベントがなく、削除されて始めて分かるため、元に戻すToolStripの位置をあらかじめ記憶しておかなければなりません。

しかも、たとえそのようなコードを書いたとしても、正常に動作しません。ControlAddedイベントが発生しても、この時ToolStripはドラッグ中なので、この時点でToolStripを戻してもうまくいかないようです。つまり、ToolStripがドロップされ、ToolStripの移動が完全に終了してから、ToolStripを元に戻さなければなりません。

ToolStripの移動が終了したことを知るための方法はないかと探したところ、.NET Framework 2.0から追加されたControl.MouseCaptureChangedイベントあたりが使えそうです。

以上のような方針で、ToolStripがToolStripContainer(toolStripContainer1)のBottomToolStripPanelに移動できないようにするコードを書いてみました。ここでは、ToolStripのLocationChangedイベントでToolStripの位置を記憶するようにしています。何回か試した範囲では、どうにかうまくいっているようです。

#code(vbnet){{
'ToolStripの位置
Private toolStripParent As ToolStripPanel = Nothing
Private toolStripLocation As Point = Point.Empty

'LocationChangedイベントハンドラ
Private Sub toolStrip1_LocationChanged(ByVal sender As Object, _
    ByVal e As EventArgs) Handles ToolStrip1.LocationChanged
    Dim ts As ToolStrip = CType(sender, ToolStrip)

    If ToolStripContainer1.BottomToolStripPanel.Equals(ts.Parent) _
        AndAlso Not (toolStripParent Is Nothing) Then
        'BottomToolStripPanelに移動させた時
        'ここでToolStripをもとに戻すことができればよいのだが...
    ElseIf Not (ts.Parent Is Nothing) _
        AndAlso TypeOf ts.Parent Is ToolStripPanel Then
        'ToolStripの位置を記憶する
        toolStripParent = CType(ts.Parent, ToolStripPanel)
        toolStripLocation = ts.Location
    End If
End Sub

'MouseCaptureChangedイベントハンドラ
Private Sub toolStrip1_MouseCaptureChanged(ByVal sender As Object, _
    ByVal e As EventArgs) Handles ToolStrip1.MouseCaptureChanged
    Dim ts As ToolStrip = CType(sender, ToolStrip)

    If ToolStripContainer1.BottomToolStripPanel.Equals(ts.Parent) _
        AndAlso Not (toolStripParent Is Nothing) Then
        'BottomToolStripPanelに移動させた時
        'ToolStripを元の位置に戻す
        toolStripParent.Join(ts, toolStripLocation)
    End If
End Sub
}}

#code(csharp){{
//ToolStripの位置
private ToolStripPanel toolStripParent = null;
private Point toolStripLocation = Point.Empty;

//LocationChangedイベントハンドラ
private void toolStrip1_LocationChanged(object sender, EventArgs e)
{
    ToolStrip ts = (ToolStrip)sender;

    if (toolStripContainer1.BottomToolStripPanel.Equals(ts.Parent)
        && toolStripParent != null)
    {
        //BottomToolStripPanelに移動させた時
        //ここでToolStripをもとに戻すことができればよいのだが...
    }
    else if (ts.Parent != null && ts.Parent is ToolStripPanel)
    {
        //ToolStripの位置を記憶する
        toolStripParent = (ToolStripPanel)ts.Parent;
        toolStripLocation = ts.Location;
    }
}

//MouseCaptureChangedイベントハンドラ
private void toolStrip1_MouseCaptureChanged(object sender, EventArgs e)
{
    ToolStrip ts = (ToolStrip)sender;

    if (toolStripContainer1.BottomToolStripPanel.Equals(ts.Parent)
        && toolStripParent != null)
    {
        //BottomToolStripPanelに移動させた時
        //ToolStripを元の位置に戻す
        toolStripParent.Join(ts, toolStripLocation);
    }
}
}}

もっと良い方法をご存知の方がいらっしゃれば、ぜひ教えてください。

残念ながら、今回は紹介したいことの3分の1も紹介できませんでした。ToolStripとToolStripContainerコントロールのTipsは、次回も続きます。

//これより下は編集しないでください
#pageinfo(,2010-03-19 (金) 02:04:19,DOBON!,2010-03-19 (金) 02:16:06,DOBON!)
[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]