WinForms フォーム間通信 チュートリアル(コンストラクタやイベントを活用)

2025年3月3日

~Form2からForm1のラベル更新を例として~

概要

本資料では、WinFormsアプリケーションにおいて、Form2のボタン押下によりForm1のラベルのテキストを更新する方法を、以下の3つのアプローチで紹介します。

  1. 直接呼び出し(公開メソッドを利用)
    Form1に公開メソッドを実装し、Form2からそのメソッドを呼び出す方法です。シンプルですが、フォーム間の依存が強くなります。
  2. イベントとデリゲートを利用する方法
    Form2でイベントを定義し、Form1がそのイベントに対してハンドラを登録することで、疎結合な設計を実現します。
  3. データバインディングを利用する方法
    共通のデータオブジェクトを両フォームで共有し、プロパティの変更により自動的にUIが更新される仕組みを利用します。

1. 直接呼び出し(公開メソッドを利用する方法)

説明

Form1のラベルはデフォルトではprivateのため、直接アクセスできません。
そのため、Form1に公開メソッド SetLabelText を用意し、Form2から呼び出すことでラベルのテキストを更新します。

コード例

Form1.cs

using System;
using System.Windows.Forms;

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

        // Form2からラベルのテキストを更新するための公開メソッド
        public void SetLabelText(string text)
        {
            label1.Text = text;
        }

        // Form2を開くボタンのクリックイベントハンドラ
        private void buttonOpenForm2_Click(object sender, EventArgs e)
        {
            Form2 form2 = new Form2(this);
            form2.Show();
        }
    }
}

Form2.cs

using System;
using System.Windows.Forms;

namespace MethodAccessSample
{
    public partial class Form2 : Form
    {
        // Form1のインスタンスを保持するフィールド
        private Form1 mainForm;

        // コンストラクタでForm1のインスタンスを受け取る
        public Form2(Form1 form1)
        {
            InitializeComponent();
            mainForm = form1;
        }

        // ボタンのクリックイベントでForm1のラベルを更新
        private void buttonUpdateLabel_Click(object sender, EventArgs e)
        {
            if (mainForm != null)
            {
                // Form1の公開メソッドを利用してラベルのテキストを更新
                mainForm.SetLabelText("更新されました!");
            }
        }
    }
}

デザイナー設定例

  • Form1
    • Label: 名前を label1 に設定
    • Button: 名前を buttonOpenForm2 に設定し、Click イベントに buttonOpenForm2_Click を割り当て
  • Form2
    • Button: 名前を buttonUpdateLabel に設定し、Click イベントに buttonUpdateLabel_Click を割り当て

補足

  • デザイナーでlabel1のModifiersプロパティをPublicに変更する方法もありますが、カプセル化の観点から公開メソッドを利用する方法が推奨されます。

2. イベントとデリゲートを利用する方法

説明

Form2でイベントを定義し、Form1がそのイベントにハンドラを登録します。
これにより、Form2は更新処理の呼び出し先を意識せず、イベントを発火するだけで済むため、フォーム間の依存が低減され、再利用性・保守性が向上します。

コード例

Form2.cs

using System;
using System.Windows.Forms;

namespace EventAccessSample
{
    public partial class Form2 : Form
    {
        // Form1のラベルを更新するためのイベントを定義
        public event Action<string> LabelTextChanged;

        public Form2()
        {
            InitializeComponent();
        }

        // ボタン押下時のイベントハンドラ
        private void buttonUpdateLabel_Click(object sender, EventArgs e)
        {
            // イベントが登録されていれば、イベントを発火する
            LabelTextChanged?.Invoke("更新されました!(イベント利用)");
        }
    }
}

以下のコードは、C#でフォームのラベルを更新するためのイベントを定義しています。

// Form1のラベルを更新するためのイベントを定義
public event Action<string> LabelTextChanged;

各部分の解説

  • public
    このキーワードは、イベントがクラスの外部からもアクセスできることを意味します。つまり、他のクラスやフォームからこのイベントに対してイベントハンドラー(処理内容)を登録(購読)できるようになります。
  • event
    「イベント」は、ある「出来事」が発生したときに通知する仕組みです。イベントを利用することで、例えばフォーム内で何かが起こった場合に、登録されたメソッドが自動的に実行されるように設計できます。
  • Action
    Action<string> は、戻り値がなく引数として文字列を受け取るデリゲートです。
  • 使用例: 例えば、void UpdateLabel(string newText) というメソッドを登録すると、イベント発生時に newText が渡され、ラベルの更新処理が実行されます。
  • 置き換えの可能性:
    • Action<string> はあくまで便利な汎用デリゲートですが、同じ機能を持つ独自のデリゲートを定義することも可能です。たとえば、次のようにカスタムデリゲートを定義して使用する方法もあります。
    • このようにすることで、コードの可読性を高めたり、イベントに対する意味づけを明確にすることができます。
// 独自のデリゲート型を定義
public delegate void LabelTextChangedHandler(string newText);

// イベント定義で独自のデリゲートを使用
public event LabelTextChangedHandler LabelTextChanged;
  • LabelTextChanged
    • これはイベントの名前です。名前から分かるように、「ラベルのテキストが変更された」ことを通知するためのイベントであることを示しています。
  • 利用例: 他のクラスやフォームで、LabelTextChanged += UpdateLabel; のようにイベントハンドラーを登録すると、イベントが発生したときに UpdateLabel メソッドが自動的に実行され、ラベルが更新されます。

イベントの流れ

  1. イベントの定義:
    イベントをクラス内で定義しておくと、特定のタイミングでこのイベントを発火(raise)できます。
  2. イベントハンドラーの登録:
    他の場所で、イベントに対する処理を登録します。例として、フォームの初期化時に LabelTextChanged += UpdateLabel; のようにしておくと、後でイベントが発生したときに UpdateLabel が実行されます。
  3. イベントの発火:
    何らかのタイミング(例:ボタンがクリックされたとき、データが更新されたときなど)でイベントを呼び出すと、登録済みのハンドラーが実行され、ラベルのテキストが更新されます。

まとめ

  • 目的:
    このイベントは、フォームのラベルのテキストが変更されるタイミングで、他の部分にその変更を通知するために作られています。
  • 仕組み:
    クラス外部からイベントハンドラーを登録し、イベントが発生したときに自動的にそのハンドラーが呼び出される仕組みです。
  • Actionの代替:
    Action<string> は便利な汎用デリゲートですが、必要に応じて独自のデリゲート(例:LabelTextChangedHandler)に置き換えることもできます。これにより、イベントの意図や用途がより明確になり、コードの可読性が向上する場合があります。

このように、C#のイベント機構を利用することで、コードの各部分が独立して動作しながらも、必要なときに連携して動く柔軟なプログラム設計が可能になります。

Form1.cs

using System;
using System.Windows.Forms;

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

        // Form2を開くボタンのクリックイベントハンドラ
        private void buttonOpenForm2_Click(object sender, EventArgs e)
        {
            Form2 form2 = new Form2();
            // Form2のイベントにハンドラを登録し、ラベル更新処理を実施
            form2.LabelTextChanged += UpdateLabel;
            form2.Show();
        }

        // イベントハンドラ:ラベルのテキストを更新
        private void UpdateLabel(string text)
        {
            label1.Text = text;
        }
    }
}

補足

  • Form2は更新内容をイベントとして通知するだけなので、どのフォームが受け取るかを意識する必要がなくなります。
  • より疎結合な設計により、再利用性・保守性が向上します。

3. データバインディングを利用する方法

説明

共通のデータソース(ここではSharedDataクラス)をForm1とForm2で共有し、そのプロパティの変更に応じてUIが自動更新される仕組みを利用します。
WinFormsのバインディング機能を用いることで、変更通知によりラベルのテキストが自動更新されます。

コード例

共通データクラス: SharedData.cs

using System.ComponentModel;

namespace BindingAccessSample
{
    public class SharedData : INotifyPropertyChanged
    {
        private string labelText;
        public string LabelText
        {
            get => labelText;
            set
            {
                if (labelText != value)
                {
                    labelText = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LabelText)));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Form1.cs

using System.Windows.Forms;

namespace BindingAccessSample
{
    public partial class Form1 : Form
    {
        private SharedData sharedData;

        public Form1()
        {
            InitializeComponent();
            sharedData = new SharedData();
            // バインディング設定:LabelのTextプロパティとSharedDataのLabelTextプロパティを連動
            label1.DataBindings.Add("Text", sharedData, "LabelText", false, DataSourceUpdateMode.OnPropertyChanged);
        }

        private void buttonOpenForm2_Click(object sender, EventArgs e)
        {
            Form2 form2 = new Form2(sharedData);
            form2.Show();
        }
    }
}

このコードは、Windows Forms などで使われるデータバインディングの機能を利用して、UI コントロール(この場合は label1)のプロパティと、あるデータソース(sharedData)のプロパティを連動させる仕組みを設定しています。

コードの各部分の説明

  • label1.DataBindings.Add(“Text", …):
    label1 というラベルコントロールの Text プロパティにデータバインディングを追加しています。つまり、ラベルに表示される文字列を後でデータから自動で更新できるようにするという意味です。
  • sharedData:
    これはデータソースのオブジェクトです。通常、フォームのデータ(例えば、ユーザー情報や設定など)が格納されているオブジェクトになります。
  • “LabelText":
    sharedData の中にあるプロパティの名前です。ここでは、sharedData.LabelText の値が label1 の Text プロパティに反映されます。
  • false:
    この引数は、データのフォーマットを有効にするかどうかを指定します。ここでは false にしているため、特別なフォーマット変換は行われません。
  • DataSourceUpdateMode.OnPropertyChanged:
    これは更新モードを指定しており、「プロパティが変更されたときに更新する」という設定です。つまり、sharedData の LabelText プロパティが変更されると、即座に label1 の Text プロパティも更新されるようになります。

全体のまとめ

この1行のコードにより、sharedDataLabelText プロパティの値が変わるたびに、自動的に label1 の表示が更新されるようになります。これにより、手動で UI を更新する必要がなくなり、データと UI の同期が簡単に実現できるようになります。

Form2.cs

using System;
using System.Windows.Forms;

namespace BindingAccessSample
{
    public partial class Form2 : Form
    {
        private SharedData sharedData;

        public Form2(SharedData data)
        {
            InitializeComponent();
            sharedData = data;
        }

        private void buttonUpdateLabel_Click(object sender, EventArgs e)
        {
            // データソースのプロパティを更新すると、Form1のLabelも自動更新される
            sharedData.LabelText = "更新されました!(データバインディング利用)";
        }
    }
}

補足

  • 両フォームで共通のデータオブジェクトを利用することで、変更が自動的に反映されます。
  • WinFormsのデータバインディングはWPFほど柔軟ではありませんが、簡易な更新連動には十分に利用可能です。

まとめ

本資料では、WinFormsアプリケーションにおいて、Form2のボタン押下でForm1のラベルを更新する実装例を3つのアプローチで紹介しました。

  • 直接呼び出し(公開メソッド)
    シンプルな実装方法ですが、フォーム間の依存度が高まります。
  • イベントとデリゲート
    フォーム間の依存を軽減し、より疎結合な設計が可能になります。
  • データバインディング
    共通データを利用し、プロパティ変更時に自動的にUI更新を行います。

各手法は用途やアプリケーションの規模、保守性の観点から選択することができます。