【C#】ソースで動的に追加したコントロール - 2.動的に追加したテキストボックスを識別し、参照する

# コントロールを動的にフォームへ追加して使いたい

## C#の記事
- [C#でMySQLを使う - 1.開発環境インストール](https://qiita.com/kazumi75kitty/items/9953bb84edd7717bc90a)
- [C#でMySQLを使う - 2.SELECT・画面表示](https://qiita.com/kazumi75kitty/items/dd391cab06762555dc5d)
- [C#でMySQLを使う - 3.追加更新と削除](https://qiita.com/kazumi75kitty/items/65f8ae261bbe61964627)
- [C#でのデータベースのデータのNULLかどうかの判定](https://qiita.com/kazumi75kitty/items/64002467350a1ea08ab0)
- [C#のdelegate(デリゲート)と=>(ラムダ式)はC言語の関数へのポインタに似た扱い](https://qiita.com/kazumi75kitty/items/fdf4e1d2ea8e9c9326ba#comment-dbed4d53e43dbfdf5baa)
- [動的にコントロールを追加 - 1.ボタンクリック](https://qiita.com/kazumi75kitty/items/2e3f4c10a93d26032382)
- 動的にコントロールを追加 - 2.テキストボックスの参照【この記事】
- [動的にコントロールを追加 - 3.DB(MySQL)から動的に作成](https://qiita.com/kazumi75kitty/items/3bbb8afdfbc21fb6b3f1)

[前回はソースで動的にボタンコントロールをフォームへ追加して、クリックイベントを用いてみた](https://qiita.com/kazumi75kitty/items/2e3f4c10a93d26032382)んですが、今回はそれを応用して、さらにテキストボックスも動的に追加して、それら**動的作成したコントロールを参照します**

# テキストボックスを動的に追加して、それを参照してみる

## 新たにテキストボックスを追記してみる
ボタンの動的な追加ができたので、今度はテキストボックスを動的に追加して、それが他から参照できるかどうかをやってみました( ˙꒳​˙ᐢ ) そのためには、**テキストボックスを動的にソースから参照したときは、それぞれのテキストボックスをNameに識別名を割り当てないといけない**ことです。
というのも、テキストボックスなどが通常のコーディングでは静的に配置されるので、コントロールのプロパティの「Name」は変数名となるので、その固定された変数(オブジェクト)の中のTextなどのプロパティを参照する形となります(例えば静的配置されたコントロールのNameが「myTextBox」であれば、「TextBox myTextBox = new TextBox()」みたいな内部定義が生じているので、myTextBox.TextやmyTextBox.Valueのような参照ができる)が、動的に後から追加されたコントロールについてはそうにはいかないからです。


本来は設定ファイルやデータベースで受け持つけど、いきなり話が複雑になるので、今回は手法検証ということでプログラムにて。。。

![フォームデザイン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/9bd0d488-2947-98f8-34a2-1a61142c98c6.png)
今度は新たに「buttonCheckEntry」をボタン追加しました！このボタンをクリックすると、動的に追加したテキストフォームの内容を拾ってくれるようにソースコードを作ります(動的にテキストフォームを作成する前はグレーアウトします)。

```c#:Form1.cs
using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MyTrainingCsFrm1
{
    public partial class Form1 : Form
    {
        // フォーム呼び出しで作成するボタン(Windowsフォームのボタン - Buttonを継承する)
        private Kazumi75Button[] manyButtons;

        // フォーム呼び出しで作成するテキストボックスとその識別名
        private TextBox[] manyTextBoxes;
        private string[] txtbxNm;

        // 配列の要素数(ここでは8個)
        private const int ElementNum = 8;

        public Form1()
        {
            InitializeComponent();
            this.manyButtons = null;
            this.manyTextBoxes = null;
            this.txtbxNm = null;
        }

        // フォームを呼び出すボタン
        private void formCallButton_Click(object sender, EventArgs e)
        {
            if (this.manyButtons != null || this.manyTextBoxes != null)
            {
                MessageBox.Show("すでに表示されています");
                return;
            }

            // ボタンの各メッセージはここであらかじめ設定する
            string[] msgs = new string[ElementNum];
            msgs[0] = "浦賀";
            msgs[1] = "鎌倉";
            msgs[2] = "三崎";
            msgs[3] = "観音崎";
            msgs[4] = "横須賀中央";
            msgs[5] = "城ヶ島";
            msgs[6] = "七里ガ浜";
            msgs[7] = "馬堀";

            // テキストボックスの各識別名はここであらかじめ設定する
            this.txtbxNm = new string[ElementNum];
            this.txtbxNm[0] = "txbUraga";
            this.txtbxNm[1] = "txbKamakura";
            this.txtbxNm[2] = "txbMisaki";
            this.txtbxNm[3] = "txbKannonzaki";
            this.txtbxNm[4] = "txbYokosuka";
            this.txtbxNm[5] = "txbJogashima";
            this.txtbxNm[6] = "txbShichirigahama";
            this.txtbxNm[7] = "txbMabori";

            // ボタン(Windowsフォームのボタンを継承する)とテキストボックスの作成
            this.manyButtons = new Kazumi75Button[ElementNum];
            this.manyTextBoxes = new TextBox[ElementNum];

            // ボタンとテキストボックスの各要素に対するプロパティ設定とフォーム配置
            for (int i = 0; i < this.manyButtons.Length; i++)
            {
                // インスタンス作成
                this.manyButtons[i] = new Kazumi75Button();
                this.manyTextBoxes[i] = new TextBox();
                
                // ボタンの名前とテキストのプロパティを設定
                this.manyButtons[i].Name = "OriginalButton" + i;
                this.manyButtons[i].Text = "ボタン" + i;
                // テキストボックスの名前とテキストのプロパティを設定
                this.manyTextBoxes[i].Name = txtbxNm[i];
                this.manyTextBoxes[i].Text = "(" + (i + 1) + "つ目に何か入れてね)";

                // ボタンクリック時に参照するリストボックスを指定
                this.manyButtons[i].targetLbox = listBox1;

                // ボタンクリック時のメッセージを設定
                this.manyButtons[i].buttonMsg = msgs[i];

                // ボタンのサイズと配置
                this.manyButtons[i].Size = new Size(100, 20);
                this.manyButtons[i].Location = new Point(10, 10 + i * 22);
                // テキストボックスのサイズと配置
                this.manyTextBoxes[i].Size = new Size(150, 20);
                this.manyTextBoxes[i].Location = new Point(120, 10 + i * 22);

                // フォームへの追加
                this.Controls.Add(this.manyButtons[i]);
                this.Controls.Add(this.manyTextBoxes[i]);

                // クリック時のボタンごとのイベント動作を作成する
                this.manyButtons[i].eventMaking();
            }

            // フォーム呼び出し後は、テキストボックスの内容確認ボタンを有効化する
            buttonCheckEntry.Enabled = true;
        }

        // 動的に呼び出したテキストボックスの内容を確認するボタン
        private void buttonCheckEntry_Click(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            
            // テキストボックスを識別名で探し、その内容を取得する
            for (int i = 0; i < ElementNum; i++)
            {
                sb.Append("[" + i + "]");
                
                // フォーム上の識別名に該当するコントロールを探す
                Control[] txtboxctrls = this.Controls.Find(this.txtbxNm[i], false);

                for (int j = 0; j < txtboxctrls.Length; j++)
                {
                    // 取得したテキストボックスの内容を文字列取得
                    TextBox tbox = (TextBox)txtboxctrls[j];

                    sb.Append(tbox.Text);
                }
                
                // 最終要素以外の場合は「/」で区切る
                if (i < ElementNum - 1)
                {
                    sb.Append(" / ");
                }
            }

            // メッセージを表示する
            MessageBox.Show("入力したものは…" + sb.ToString() + " です。");
        }
    }
}
```

まずは本来データベースや設定ファイルで取得すべき、識別名をテキストボックスに設定する箇所

```c#
this.txtbxNm = new string[ElementNum];
this.txtbxNm[0] = "txbUraga";
this.txtbxNm[1] = "txbKamakura";
this.txtbxNm[2] = "txbMisaki";
this.txtbxNm[3] = "txbKannonzaki";
this.txtbxNm[4] = "txbYokosuka";
this.txtbxNm[5] = "txbJogashima";
this.txtbxNm[6] = "txbShichirigahama";
this.txtbxNm[7] = "txbMabori";
```
```c#
this.manyTextBoxes[i].Name = txtbxNm[i];
```

このことをしなければ、まずテキストボックスを参照することができません。なぜなら**ボタンbuttonCheckEntryのクリックイベントでは、識別名をFindすることで、該当するテキストボックスを検索する**からです。

```c#
Control[] txtboxctrls = this.Controls.Find(this.txtbxNm[i], false);
```
Findメソッドの引数は、それぞれ**識別名、子要素を探すかのフラグ**。ここではテキストフォームだけを使うので、それ以下の子要素は意識しないのでfalseで。要素が見つかれば、上記のtxtboxctrlsの要素数は0より大きい(1以上)になります。

![実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/b8f67e8b-9395-92ce-63ad-0053ada64124.png)

これも成功しました。
最後は要素数を動的に変えて対応できるかもチェックします

## 要素数を動的に変えてみる

```c#:Form1.csの修正箇所
// 配列の要素数(ここでは11個)
private const int ElementNum = 11;

　…(中略)…

string[] msgs = new string[ElementNum];
msgs[0] = "浦賀";
msgs[1] = "鎌倉";
msgs[2] = "三崎";
msgs[3] = "観音崎";
msgs[4] = "横須賀中央";
msgs[5] = "城ヶ島";
msgs[6] = "七里ガ浜";
msgs[7] = "馬堀";
msgs[8] = "走水";
msgs[9] = "逗子";
msgs[10] = "葉山";

this.txtbxNm = new string[ElementNum];
this.txtbxNm[0] = "txbUraga";
this.txtbxNm[1] = "txbKamakura";
this.txtbxNm[2] = "txbMisaki";
this.txtbxNm[3] = "txbKannonzaki";
this.txtbxNm[4] = "txbYokosuka";
this.txtbxNm[5] = "txbJogashima";
this.txtbxNm[6] = "txbShichirigahama";
this.txtbxNm[7] = "txbMabori";
this.txtbxNm[8] = "txbHashirimizu";
this.txtbxNm[9] = "txbZushi";
this.txtbxNm[10] = "txbHayama";
```

最後は要素数を8→11に増やして、そうするとボタンを11個に増やし、テキストボックスも11個識別しなければならなくなるので、上記のように修正します。

![実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/7111220e-a57a-b229-0b43-7d26a6be079e.png)

これも11個のテキストボックスを正しく識別できました！！(˶ ･ᴗ･ )੭⚐⚑

# 次回
今回はデータベースのマスタだの設定ファイルだのといっぺんに話を混ぜると疲れるので、動的にコントロールを追加する方法に絞って記事にしてみましたが、次はデータベースのマスタデータから動的にコントロールを追加することに挑戦してみます( ˙꒳​˙ᐢ )

# 参照文献

- [【.NET】コントロールを実行時に作成する](https://dobon.net/vb/dotnet/control/addcontrol.html)
- [【.NET】イベントを動的に登録する](http://www.r-nakai.com/archives/19)
- [コントロール名を表す文字列からコントロールを取得する - 文字列名のstring型からのコントロールのアクセス - C#プログラミング](https://www.ipentec.com/document/csharp-get-control-by-name-of-control-string)
- [C#_プログラムから生成した ( 動的 ) コントロールにイベントをつける](http://koshinran.hateblo.jp/entry/2017/11/17/193251)
- [Control.ControlCollection.Find(String, Boolean) メソッド](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.controlcollection.find?view=netcore-3.1) - Microsoft .NET 公式
- [Windowsフォーム上のコントロールを検索するには？［2.0以降、C#、VB］](https://www.atmarkit.co.jp/fdotnet/dotnettips/479ctrlfind/ctrlfind.html) - atmark IT