PowerShell で GUI 開発(WinForms)する際、Size と Location を気にしなくていいように、デザインツールを作った

アドベントカレンダーネタで作った WinForms スタイルのデザインツールを引っ張り出してカスタマイズしてみました。PowerShell だけで書き直したかったのですが断念orz。C# + WinForms のアプリです。

これを、PowerShell だけで完結したかった・・・。そして、やっぱり Visual Studio 無いとビルドできないかな~?CSC コンパイラでビルドできれば、Visual Studio が無い環境でもデザインツールを生成できるのですがどうなんでしょうね?

目次

画面イメージ

f:id:sutefu7:20190831153704p:plain

f:id:sutefu7:20190831153717p:plain

参照追加

今回のサンプルは、VS2017Community/C#/WinForms/.NET Framework 4.7.2で作成しています。以下のdllを参照追加します。

  • System.Design.dll

Form1.Designer.cs

上記のデザイン画面で分からなければ、以下を参考にしてください。

namespace WindowsFormsApp2
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.treeView1 = new System.Windows.Forms.TreeView();
            this.splitContainer2 = new System.Windows.Forms.SplitContainer();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.tabPage1 = new System.Windows.Forms.TabPage();
            this.panel1 = new System.Windows.Forms.Panel();
            this.tabPage2 = new System.Windows.Forms.TabPage();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.splitContainer3 = new System.Windows.Forms.SplitContainer();
            this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
            this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
            this.splitContainer2.Panel1.SuspendLayout();
            this.splitContainer2.Panel2.SuspendLayout();
            this.splitContainer2.SuspendLayout();
            this.tabControl1.SuspendLayout();
            this.tabPage1.SuspendLayout();
            this.tabPage2.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit();
            this.splitContainer3.Panel1.SuspendLayout();
            this.splitContainer3.Panel2.SuspendLayout();
            this.splitContainer3.SuspendLayout();
            this.toolStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // splitContainer1
            // 
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
            this.splitContainer1.Location = new System.Drawing.Point(0, 25);
            this.splitContainer1.Name = "splitContainer1";
            // 
            // splitContainer1.Panel1
            // 
            this.splitContainer1.Panel1.Controls.Add(this.treeView1);
            // 
            // splitContainer1.Panel2
            // 
            this.splitContainer1.Panel2.Controls.Add(this.splitContainer2);
            this.splitContainer1.Size = new System.Drawing.Size(884, 425);
            this.splitContainer1.SplitterDistance = 173;
            this.splitContainer1.TabIndex = 0;
            // 
            // treeView1
            // 
            this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.treeView1.Location = new System.Drawing.Point(0, 0);
            this.treeView1.Name = "treeView1";
            this.treeView1.Size = new System.Drawing.Size(173, 425);
            this.treeView1.TabIndex = 0;
            // 
            // splitContainer2
            // 
            this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel2;
            this.splitContainer2.Location = new System.Drawing.Point(0, 0);
            this.splitContainer2.Name = "splitContainer2";
            // 
            // splitContainer2.Panel1
            // 
            this.splitContainer2.Panel1.Controls.Add(this.tabControl1);
            // 
            // splitContainer2.Panel2
            // 
            this.splitContainer2.Panel2.Controls.Add(this.splitContainer3);
            this.splitContainer2.Size = new System.Drawing.Size(707, 425);
            this.splitContainer2.SplitterDistance = 462;
            this.splitContainer2.TabIndex = 0;
            // 
            // tabControl1
            // 
            this.tabControl1.Controls.Add(this.tabPage1);
            this.tabControl1.Controls.Add(this.tabPage2);
            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tabControl1.Location = new System.Drawing.Point(0, 0);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(462, 425);
            this.tabControl1.TabIndex = 0;
            // 
            // tabPage1
            // 
            this.tabPage1.Controls.Add(this.panel1);
            this.tabPage1.Location = new System.Drawing.Point(4, 22);
            this.tabPage1.Name = "tabPage1";
            this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage1.Size = new System.Drawing.Size(454, 399);
            this.tabPage1.TabIndex = 0;
            this.tabPage1.Text = "デザイン";
            this.tabPage1.UseVisualStyleBackColor = true;
            // 
            // panel1
            // 
            this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panel1.Location = new System.Drawing.Point(3, 3);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(448, 393);
            this.panel1.TabIndex = 0;
            // 
            // tabPage2
            // 
            this.tabPage2.Controls.Add(this.textBox1);
            this.tabPage2.Location = new System.Drawing.Point(4, 22);
            this.tabPage2.Name = "tabPage2";
            this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage2.Size = new System.Drawing.Size(454, 399);
            this.tabPage2.TabIndex = 1;
            this.tabPage2.Text = "ソースコード";
            this.tabPage2.UseVisualStyleBackColor = true;
            // 
            // textBox1
            // 
            this.textBox1.AcceptsReturn = true;
            this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.textBox1.Font = new System.Drawing.Font("MS UI Gothic", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.textBox1.Location = new System.Drawing.Point(3, 3);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(448, 393);
            this.textBox1.TabIndex = 0;
            this.textBox1.WordWrap = false;
            // 
            // splitContainer3
            // 
            this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
            this.splitContainer3.Location = new System.Drawing.Point(0, 0);
            this.splitContainer3.Name = "splitContainer3";
            this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // splitContainer3.Panel1
            // 
            this.splitContainer3.Panel1.Controls.Add(this.label2);
            this.splitContainer3.Panel1.Controls.Add(this.label1);
            // 
            // splitContainer3.Panel2
            // 
            this.splitContainer3.Panel2.Controls.Add(this.propertyGrid1);
            this.splitContainer3.Size = new System.Drawing.Size(241, 425);
            this.splitContainer3.SplitterDistance = 80;
            this.splitContainer3.TabIndex = 0;
            // 
            // propertyGrid1
            // 
            this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.propertyGrid1.Location = new System.Drawing.Point(0, 0);
            this.propertyGrid1.Name = "propertyGrid1";
            this.propertyGrid1.Size = new System.Drawing.Size(241, 341);
            this.propertyGrid1.TabIndex = 0;
            // 
            // toolStrip1
            // 
            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.toolStripButton1});
            this.toolStrip1.Location = new System.Drawing.Point(0, 0);
            this.toolStrip1.Name = "toolStrip1";
            this.toolStrip1.Size = new System.Drawing.Size(884, 25);
            this.toolStrip1.TabIndex = 2;
            this.toolStrip1.Text = "toolStrip1";
            // 
            // toolStripButton1
            // 
            this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
            this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
            this.toolStripButton1.Name = "toolStripButton1";
            this.toolStripButton1.Size = new System.Drawing.Size(114, 22);
            this.toolStripButton1.Text = "toolStripButton1";
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("MS UI Gothic", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
            this.label1.Location = new System.Drawing.Point(3, 13);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(14, 16);
            this.label1.TabIndex = 0;
            this.label1.Text = " ";
            // 
            // label2
            // 
            this.label2.Location = new System.Drawing.Point(3, 40);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(226, 40);
            this.label2.TabIndex = 1;
            this.label2.Text = " ";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(884, 450);
            this.Controls.Add(this.splitContainer1);
            this.Controls.Add(this.toolStrip1);
            this.Name = "Form1";
            this.Text = "デザインツール";
            this.splitContainer1.Panel1.ResumeLayout(false);
            this.splitContainer1.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
            this.splitContainer1.ResumeLayout(false);
            this.splitContainer2.Panel1.ResumeLayout(false);
            this.splitContainer2.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
            this.splitContainer2.ResumeLayout(false);
            this.tabControl1.ResumeLayout(false);
            this.tabPage1.ResumeLayout(false);
            this.tabPage2.ResumeLayout(false);
            this.tabPage2.PerformLayout();
            this.splitContainer3.Panel1.ResumeLayout(false);
            this.splitContainer3.Panel1.PerformLayout();
            this.splitContainer3.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
            this.splitContainer3.ResumeLayout(false);
            this.toolStrip1.ResumeLayout(false);
            this.toolStrip1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.SplitContainer splitContainer1;
        private System.Windows.Forms.SplitContainer splitContainer2;
        private System.Windows.Forms.TreeView treeView1;
        private System.Windows.Forms.TabControl tabControl1;
        private System.Windows.Forms.TabPage tabPage1;
        private System.Windows.Forms.Panel panel1;
        private System.Windows.Forms.TabPage tabPage2;
        private System.Windows.Forms.ToolStrip toolStrip1;
        private System.Windows.Forms.SplitContainer splitContainer3;
        private System.Windows.Forms.PropertyGrid propertyGrid1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.ToolStripButton toolStripButton1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Label label1;
    }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing.Design;
using System.Collections;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        private DesignSurface _DesignSurface = null;

        public Form1()
        {
            InitializeComponent();

            // 画面デザインに関する窓口的なクラスを宣言
            _DesignSurface = new DesignSurface(typeof(Form));

            // 各サービスを管理するコンテナです。
            var serviceContainer = _DesignSurface.GetService(typeof(IServiceContainer)) as IServiceContainer;

            // ツールボックスとコントロール配置サービスの役割
            serviceContainer.AddService(typeof(IToolboxService), new WinformToolboxService(treeView1, panel1));

            // 配置したコントロールのデフォルト名を付ける役割
            serviceContainer.AddService(typeof(INameCreationService), new NameCreationService());

            // 配置コントロールの選択系イベント管理の役割
            // 選択したコントロール情報を、プロパティウィンドウに表示更新する
            var selectionService = serviceContainer.GetService(typeof(ISelectionService)) as ISelectionService;
            selectionService.SelectionChanged += this.SelectionService_SelectionChanged;

            // 配置コントロールの編集系イベント管理の役割
            // 任意の動作に応じて、都度ソースコード生成をおこないます。
            var componentChangeService = serviceContainer.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
            componentChangeService.ComponentAdded += this.ComponentChangeService_ComponentAdded;
            componentChangeService.ComponentChanged += this.ComponentChangeService_ComponentChanged;
            componentChangeService.ComponentRemoved += this.ComponentChangeService_ComponentRemoved;
            componentChangeService.ComponentRename += this.ComponentChangeService_ComponentRename;

            // デザイン画面にアクセスするためのサービス機能
            var host = serviceContainer.GetService(typeof(IDesignerHost)) as IDesignerHost;
            var frm = host.RootComponent as Form;
            frm.Text = "WindowsFormsApp1";
            frm.Name = "form1";

            var view = _DesignSurface.View as Control;
            view.Dock = DockStyle.Fill;
            this.panel1.Controls.Add(view);
        }



        // 選択したコントロールは、PropertyGrid にセットして詳細データを表示する
        private void SelectionService_SelectionChanged(object sender, EventArgs e)
        {
            var selectionService = _DesignSurface.GetService(typeof(ISelectionService)) as ISelectionService;

            if (selectionService.SelectionCount == 0)
            {
                propertyGrid1.SelectedObject = null;
                label1.Text = "";
                label2.Text = "";
            }
            else
            {
                var items = new object[selectionService.SelectionCount];
                selectionService.GetSelectedComponents().CopyTo(items, 0);
                propertyGrid1.SelectedObjects = items;

                if (propertyGrid1.SelectedObjects.Length == 1)
                {
                    var ctrl = propertyGrid1.SelectedObjects[0] as Control;
                    label1.Text = ctrl.Name;
                    label2.Text = ctrl.GetType().FullName;
                }
                else
                {
                    label1.Text = "";
                    label2.Text = "";
                }
            }

        }



        // 配置コントロールが追加された時
        private void ComponentChangeService_ComponentAdded(object sender, ComponentEventArgs e)
        {
            MakeSource();
        }

        // 配置コントロールが変更された時
        private void ComponentChangeService_ComponentChanged(object sender, ComponentChangedEventArgs e)
        {
            SelectionService_SelectionChanged(sender, EventArgs.Empty);
            MakeSource();
        }

        // 配置コントロールが削除された時
        private void ComponentChangeService_ComponentRemoved(object sender, ComponentEventArgs e)
        {
            MakeSource();
        }

        // 配置コントロールがリネームされた時
        private void ComponentChangeService_ComponentRename(object sender, ComponentRenameEventArgs e)
        {
            MakeSource();
        }

        // Form を取得して、再帰的にコントロールを把握、ソースコード生成していきます。
        private void MakeSource()
        {
            // デザイン画面を取得
            var host = _DesignSurface.GetService(typeof(IDesignerHost)) as IDesignerHost;
            var frm = host.RootComponent as Form;
            var sb = new StringBuilder();

            WriteChildControls(frm, sb);
            this.textBox1.Text = sb.ToString();
        }

        private void WriteChildControls(Control current, StringBuilder sb)
        {
            var quote = '"';
            var parent = current.Parent?.Name;

            if (current is Form)
            {
                var ctrl = current as Form;
                sb.AppendLine($"${ctrl.Name} = New-Object {ctrl.GetType().FullName}");
                sb.AppendLine($"${ctrl.Name}.Name = {quote}{ctrl.Name}{quote}");
                sb.AppendLine($"${ctrl.Name}.Text = {quote}{ctrl.Text}{quote}");
                sb.AppendLine($"${ctrl.Name}.Size = {quote}{ctrl.Size.Width}, {ctrl.Size.Height}{quote}");
                sb.AppendLine($"${ctrl.Name}.Location = {quote}{ctrl.Location.X}, {ctrl.Location.Y}{quote}");
                sb.AppendLine($"");
            }

            if (current is Button)
            {
                var ctrl = current as Button;
                sb.AppendLine($"${ctrl.Name} = New-Object {ctrl.GetType().FullName}");
                sb.AppendLine($"${ctrl.Name}.Name = {quote}{ctrl.Name}{quote}");
                sb.AppendLine($"${ctrl.Name}.Text = {quote}{ctrl.Text}{quote}");
                sb.AppendLine($"${ctrl.Name}.Size = {quote}{ctrl.Size.Width}, {ctrl.Size.Height}{quote}");
                sb.AppendLine($"${ctrl.Name}.Location = {quote}{ctrl.Location.X}, {ctrl.Location.Y}{quote}");
                sb.AppendLine($"${parent}.Controls.Add(${ctrl.Name})");
                sb.AppendLine($"");
            }

            if (current is CheckBox)
            {
                var ctrl = current as CheckBox;
                sb.AppendLine($"${ctrl.Name} = New-Object {ctrl.GetType().FullName}");
                sb.AppendLine($"${ctrl.Name}.Name = {quote}{ctrl.Name}{quote}");
                sb.AppendLine($"${ctrl.Name}.Text = {quote}{ctrl.Text}{quote}");
                sb.AppendLine($"${ctrl.Name}.Size = {quote}{ctrl.Size.Width}, {ctrl.Size.Height}{quote}");
                sb.AppendLine($"${ctrl.Name}.Location = {quote}{ctrl.Location.X}, {ctrl.Location.Y}{quote}");
                sb.AppendLine($"${parent}.Controls.Add(${ctrl.Name})");
                sb.AppendLine($"");
            }

            if (current is TextBox)
            {
                var ctrl = current as TextBox;
                sb.AppendLine($"${ctrl.Name} = New-Object {ctrl.GetType().FullName}");
                sb.AppendLine($"${ctrl.Name}.Name = {quote}{ctrl.Name}{quote}");
                sb.AppendLine($"${ctrl.Name}.Text = {quote}{ctrl.Text}{quote}");
                sb.AppendLine($"${ctrl.Name}.Size = {quote}{ctrl.Size.Width}, {ctrl.Size.Height}{quote}");
                sb.AppendLine($"${ctrl.Name}.Location = {quote}{ctrl.Location.X}, {ctrl.Location.Y}{quote}");
                sb.AppendLine($"${parent}.Controls.Add(${ctrl.Name})");
                sb.AppendLine($"");
            }

            if (current is RadioButton)
            {
                var ctrl = current as RadioButton;
                sb.AppendLine($"${ctrl.Name} = New-Object {ctrl.GetType().FullName}");
                sb.AppendLine($"${ctrl.Name}.Name = {quote}{ctrl.Name}{quote}");
                sb.AppendLine($"${ctrl.Name}.Text = {quote}{ctrl.Text}{quote}");
                sb.AppendLine($"${ctrl.Name}.Size = {quote}{ctrl.Size.Width}, {ctrl.Size.Height}{quote}");
                sb.AppendLine($"${ctrl.Name}.Location = {quote}{ctrl.Location.X}, {ctrl.Location.Y}{quote}");
                sb.AppendLine($"${parent}.Controls.Add(${ctrl.Name})");
                sb.AppendLine($"");
            }

            // ...

            if (current.HasChildren)
            {
                foreach (Control childControl in current.Controls)
                {
                    WriteChildControls(childControl, sb);
                }
            }
        }
    }
}

WinformToolboxService.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel.Design;
using System.Drawing.Design;
using System.Collections;

namespace WindowsFormsApp2
{
    public class WinformToolboxService : IToolboxService
    {
        // 配置コントロールのリストと、選択中の配置コントロール
        private ToolboxItemCollection _ToolboxItemCollection;
        private ToolboxItem _SelectedToolboxItem;

        // 配置コントロールの種類となるカテゴリ名リストと、選択中のコントロールの種類となるカテゴリ名
        public CategoryNameCollection CategoryNames { get; }
        public string SelectedCategory { get; set; }

        // 配置コントロールの選択機能を提供するコントロール
        public TreeView ToolBox { get; set; }

        // 配置コントロール選択時、マウスカーソルを変更するための用途
        public Control DesignControl { get; set; }

        // コンストラクタ
        // カテゴリ名や配置コントロールの準備
        public WinformToolboxService(TreeView toolBox, Control designControl)
        {
            CategoryNames = new CategoryNameCollection(new string[] { "全てのカテゴリー" });
            SelectedCategory = CategoryNames[0];
            this.DesignControl = designControl;

            if (!(this.ToolBox is null))
            {
                this.ToolBox.AfterSelect -= this.ToolBox_AfterSelect;
            }

            this.ToolBox = toolBox;
            InitializeToolBox();
            this.ToolBox.AfterSelect += this.ToolBox_AfterSelect;
        }

        private void InitializeToolBox()
        {
            var items = new List<ToolboxItem>
            {
                new ToolboxItem { DisplayName = "ポインター"},
                new ToolboxItem(typeof(Button)),
                new ToolboxItem(typeof(CheckBox)),
                new ToolboxItem(typeof(TextBox)),
                new ToolboxItem(typeof(RadioButton))
            };

            _ToolboxItemCollection = new ToolboxItemCollection(items.ToArray());

            // TreeView には、カテゴリ名(ここでは1つだけ)をトップノードとして登録して、
            // その下に、子ノードとして配置コントロールを登録する
            this.ToolBox.Nodes.Clear();
            var rootNode = this.ToolBox.Nodes.Add(SelectedCategory);

            // ※rootNode に新しい子ノードを追加しつつ、そのノードを受け取れるので、タグに ToolboxItem インスタンスを入れておく
            foreach (ToolboxItem item in _ToolboxItemCollection)
            {
                var childNode = rootNode.Nodes.Add(item.DisplayName);
                childNode.Tag = item;
            }

            this.ToolBox.ExpandAll();
        }

        // TreeView のノードが選択されたら、IToolboxService サービス側も同期を取る
        private void ToolBox_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (0 < e.Node.Level && e.Node.Tag is ToolboxItem)
            {
                var item = e.Node.Tag as ToolboxItem;
                SetSelectedToolboxItem(item);
            }
        }




        // 指定したデータ形式の新しいツールボックス アイテムの作成者を追加します。
        public void AddCreator(ToolboxItemCreatorCallback creator, string format)
        {
            
        }

        // 指定したデータ形式とデザイナー ホストの新しいツールボックス アイテムの作成者を追加します。
        public void AddCreator(ToolboxItemCreatorCallback creator, string format, IDesignerHost host)
        {
            
        }

        // 指定したプロジェクトにリンクされたツールボックス項目をツールボックスに追加します。
        public void AddLinkedToolboxItem(ToolboxItem toolboxItem, IDesignerHost host)
        {
            
        }

        // 指定したプロジェクトにリンクされたツールボックス項目を指定したカテゴリのツールボックスに追加します。
        public void AddLinkedToolboxItem(ToolboxItem toolboxItem, string category, IDesignerHost host)
        {
            
        }

        // 指定したツールボックス項目をツールボックスに追加します。
        public void AddToolboxItem(ToolboxItem toolboxItem)
        {
            
        }

        // 指定したツールボックス項目を指定したカテゴリのツールボックスに追加します。
        public void AddToolboxItem(ToolboxItem toolboxItem, string category)
        {
            
        }

        // 指定したオブジェクトをシリアル化された形式で、ツールボックス項目を表すからツールボックス項目を取得します。
        public ToolboxItem DeserializeToolboxItem(object serializedObject)
        {
            return DeserializeToolboxItem(serializedObject, null);
        }

        // 指定したデザイナー ホストを使用してシリアル化の形式でツールボックス項目を表す指定したオブジェクトから、ツールボックス項目を取得します。
        public ToolboxItem DeserializeToolboxItem(object serializedObject, IDesignerHost host)
        {
            return null;
        }

        // 現在選択されているツールボックス項目を取得します。
        public ToolboxItem GetSelectedToolboxItem()
        {
            return GetSelectedToolboxItem(null);
        }

        // すべてのデザイナーに表示されている場合、または指定したデザイナーがサポートされている場合は、現在選択されているツールボックス項目を取得します。
        public ToolboxItem GetSelectedToolboxItem(IDesignerHost host)
        {
            return _SelectedToolboxItem;
        }

        // ツールボックスのツールボックス項目のコレクション全体を取得します。
        public ToolboxItemCollection GetToolboxItems()
        {
            return GetToolboxItems("");
        }

        // ツールボックスから、指定したデザイナー ホストに関連付けられているツールボックス項目のコレクションを取得します。
        public ToolboxItemCollection GetToolboxItems(IDesignerHost host)
        {
            return GetToolboxItems("");
        }

        // 指定したカテゴリに一致するツールボックスのツールボックス項目のコレクションを取得します。
        public ToolboxItemCollection GetToolboxItems(string category)
        {
            return GetToolboxItems(category, null);
        }

        // 指定したデザイナー ホストと、ツールボックスからカテゴリに関連付けられているツールボックス項目のコレクションを取得します。
        public ToolboxItemCollection GetToolboxItems(string category, IDesignerHost host)
        {
            return _ToolboxItemCollection;
        }

        // 指定したデザイナー ホストによってシリアル化されたツールボックス項目を表す、指定したオブジェクトを使用できるかどうかを示す値を取得します。
        public bool IsSupported(object serializedObject, IDesignerHost host)
        {
            return true;
        }

        // 指定した属性がシリアル化されたツールボックス項目を表す、指定したオブジェクトに一致するかどうかを示す値を取得します。
        public bool IsSupported(object serializedObject, ICollection filterAttributes)
        {
            return true;
        }

        // 指定したオブジェクトがシリアル化されたツールボックス項目であるかどうかを示す値を取得します。
        public bool IsToolboxItem(object serializedObject)
        {
            return IsToolboxItem(serializedObject, null);
        }

        // 指定したオブジェクトが指定したデザイナー ホストを使用して、シリアル化されたツールボックスのアイテムであるかどうかを示す値を取得します。
        public bool IsToolboxItem(object serializedObject, IDesignerHost host)
        {
            return false;
        }

        // ツールボックス項目の状態を更新します。
        public void Refresh()
        {
            
        }

        // 指定したデータ形式の以前に追加したツールボックス項目の作成者を削除します。
        public void RemoveCreator(string format)
        {
            
        }

        // 指定したデータ形式と指定したデザイナー ホストに関連付けられている以前に追加したツールボックス クリエーターを削除します。
        public void RemoveCreator(string format, IDesignerHost host)
        {
            
        }

        // ツールボックスから、指定したツールボックス項目を削除します。
        public void RemoveToolboxItem(ToolboxItem toolboxItem)
        {
            
        }

        // ツールボックスから、指定したツールボックス項目を削除します。
        public void RemoveToolboxItem(ToolboxItem toolboxItem, string category)
        {
            
        }

        // 選択したツールが使用されたことをツールボックスのサービスに通知します。
        public void SelectedToolboxItemUsed()
        {
            _SelectedToolboxItem = null;
        }

        // 指定したツールボックス項目を表すシリアル化可能なオブジェクトを取得します。
        public object SerializeToolboxItem(ToolboxItem toolboxItem)
        {
            return null;
        }

        // 現在選択されているツールを表すようにカーソルを現在のアプリケーションのカーソルを設定します。
        public bool SetCursor()
        {
            if (_SelectedToolboxItem is null || _SelectedToolboxItem.DisplayName == "ポインター")
            {
                DesignControl.Cursor = Cursors.Default;
                return false;
            }

            // 今回は、それぞれのコントロール用のカーソルアイコンを持っていないため、一律同じカーソルアイコンを設定しておく
            DesignControl.Cursor = Cursors.Cross;
            return true;
        }

        // 指定したツールボックス項目を選択します。
        public void SetSelectedToolboxItem(ToolboxItem newSelectedItem)
        {
            if (!(_SelectedToolboxItem is null) && _SelectedToolboxItem.Equals(newSelectedItem))
            {
                return;
            }

            _SelectedToolboxItem = newSelectedItem;

            // 通常は、TreeView クリックからの WinformToolboxService.SetSelectedToolboxItem() の順番なので問題ないはずだが、
            // WinformToolboxService 始まりの場合(あるのか?)、TreeView 側も同期を取る
            if (ToolBox.SelectedNode is null)
            {
                var results = ToolBox.Nodes.Find(_SelectedToolboxItem.DisplayName, true);
                ToolBox.SelectedNode = results[0];
                return;
            }

            var selectedItem = ToolBox.SelectedNode.Tag as ToolboxItem;
            if (selectedItem.DisplayName != _SelectedToolboxItem.DisplayName)
            {
                var results = ToolBox.Nodes.Find(_SelectedToolboxItem.DisplayName, true);
                ToolBox.SelectedNode = results[0];
            }
        }
    }
}

NameCreationService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;

namespace WindowsFormsApp2
{
    public class NameCreationService : INameCreationService
    {
        // 指定されたコンテナーのすべてのコンポーネントに一意の新しい名前を作成します。
        // 型名をベースに、連番(1始まり)
        public string CreateName(IContainer container, Type dataType)
        {
            var i = 0;
            var name = dataType.Name;
            name = name.Substring(0, 1).ToLower() + name.Substring(1);

            while (true)
            {
                i++;
                if (container.Components[$"{name}{i}"] is null)
                {
                    break;
                }
            }

            return $"{name}{i}";
        }

        // 指定した名前が有効かどうかを示す値を取得します。
        public void ValidateName(string name)
        {
            if (!IsValidName(name))
                throw new ArgumentException($"{name} is invalid.");
        }

        // 指定した名前が有効かどうかを示す値を取得します。
        // 名前があること、1文字目はアンダーバーじゃないこと、などなど
        public bool IsValidName(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                return false;

            if (char.IsDigit(name, 0) || name[0] == '_')
                return false;

            return true;
        }
    }
}

タイトルの回収

それで、このツールを使ってマウスでポトペタすると裏でソースコードが自動生成されるので、これを PowerShellソースコードにコピペすれば、Size と Location の調整をわざわざしなくてもいいよね~っていう話でした。

f:id:sutefu7:20190831153736p:plain

そして、処理内容をほとんど忘れてしまっていました。確か各機能細かく分かれていて、インターフェース越しにみんなで協力してデザインプログラムとして動かすような流れだったような・・・。今回、よく何となくの記憶だけで作れたと思いました(;^_^A。