【VB6・VB】古いコードのリプレース　その１　コントロール配列

**※この記事は[【VB6・VB】古いコードのリプレース](https://qiita.com/so_rei/items/62a0327d321a5e0dd279) のシリーズその１です。**

# その１　コントロール配列(VB6)

まあVB6ではよくあるパターン。むしろ無いほうが珍しい。
VB6には表みたいな機能がろくになかったので、表のようにラベルやテキストをコントロール配列でぺたぺた貼ってぐるぐる回して処理してたんですよねー。


##対処法：手動で配列にセットして処理する。
※以下、"Button1","Button2","Button3"という名前のボタンコントロール３つが画面で宣言されており、
これをコントロール配列として扱いたいとする。

```VB:Form.vb

    Private BtnArray() As Button 'セットする配列

    Public Sub Form1_Load() Handles Me.Load

        InitializeComponent()

        '順番を考慮して配列にセットする必要がある
        BtnArray = {Button1, Button2, Button3}

        'イベントをまとめたい場合、ForEachで回す以外方法がない
        For Each Btn In BtnArray
            AddHandler Btn.Click, AddressOf Me.testButtons_Click
        Next

    End Sub

    Private Sub testButtons_Click(ByVal sender As Object,ByVal e As EventArgs)
        'イベント内で配列の何番目かを取得するには、実際に配列の中身を探しに行かなくてはいけない
        Dim index As Integer = Array.IndexOf(BtnArray, CType(sender, Button))
    End Sub
```

```C#:Form.cs
        private Button[] BtnArray;//セットする配列

        public Form1()
        {
            InitializeComponent();
            
            //順番を考慮して配列にセットする必要がある
            BtnArray = new Button[] { Button1, Button2, Button3 };

            //イベントをまとめたい場合、ForEachで回す以外方法がない
            foreach (Button Btn in BtnArray)
            {
                Btn.Click += new EventHandler(testButtons_Click);
            }
        }

        private void testButtons_Click(object sender, EventArgs e)
        {
            //イベント内で配列の何番目かを取得するには、実際に配列の中身を探しに行かなくてはいけない
            int index = Array.IndexOf(BtnArray, ((Button)sender));
        }
```

大体はこれで解決するんですが、コメント内にあるように、いくつか注意点があります。

[**1.順番を考慮して配列にセットする必要がある**](#1順番を考慮して配列にセットする必要がある)

[**2.イベントをまとめたい場合、ForEachで回す以外方法がない**](#2イベントをまとめたい場合foreachで回す以外方法がない)

[**3.イベント内で配列の何番目かを取得するには、実際に配列の中身を探しに行かなくてはいけない**](#3イベント内で配列の何番目かを取得するには実際に配列の中身を探しに行かなくてはいけない)

以下、注意点について追記します。

---

####1.順番を考慮して配列にセットする必要がある

このうち１が多分一番面倒。場合によっては何百個も入れることになるので、手書きは辛い。
ということで以下に自動で配列に入れる例を紹介します。

**特定の条件を満たすものを自動で配列に入れたい！**
**全て手動で配列に入れるにはたくさんあって面倒！**

そういうときはこんな感じ。

```VB:VB：特定のコントロールだけコントロール配列に入れる
        Dim BtnList As New List(Of Button) 'セットするリスト

        'フォーム内のすべてのコントロールを取得する
        For Each con As Control In Me.Controls
            'それがボタンである
            If IsNothing(TryCast(con, Button)) = False Then
                'それが特定の名前である
                If con.Name Like "B*" Then
                    BtnList.Add(con)
                End If
            End If
        Next

        'タブインデックス順に並び替えて配列に入れる
        BtnArray =  BtnList.OrderBy(Function(p) p.TabIndex).ToArray
```

```c#:c#：特定のコントロールだけコントロール配列に入れる
            var BtnList = new List<Button>();//セットするリスト

            //フォーム内のすべてのコントロールを取得する
            foreach (Control con in this.Controls)
            {
                //それがボタンである
                if (con is Button)
                {
                    //それが特定の名前である
                    if (Regex.IsMatch(con.Name, "B*"))
                    {
                        BtnList.Add((Button)con);
                    }
                }
            }

            //タブインデックス順に並び替える
            BtnArray = BtnList.OrderBy(p => p.TabIndex).ToArray();
```

こうやって条件に合うコントロールだけを配列に入れていけば良い。
例では、"B"から始まるボタンコントロールをタブインデックス順に放り込んでいる。

~~＊なお、Me.Controlsから取得するControlCollectionクラスはIEnumerable(Of T)を実装してないので全部まとめるのは無理。~~

**追記：Me.ControlsはIEnumerable(Of T)は実装していませんが、IEnumerableは実装しています。
IEnumerableにはOfTypeやCastメソッドが用意されており、これはIEnumerable(Of T)を出力します。**
そのため、以下のように全てラムダ式に書き換えることが可能です。
System.Linqを宣言しておくこと

```VB:VB：全てラムダ式に
BtnArray = Me.Controls.OfType(Of Button) _
                      .Where(Function(b) b.Name Like "B*") _
                      .OrderBy(Function(p) p.TabIndex).ToArray()
```
```c#:c#：全てラムダ式に
//c#にはLike文が無いので面倒
BtnArray = this.Controls.OfType<Button>()
                      .Where(p => System.Text.RegularExpressions.Regex.IsMatch(p.Name, "^B.*", RegexOptions.Singleline))
                      .OrderBy(q => q.TabIndex).ToArray();
```

---

####2.イベントをまとめたい場合、ForEachで回す以外方法がない

また、2.は割とどうしようもないです。
全てのイベントを取得し、そのうち使用されているものだけを探す…ような処理自体はあるみたいですが、かなりややこしいです。
結局１つ１つ書くほうが早いです。

---

####3.イベント内で配列の何番目かを取得するには、実際に配列の中身を探しに行かなくてはいけない

3.もこれ以上の短縮は面倒です。
どうしても短縮したい場合は、**Indexを保存した拡張コントロールを作成して、それに全部差し替える**ことになると思います。
(TagやTabIndexなどに配列のインデックスとなる値を入れておくというズルはできますが、デザイナから変えられるため確実性が無い。)