WinForms でドッキングコントロール(DockPanelSuite)を扱う
WPF/AvalonDock の WinForms 版みたいなやつね。以下備忘録。コード見てね。
イメージ
サンプルコード
using System; using System.Data; using System.Linq; using System.Windows.Forms; using WeifenLuo.WinFormsUI.Docking; /* * 本体 * nuget: DockPanelSuite * author: Weifen Luo * * スキン(見た目のテーマ) * nuget: DockPanelSuite.ThemeVS2015 とか * author: Weifen Luo * * * */ namespace WindowsFormsApp29 { public partial class Form1 : Form { public Form1() { InitializeComponent(); //Test1(); //Test2(); Test3(); } private void Test1() { // DockPanel コンテナーコントロールを配置 var dockPanel1 = new DockPanel(); dockPanel1.ShowDocumentIcon = true; dockPanel1.Dock = DockStyle.Fill; dockPanel1.DocumentStyle = DocumentStyle.DockingWindow; Controls.Add(dockPanel1); // DockPanelの中に、子コントロールの DockContent コントロールを配置 // Form2 の継承元クラスを DockContent に変えて、(一応いったんリビルドした後、デザイナー開いて)Button を配置している // Form2 : WeifenLuo.WinFormsUI.Docking.DockContent // 左ペイン var left1 = new Form2(); left1.TabText = nameof(left1); left1.Show(dockPanel1, DockState.DockLeft); // 右ペイン var right1 = new Form2(); right1.TabText = nameof(right1); right1.Show(dockPanel1, DockState.DockRight); // 下ペイン var bottom1 = new Form2(); bottom1.TabText = nameof(bottom1); bottom1.Show(dockPanel1, DockState.DockBottom); // ドキュメントペイン var center1 = new Form2(); center1.TabText = nameof(center1); center1.Show(dockPanel1, DockState.Document); var center2 = new Form2(); center2.TabText = nameof(center2); center2.Show(dockPanel1, DockState.Document); // 下側ペインよりも左側ペインを手前に表示したい問題の対応 dockPanel1.UpdateDockWindowZOrder(DockStyle.Left, true); } private void Test2() { // DockPanel コンテナーコントロールを配置 var dockPanel1 = new DockPanel(); dockPanel1.ShowDocumentIcon = true; dockPanel1.Dock = DockStyle.Fill; dockPanel1.DocumentStyle = DocumentStyle.DockingWindow; Controls.Add(dockPanel1); // DockPanelの中に、子コントロールの DockContent コントロールを配置 // Form2 の継承元クラスを DockContent に変えて、(一応いったんリビルドした後、デザイナー開いて)Button を配置している // Form2 : WeifenLuo.WinFormsUI.Docking.DockContent // 左ペイン var left1 = new Form2(); left1.TabText = nameof(left1); left1.Show(dockPanel1, DockState.DockLeft); // 指定ペインの前に挿入 var left2 = new Form2(); left2.TabText = nameof(left2); left2.Show(left1.Pane, left1); var left3 = new Form2(); left3.TabText = nameof(left3); left3.Show(left2.Pane, DockAlignment.Bottom, 0.5); var left4 = new Form2(); left4.TabText = nameof(left4); left4.Show(dockPanel1, DockState.DockLeft); // 右ペイン var right1 = new Form2(); right1.TabText = nameof(right1); right1.Show(dockPanel1, DockState.DockRight); var right2 = new Form2(); right2.TabText = nameof(right2); right2.Show(dockPanel1, DockState.DockRight); // 下ペイン var bottom1 = new Form2(); bottom1.TabText = nameof(bottom1); bottom1.Show(dockPanel1, DockState.DockBottom); var bottom2 = new Form2(); bottom2.TabText = nameof(bottom2); bottom2.Show(dockPanel1, DockState.DockBottom); // ドキュメントペイン var center1 = new Form2(); center1.TabText = nameof(center1); center1.Show(dockPanel1, DockState.Document); var center2 = new Form2(); center2.TabText = nameof(center2); center2.Show(dockPanel1, DockState.Document); // 下側ペインよりも左側ペインを手前に表示したい問題の対応 dockPanel1.UpdateDockWindowZOrder(DockStyle.Left, true); // 多分、各画面同士で、データのやり取りがあると思う、その操作方法 // シングルトンクラスとかで、あちこちから呼び出せれば楽かな? // 上下左右のペイン //var items1a = dockPanel1.DockWindows; //var items1b = dockPanel1.DockWindows.OfType<DockWindow>().ToList(); //var items1c = dockPanel1.DockWindows.OfType<Form2>().ToList(); // 上下左右、ドキュメントの全ペイン1 var items1d = dockPanel1.DockWindows.OfType<DockWindow>() .SelectMany(x => x.Controls.OfType<DockPane>()) .SelectMany(x => x.Controls.OfType<Form2>()) .Select(x => x.TabText) .ToList(); // 上下左右、ドキュメントの全ペイン2 var items1e = dockPanel1.DockWindows.OfType<DockWindow>() .SelectMany(x => x.NestedPanes.OfType<DockPane>()) .SelectMany(x => x.Contents.OfType<Form2>()) .Select(x => x.TabText) .ToList(); // 上下左右のペイン var items1f = dockPanel1.DockWindows.OfType<DockWindow>() .SelectMany(x => x.NestedPanes.OfType<DockPane>()) .SelectMany(x => x.Contents.OfType<Form2>()) .Where(x => x.DockState != DockState.Document) .Select(x => x.TabText) .ToList(); // 独立画面中のフロートペイン //var items2a = dockPanel1.FloatWindows; var items2b = dockPanel1.FloatWindows.OfType<FloatWindow>() .SelectMany(x => x.Controls.OfType<DockPane>()) .SelectMany(x => x.Controls.OfType<Form2>()) .Select(x => x.TabText) .ToList(); // 中央のドキュメントペイン //var items3a = dockPanel1.Documents; var items3b = dockPanel1.Documents.OfType<DockContent>().ToList(); var items3c = dockPanel1.Documents.OfType<Form2>().ToList(); var items3d = dockPanel1.Documents.OfType<DockContent>() .Select(x => x.TabText) .ToList(); Console.WriteLine(""); } private void Test3() { // DockPanel コンテナーコントロールを配置 var dockPanel1 = new DockPanel(); dockPanel1.ShowDocumentIcon = true; dockPanel1.Dock = DockStyle.Fill; dockPanel1.DocumentStyle = DocumentStyle.DockingWindow; // 下ペインよりも左ペインを手前にする dockPanel1.UpdateDockWindowZOrder(DockStyle.Left, true); // 右ペイン幅を固定幅にする(小数点の場合はパーセント扱い。整数の場合は固定値) dockPanel1.DockRightPortion = 250; //dockPanel1.Theme = new VS2015LightTheme(); dockPanel1.Theme = new VS2015BlueTheme(); Controls.Add(dockPanel1); // DockPanelの中に、子コントロールの DockContent コントロールを配置 // Form2 の継承元クラスを DockContent に変えて、(一応いったんリビルドした後、デザイナー開いて)Button を配置している // Form2 : WeifenLuo.WinFormsUI.Docking.DockContent // 左ペイン var left1 = new Form2(); left1.TabText = nameof(left1); left1.Show(dockPanel1, DockState.DockLeft); // 右ペイン var right1 = new Form2(); right1.TabText = nameof(right1); right1.Show(dockPanel1, DockState.DockRight); // 下ペイン var bottom1 = new Form2(); bottom1.TabText = nameof(bottom1); bottom1.Show(dockPanel1, DockState.DockBottom); // ドキュメントペイン var center1 = new Form2(); center1.TabText = nameof(center1); center1.Show(dockPanel1, DockState.Document); var center2 = new Form2(); center2.TabText = nameof(center2); center2.Show(dockPanel1, DockState.Document); // 下側ペインよりも左側ペインを手前に表示したい問題の対応 dockPanel1.UpdateDockWindowZOrder(DockStyle.Left, true); } } }
その他
・階層関係
DockPanel(コンテナーコントロール) + DockWindows プロパティ(DockWindowCollection / ReadOnlyCollection<DockWindow>) + DockWindow.NestedPanes プロパティ(NestedPaneCollection / ReadOnlyCollection<DockPane>) + DockPane.Contents プロパティ(DockContentCollection / ReadOnlyCollection<IDockContent>) + Documents プロパティ(IEnumerable<IDockContent>) + FloatWindows プロパティ(FloatWindowCollection / ReadOnlyCollection<FloatWindow>) + FloatWindow.NestedPanes プロパティ(NestedPaneCollection / ReadOnlyCollection<DockPane>) + DockPane.Contents プロパティ(DockContentCollection / ReadOnlyCollection<IDockContent>)
つまり、
DockWindow / FloatWindow + DockPane + IDockContent
直接扱うのは、DockPanel コントロールと DockContent コントロールを継承した画面だが、各画面同士でやり取りする際は継承関係を知る必要あるかも。他にもたくさんプロパティやメソッドがあるが、とりあえずは基本の上記。
・各画面(DockContent 継承先の画面)の管理
DockContent.Text
よりもDockContent.TabText
の方が表示的に優先されるみたい。
インスタンス生成時、DockContent.HideOnClose = true
しておいて、閉じてもインスタンス破棄されないようにしておく。そうすると閉じた際、DockContent.IsHidden = true
になるので、DockContent.Show()
かDockContent.Activate()
を呼び出す。
・(2020/03/22追記)ShowDialog() しているDockPanel を使った画面
を閉じた際、呼び出し元画面が不安定になる現象の対応
画面を閉じる際に(FormClosing/FormClosed)、自前でペインを閉じたら直った。
// 例えばドキュメントペイン。他にもサイドのペインも。 var panes = dockPanel1.Documents.OfType<DockContent>().ToList(); for (var i = panes.Count - 1; i >= 0; i--) { panes[i].HideOnClose = false; panes[i].Close() }