┏第69号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■.NET Tips ・FlowLayoutPanelコントロール -AnchorとDockプロパティ -FlowBreakプロパティ -コントロールの順番を変更する ・TableLayoutPanelコントロール -TableLayoutPanelコントロールにコントロールを追加する -子コントロールの位置の取得(GetCellPositionと GetPositionFromControlの違い) -Controls.Addで指定した位置に配置できない?! -ColumnCountとRowCountは実際のTableLayoutPanelの列数と行数を 表すものではない ─────────────────────────────── ─────────────────────────────── ■.NET Tips ─────────────────────────────── 今回は、.NET Framework 2.0から追加されたWindowsアプリケーショ ンのコントロール、FlowLayoutPanelとTableLayoutPanelコントロー ルについて説明します。これらのコントロールはコンテナとして働き、 複数のコントロールを整列させたい時に使用します。 ─────────────────────────────── ●FlowLayoutPanelコントロール FlowLayoutPanelコントロールについて、MSDNでは次のように説明さ れています。 「FlowLayoutPanel コントロールは、その内容を水平または垂直のフ ロー方向に整列させます。内容は次の行または次の列に折り返すこと ができます。また、折り返さずにクリップすることもできます。」 [URL]FlowLayoutPanel コントロール (Windows フォーム) http://msdn2.microsoft.com/ja-jp/library/zah8ywcc.aspx 言葉で説明するより、実際に使ってみるのが一番早いというわけで、 次のURLにFlowLayoutPanelを使ったサンプル(FlowLayoutPanel1.exe) を置いておきます。(ソースコードも置いておきます。)実行するに は、.NET Framework 2.0がインストールされている必要があります。 (VB.NETのコードはありません。また、これらのファイルは、1ヶ月 ほどで削除します。) [URL]サンプル「FlowLayoutPanel1.exe」 http://dobon.net/vb/dotnet/control/files/FlowLayoutPanel1.exe [URL]ソース http://dobon.net/vb/dotnet/control/files/FlowLayoutPanel1.zip このサンプルは、フォームにFlowLayoutPanelコントロールを配置し、 DockプロパティをFillとし、Buttonコントロールを15個配置したもの です。フォームの大きさを変更させると、Buttonコントロールが右端 で折り返されて整列することが確認できるでしょう。 FlowLayoutPanelコントロールには、重要なプロパティとして、 FlowDirectionとWrapContentsがあります。FlowDirectionプロパティ は、整列の方向を指定するために使用します。また、WrapContentsプ ロパティは、FlowLayoutPanelに配置したコントロールを折り返して 整列させるかを指定するために使用します。 サンプル「FlowLayoutPanel1.exe」では、FlowDirectionと WrapContentsプロパティを試すため、メニューを使って FlowLayoutPanelコントロールのFlowDirectionとWrapContentsプロパ ティの値を簡単に変更できるようになっています。実際にこれらの値 を変更させるとどのように変化するのかを試してみてください。 ★AnchorとDockプロパティ FlowLayoutPanelコントロールに配置された子コントロールは、 AnchorとDockプロパティに、通常とは少し異なる特別な役割が与えら れています。これに関しては、MSDNで説明されています。 [URL]方法 : FlowLayoutPanel コントロールで子コントロールを固定 およびドッキングする http://msdn2.microsoft.com/ja-jp/library/ms171633.aspx これを実際に確認するためのサンプルが次に紹介する「 FlowLayoutPanel2.exe」です。 [URL]サンプル「FlowLayoutPanel2.exe」 http://dobon.net/vb/dotnet/control/files/FlowLayoutPanel2.exe [URL]ソース http://dobon.net/vb/dotnet/control/files/FlowLayoutPanel2.zip このサンプルでは、様々な大きさのButtonコントロールが FlowLayoutPanelコントロールに配置されています。さらに、Button コントロールを右クリックすることにより、コンテキストメニューが 表示され、ButtonのAnchorとDockプロパティを簡単に変更できるよう になっています。 例えば、FlowLayoutPanelのFlowDirectionプロパティがLeftToRight の時、ButtonのAnchorをTopにすると、Buttonはその列の一番上に配 置されるようになります。AnchorをBottomにすると、逆に一番下に配 置されます。TopとBottom両方を指定すると、列の幅いっぱいに広が ります。Noneでは中央に配置されます。LeftとRightは、この場合は、 関係ありません。 Dockプロパティも同様に、Topで一番上、Bottomで一番下、Fillで幅 いっぱいに配置されるようになります。 ★FlowBreakプロパティ さらにこのサンプルでは、FlowLayoutPanelコントロールに配置され たコントロールのFlowBreakプロパティを変更することもできます。 FlowBreakプロパティをTrueとすることにより、そのコントロールの 次で強制的に折り返して整列されるようになります。 FlowBreakプロパティはVS 2005のデザイナのプロパティウィンドウで 変更することができますが、実際のコントロールのクラスに FlowBreakというプロパティが存在するわけではありません。実際に はFlowLayoutPanelコントロールのSetFlowBreakメソッドを使って指 定します。例えば、FlowLayoutPanelコントロール「 FlowLayoutPanel1」に配置されたButtonコントロール「Button1」の FlowBreakプロパティをTrueにするには、次のようにします。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ FlowLayoutPanel1.SetFlowBreak(Button1, True) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ FlowLayoutPanel1.SetFlowBreak(Button1, true); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ また、Button1のFlowBreakの状態を調べるには、FlowLayoutPanel. GetFlowBreakメソッドを使います。 ★コントロールの順番を変更する FlowLayoutPanelに配置されるコントロールの順番は、 FlowLayoutPanel.Controlsコレクションの順番で決まるようです。よ って、コントロールの順番を変更するには、変更するコントロール以 降のコントロールをすべて削除してから、追加しなおさなければなら ないようです。 サンプル「FlowLayoutPanel2.exe」で、コントロールを先頭に移動す る例を紹介しています。以下にその抜粋を示します。currentButton というコントロールを移動させています。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ '移動先の位置 Dim newIndex As Integer = 0 Dim conts As New List(Of Control) FlowLayoutPanel1.SuspendLayout() 'flowLayoutPanel1内のコントロールを記憶&削除 Dim i As Integer For i = FlowLayoutPanel1.Controls.Count - 1 To newIndex Step -1 If Not FlowLayoutPanel1.Controls(i).Equals(currentButton) Then conts.Insert(0, FlowLayoutPanel1.Controls(i)) End If FlowLayoutPanel1.Controls.RemoveAt(i) Next i '移動するコントロールがまだあるときは、削除 If FlowLayoutPanel1.Controls.IndexOf(currentButton) > -1 Then FlowLayoutPanel1.Controls.Remove(currentButton) End If '移動するコントロールを追加 FlowLayoutPanel1.Controls.Add(currentButton) '残りのコントロールを追加 FlowLayoutPanel1.Controls.AddRange(conts.ToArray()) FlowLayoutPanel1.ResumeLayout() ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //移動先の位置 int newIndex = 0; List conts = new List(); flowLayoutPanel1.SuspendLayout(); //flowLayoutPanel1内のコントロールを記憶&削除 for (int i = flowLayoutPanel1.Controls.Count - 1; i >= newIndex; i--) { if (!flowLayoutPanel1.Controls[i].Equals(currentButton)) conts.Insert(0, flowLayoutPanel1.Controls[i]); flowLayoutPanel1.Controls.RemoveAt(i); } //移動するコントロールがまだあるときは、削除 if (flowLayoutPanel1.Controls.IndexOf(currentButton) > -1) flowLayoutPanel1.Controls.Remove(currentButton); //移動するコントロールを追加 flowLayoutPanel1.Controls.Add(currentButton); //残りのコントロールを追加 flowLayoutPanel1.Controls.AddRange(conts.ToArray()); flowLayoutPanel1.ResumeLayout(); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ─────────────────────────────── ●TableLayoutPanelコントロール TableLayoutPanelコントロールは、まるでHTMLのTABLEタグのような コントロールと言えば分かりやすいでしょうか。MSDNでは、次のよう に説明されています。 TableLayoutPanel コントロールは、実行時に比例的なサイズ変更機 能を提供するため、フォームのサイズ変更に合わせてレイアウトを滑 らかに変更できます。このため、TableLayoutPanel コントロールは、 データ入力フォームやアプリケーションのローカライズなどに適して います。 [URL]TableLayoutPanel コントロール (Windows フォーム) http://msdn2.microsoft.com/ja-jp/library/3a1tbfwd.aspx MSDNの「TableLayoutPanel コントロールの推奨される手順」では、 TableLayoutPanelコントロールをどのようなケースで使用し、どのよ うなケースでは使用すべきではないかが説明されています。 [URL]TableLayoutPanel コントロールの推奨される手順 http://msdn2.microsoft.com/ja-jp/library/ms171689.aspx これによると、親フォームのサイズ変更や、ローカリゼーションに伴 いコントロールに表示するテキストの長さが変更されるようなケース でTableLayoutPanelコントロールを使うのが有効であるということで す。逆にTableLayoutPanelコントロールのDockプロパティをFillにす る、TableLayoutPanelに別のTableLayoutPanelを配置して入れ子にす る、TableLayoutPanelを配置したフォームを継承したフォームでさら にコントロールを追加する、列がレベルとテキストの2つしかない単 純なフォームで使用するなどといったことは推奨されていません。こ のような推奨事項を守る限り、TableLayoutPanelコントロールの使い 道はごく限られそうです。(巷に出回っているTableLayoutPanelコン トロールのサンプルは、これらの推奨事項を無視しているものがほと んどですので、注意が必要です。) TableLayoutPanelコントロールの使い方は、MSDNの「 TableLayoutPanel コントロール (Windows フォーム)」などに詳しい です。しかしほとんどがVisual Studioのフォームデザイナを使った 場合の説明であり、コードによる説明はほとんどありません。そこで ここでは、実行時に行うにはどうするかを中心に説明します。 なお、TableLayoutPanelコントロールの実際の挙動を確かめるための サンプル(TableLayoutPanel1.exe)も用意しました。 [URL]サンプル「TableLayoutPanel1.exe」 http://dobon.net/vb/dotnet/control/files/TableLayoutPanel1.exe [URL]ソース http://dobon.net/vb/dotnet/control/files/TableLayoutPanel1.zip ★TableLayoutPanelコントロールにコントロールを追加する MSDNでは、フォームデザイナを使った方法が次のページなどで説明さ れています。 [URL]チュートリアル : TableLayoutPanel を使用した Windows フォー ム上のコントロールの配置 http://msdn2.microsoft.com/ja-jp/library/w4yc3e8c.aspx [URL]チュートリアル : データ入力用のサイズ変更可能な Windows フォームの作成 http://msdn2.microsoft.com/ja-jp/library/991eahec.aspx TableLayoutPanelコントロール内にコントロールを配置するには、 Controls.Addメソッドを使います。このメソッドではコントロールを 挿入するセルの位置を指定することができます。 [URL]TableLayoutControlCollection.Add メソッド http://msdn2.microsoft.com/ja-jp/library/system.windows.forms.tablelayoutcontrolcollection.add.aspx TableLayoutPanelコントロールは一つのセルに一つのコントロールし か入れることができません(セルにPanelを配置し、そこに複数のコ ントロールを配置することはできます)。よって指定した挿入位置に すでにコントロールがあるときは、それ以降のコントロールが押し出 されるように次のセルに移動します。 Controls.Addでは挿入位置を指定しなくても大丈夫です。挿入位置を 指定しなかった場合は、右上の空いているセルから順番に配置されま す。((-1, -1)を指定したことになります。) すべてのセルがすでに埋まっている時に新しいコントロールを追加し た時の動作は、TableLayoutPanel.GrowStyleプロパティにより異なり ます。GrowStyleプロパティがAddRowsのとき(デフォルト)は行を拡 張し、AddColumnsのときは列を拡張します。FixedSizeのときは、例 外ArgumentExceptionがスローされます。 次の例では、insertRowとinsertColumnで指定された位置にButtonコ ントロールを追加しています。挿入位置にコントロールがあるか調べ、 無いときのみ挿入しています。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ '挿入位置 Dim insertRow As Integer = 0 Dim insertColumn As Integer = 0 If TableLayoutPanel1.GetControlFromPosition( _ insertColumn, insertRow) Is Nothing Then Dim newButton As New Button() newButton.Text = "button" TableLayoutPanel1.Controls.Add( _ newButton, insertColumn, insertRow) End If ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //挿入位置 int insertRow = 0; int insertColumn = 0; if (tableLayoutPanel1.GetControlFromPosition( insertColumn, insertRow) == null) { Button newButton = new Button(); newButton.Text = "button"; tableLayoutPanel1.Controls.Add(newButton, insertColumn, insertRow); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ サンプル「TableLayoutPanel1.exe」でも、メニューから簡単に Buttonコントロールの追加や、TableLayoutPanel.GrowStyleプロパテ ィの変更を行うことができますので、お試しください。 ★子コントロールの位置の取得(GetCellPositionと GetPositionFromControlの違い) TableLayoutPanelに配置されたコントロールが占有しているセルの位 置を取得する方法としてTableLayoutPanelクラスには、GetColumn、 GetRow、GetCellPosition、GetPositionFromControlといったメソッ ドが用意されています。この内、GetColumn、GetRowと GetCellPositionは同じですが、これらとGetPositionFromControlは 似ているようで全く異なります。 例えば、列数が十分にあるTableLayoutPanel「tableLayoutPanel1」 に次のように2つのボタンを配置したとします。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ tableLayoutPanel1.Controls.Add(button1, 0, 0) tableLayoutPanel1.Controls.Add(button2, 1, 0) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ tableLayoutPanel1.Controls.Add(button1, 0, 0); tableLayoutPanel1.Controls.Add(button2, 1, 0); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このときは、button1に対してGetCellPositionと GetPositionFromControlが返す値は同じ(0,0)になります。また、 button2に対しても両者とも(1,0)となります。 さて、さらにbutton3を次のように追加すると、どうなるでしょうか? ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ tableLayoutPanel1.Controls.Add(button3, 0, 0) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ tableLayoutPanel1.Controls.Add(button3, 0, 0); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ button3が(0,0)に入ることにより、button1は(1,0)に、button2は(2, 0)に追い出されます。このとき、button1とbutton2に対して GetPositionFromControlが返す値は(1,0)と(2,0)になりますが、 GetCellPositionが返す値は(0,0)と(1,0)のままです。button3に対し てGetCellPositionとGetPositionFromControlが返す値は(0,0)ですの で、GetCellPositionが(0,0)を返すコントロールが2つ存在すること になります。 つまり、GetPositionFromControlは現在実際にコントロールが占有し ているセルの位置を返し、GetCellPositionはControls.Add(あるい はTableLayoutPanel.SetCellPositionメソッド)で指定された値を返 すということになります。位置を指定しないでControls.Addを呼び出 したコントロールに対しては、GetCellPositionは(-1,-1)を返します。 VS2005のフォームデザイナのみでコントロールを配置した場合は GetCellPositionとGetPositionFromControlは一致すると考えてよさ そうですが、実行時に配置したコントロールがある場合は、そうなら ない可能性があることを知っておくべきでしょう。 MSDNには、 「GetPositionFromControl メソッドは、位置が LayoutEngine によ って決定される場合でも、control の実際の現在位置を返します。」 とありますが、これはこういう意味だったのです。 [URL]TableLayoutPanel.GetPositionFromControl メソッド http://msdn2.microsoft.com/ja-jp/library/system.windows.forms.tablelayoutpanel.getpositionfromcontrol.aspx ★Controls.Addで指定した位置に配置できない?! 上で紹介した例に、次のようにしてもう一つbuttonを追加してみまし ょう。一体どこに追加されるでしょうか? ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ tableLayoutPanel1.Controls.Add(button4, 1, 0) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ tableLayoutPanel1.Controls.Add(button4, 1, 0); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ちょっと意外ですが、button4は(2,0)の位置、つまりbutton1と button2の間に挿入されます。つまり、GetCellPositionが(0,0)であ るbutton1が優先されるようなのです。 それでは、間違いなく指定した位置のセルにコントロールを配置する にはどうすればいいのかということになると、あまりいい方法が思い つきません。一つの方法として、次のように実際に配置されている位 置をGetCellPositionが返す位置に強制的に設定してしまってから、 コントロールを追加するやり方が考えられます。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Dim c As Control For Each c In TableLayoutPanel1.Controls TableLayoutPanel1.SetCellPosition( _ c, TableLayoutPanel1.GetPositionFromControl(c)) Next c ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ foreach (Control c in tableLayoutPanel1.Controls) { tableLayoutPanel1.SetCellPosition( c, tableLayoutPanel1.GetPositionFromControl(c)); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ★ColumnCountとRowCountは実際のTableLayoutPanelの列数と行数を 表すものではない これもまた分かりにくい話ですが、TableLayoutPanelのColumnCount やRowCountプロパティは、実際の列数や行数と一致するとは限りませ ん。実行時にコントロールをTableLayoutPanelに追加して実際の列数 や行数が拡張された場合でも、ColumnCountやRowCountの値は変更さ れませんので、実際の列数、行数と違いが出てしまいます。つまり、 これらのプロパティはTableLayoutPanelのGrowStyleプロパティが FixedSizeの場合のみ実際の列数や行数と必ず一致し、GrowStyleが AddColumnsまたはAddRowsの場合は、RowCountまたはColumnCountだけ が実際の列数や行数と必ず一致するということになります。 [URL]TableLayoutPanel.ColumnCount プロパティ http://msdn2.microsoft.com/ja-jp/library/system.windows.forms.tablelayoutpanel.columncount.aspx それでは実際の列数と行数を取得するにはどのようにすればよいかと いうと、...これがよく分かりません。TableLayoutPanelに配置され ているすべてのコントロールの実際の位置をGetPositionFromControl で取得して、その最大値とColumnCountまたはRowCountの大きい方が 実際の列数または行数であるとする以外の良い方法が今のところ見つ かりません。 ─────────────────────────────── 今回、予想外に記事が長くなってしまいましたので、きりが良くない のですが、続きは次回とさせていただきます。 =============================== ■ここで示したコードの多くはまずC#で書き、それを「C# to VB.NET Translator」でVB.NETのコードに変換し、修正を加えたものです。 [URL]C# to VB.NET Translator http://authors.aspalliance.com/aldotnet/examples/translate.aspx ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2005-Oct 2006)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2006 DOBON! All rights reserved. ===============================