【C#入門】Controlとしてまとめて扱う|WinFormsのコントロールとforeach

広告

前回は、Button を継承した MyButton を作りました。

MyButtonButton の一種であり、さらに Control の一種でもあります。

今回はこの関係を利用して、種類の異なるコントロールを Control 型としてまとめて扱う方法を学びます。

前の記事:MyButtonで継承を体験する


① Button・Label・TextBoxの共通点

フォームに置く次のコントロールは、見た目も役割も異なります。

  • Button:クリックできる
  • Label:文字を表示する
  • TextBox:文字を入力できる

しかし、どれも Control を継承しています。

Control
  ├─ Button
  ├─ Label
  └─ TextBox

そのため、TextBackColorForeColorLocation など、Control に用意された共通機能を利用できます。


② 1つずつ設定した場合

フォームに button1label1textBox1 を配置したとします。

文字色を1つずつ設定すると、次のように書けます。

button1.ForeColor = Color.Navy;
label1.ForeColor = Color.Navy;
textBox1.ForeColor = Color.Navy;

3個なら問題ありませんが、コントロールが増えると同じ処理を何度も書くことになります。


③ Controlsをforeachで繰り返す

フォームが直接持っている子コントロールは、Controls コレクションに入っています。

foreach (Control control in Controls)
{
    control.ForeColor = Color.Navy;
}

Controls の中には ButtonLabelTextBox などが混ざっています。それでも、1個ずつ取り出す変数を Control 型にできます。

すべてが Control の一種だからです。

Button・Label・TextBoxが共通の基底クラスControlとしてforeachでまとめて扱えることを示す図
種類が違っても、共通の基底クラスであるControlとして取り出せます。

④ 共通の見た目をまとめて設定する

ForeColorBackColor をまとめて設定してみましょう。

using System.Drawing;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        ApplyCommonStyle();
    }

    private void ApplyCommonStyle()
    {
        foreach (Control control in Controls)
        {
            control.ForeColor = Color.Navy;
            control.BackColor = Color.AliceBlue;
        }
    }
}

この処理では、各コントロール固有の機能は使っていません。Control に共通して用意されている ForeColorBackColor だけを使っています。

「共通の型で受け取り、共通部分を操作する」と考えるのがポイントです。


⑤ Controlにはない固有機能を使いたい場合

Button だけの処理をしたい場合は、型を確認します。

foreach (Control control in Controls)
{
    if (control is Button button)
    {
        button.Text = "ボタンです";
    }
}

control is Button button は、次の2つを同時に行います。

  1. control の実体が Button か確認する
  2. Button として使える変数 button を用意する

Button ではない LabelTextBox は、if の中に入りません。


⑥ foreachで見えるのは直下の子だけ

ここは誤解しやすいポイントです。

foreach (Control control in Controls)

この Controls は、フォーム直下の子だけを表します。

たとえば、Panel の中に Button がある場合、フォームの Controls から直接見えるのは Panel です。中のボタンを見るには、panel1.Controls を調べます。

foreach (Control control in panel1.Controls)
{
    control.ForeColor = Color.Navy;
}

「画面内のすべて」を再帰的に探す方法もありますが、まずはそれぞれの親が、自分の直下の子をControlsに持つと理解すれば十分です。


⑦ is-aとhas-aは別の関係

この例には、2種類の関係が登場します。

関係 意味
is-a ButtonControl の一種 継承の関係
has-a フォームは子コントロールを持つ Controls による所有・包含の関係

ButtonControl は「種類」の関係です。一方、フォームとフォーム上のボタンは「持っている・含んでいる」という関係です。

似たように見えても、別の軸として整理しましょう。


⑧ MyButtonもControlとして扱える

前回作った MyButton も、同じ foreach に入ります。

MyButton → Button → Control

MyButtonButton を継承し、その ButtonControl を継承しているからです。

このように、継承関係の途中に複数のクラスがあっても、共通の基底クラスとして扱えます。


⑨ 練習問題

① フォーム直下にあるすべてのコントロールの BackColorColor.AliceBlue にしてみましょう。

Label だけを見つけて、Text を「ラベルです」に変更してみましょう。

Panel の中にボタンを置き、フォームの Controlspanel1.Controls で見えるものが違うことを確認しましょう。


⑩ まとめ

  • ButtonLabelTextBox はすべて Control の一種
  • foreach (Control control in Controls) で種類の違うコントロールをまとめて扱える
  • Control 型のまま使えるのは、Control に共通する機能
  • 固有機能を使うときは is などで実体の型を確認する
  • フォームの Controls で列挙できるのは直下の子
  • is-a(継承)とhas-a(所有・包含)は別の関係

次回は、同じ MessageButton 型として扱っていても、実体によってクリック時のメッセージが変わる仕組みを学びます。

次の記事:同じ型でも動きが変わる|ポリモーフィズムの入り口

シリーズ目次:WinFormsで学ぶ「継承」入門

訪問数 7 回, 今日の訪問数 7回

広告