【学習】WinFormsの「ドラッグ&ドロップ」の正体を理解しよう(Controlsコレクション編)

広告

シリーズWinFormsの「ドラッグ&ドロップ」の正体

Label編 では、Controls.Add によってコントロールが フォームに載る ところまで見ました。

ここから一歩進めて、

Controls は「子コントロールの一覧」であり、親子でつながっている

という仕組みを押さえます。Panel の中にボタンを置くような画面でも、同じ考え方がそのまま使えます。


今日学ぶこと

  • Controls の意味 … そのコンテナ(フォームや Panel)の  を並べたコレクションであること
  • Add と親子 … 親.Controls.Add(子) と同時に 子.Parent が親になること
  • 位置の基準 … Location は 親の左上 を原点にした座標であること
  • 列挙のイメージ … foreach で回せるのは 直下の子だけ であり、孫は別の Controls にあること

Controls は「子のリスト」

Form も Panel も UserControl も、根っこは Control です。各 Control は、子を持つための Controls というプロパティを持っています。

イメージとしては次のような 入れ子のツリーです。

Form(this)
 └─ Controls
      ├─ panel1
      │    └─ Controls
      │         └─ button1
      └─ label1

デザイナで フォームに直接置いた コントロールは、this.Controls に並びます。Panel の上に置いた コントロールは、その Panel の panel1.Controls に並びます。


Controls.Add が決めること(親子の両面)

次の1行は、

this.Controls.Add(this.label1);

次の2つを 同時に 満たします。

意味
親(Form)this.Controls に label1 が 登録される
子(Label)label1.Parent が この Form になる

だから new しただけのコントロールは「宙に浮いたオブジェクト」で、Add されて初めて どのコンテナの子かが決まり、描画やマウス操作の対象として扱われます。

Remove や Clear をすると、登録が外れ、親子のつながりも変わります。


Location は「親から見た位置」

label1.Location = new Point(10, 20); の (10, 20) は、画面の絶対座標ではなく親コントロールの左上からの距離です。

  • フォーム直下なら フォームのクライアント領域の左上が原点
  • Panel の子なら Panel の左上が原点

デザイナで Panel に入れたあと、同じ数値でも見え方が変わるのは、このためです。


列挙(foreach)で「全部」は取れない?

次のコードは、フォームの直下の子だけを列挙します。

foreach (Control c in this.Controls)
{
    // c は「このフォームの子」1つずつ
}

Panel の中の Button は、this.Controls には 直接は出てきません。出てくるのは panel1 だけです。Button を見たければ、

foreach (Control c in this.panel1.Controls)
{
    // panel1 の子
}

のように その親の Controls を見ます。

「画面にあるコントロール全部」を調べたい場合は、直下だけで終わらず 子の Controls に再帰的に入る必要があります(今回はイメージまで。必要になったらメソッド化して掘り下げます)。


デザイナのコードで確認する

Form1.Designer.cs の InitializeComponent では、だいたい次の順序が多いです。

  1. 子コントロールを new してプロパティを設定する
  2. コンテナの Controls.Add で子を載せる(入れ子なら内側から外側へ)

panel1.Controls.Add(this.button1); のあとに this.Controls.Add(this.panel1); のように、内側のコンテナから先に子を固めるイメージで読むと追いやすいです。


実験して理解する

ボタンの Click で、コードだけ Panel と Label を作って載せてみます。

private void button1_Click(object sender, EventArgs e)
{
    var panel = new Panel();
    panel.Location = new System.Drawing.Point(20, 20);
    panel.Size = new System.Drawing.Size(200, 100);
    panel.BorderStyle = BorderStyle.FixedSingle;

    var lbl = new Label();
    lbl.Text = "Panelの中";
    lbl.Location = new System.Drawing.Point(10, 10);

    panel.Controls.Add(lbl);
    this.Controls.Add(panel);
}
  • lbl は panel.Controls に入るので、見た目も座標も Panel 基準になる
  • panel は this.Controls に入るので、フォームの子になる

Label編の 「作る → 設定する → Add」 と同じ3拍子で、Add する親が違うと階層が変わることが手に取るように分かります。


まとめ

  • Controls は、そのコントロールの 子一覧(コレクション)
  • 親.Controls.Add(子) で親子がつながり、子の Location の原点も親になる
  • foreach (Control c in this.Controls) は 直下だけ。孫は別の Controls を見る

次の記事

入門シリーズの Buttonのクリックイベント とあわせて読むと、次回の「+= の正体」がつながりやすいです。

目次は シリーズ固定ページ を参照してください。

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

広告