【WinForm】イベントを使ったフォーム間通信

複数のフォームを使ったアプリで、サブ画面の更新をきっかけにメインの画面(クラス)で登録されているメソッドが実行される方法についてみていきましょう

サンプルの構成(データを渡さなくても良い場合)

このサンプルコードは、Windows Forms アプリケーションで使用されるイベント駆動型プログラミングの一例を示しています。具体的には、メインフォーム (Form1) とサブフォーム (SubForm) 間でのイベント通信の実装方法を示しています。以下、デザイン画面とコードの主要な部分について説明します。

Form1

デザイン画面

Form1 クラス

  • Form1 はメインフォームを表し、Form クラスから派生しています。
  • コンストラクタ (public Form1()) 内で、InitializeComponent() メソッドを呼び出してフォームの初期化を行っています。これはデザイナーによって生成されたコードで、フォーム上のコントロールの配置やプロパティの設定を含みます。
  • その後、SubForm インスタンスを作成し、SubForm から発行される OnButtonClicked イベントに CheckSubForm メソッドをイベントハンドラとして登録しています。これにより、サブフォーム上の特定のボタンがクリックされたときに CheckSubForm メソッドが呼び出されるように設定されています。
  • CheckSubForm メソッドでは、メインフォーム上のラベル (label1) のテキストを変更して、サブフォームのボタンが押されたことを示しています。
// メインフォームのクラス定義
public partial class Form1 : Form
{
    // Form1のコンストラクタ
    public Form1()
    {
        InitializeComponent(); // フォームの初期設定を行うメソッド

        // サブフォームのインスタンスを作成
        SubForm subForm = new SubForm();
        // サブフォームのボタンクリックイベントにイベントハンドラを登録
        subForm.OnButtonClicked += CheckSubForm;
        // サブフォームを表示
        subForm.Show();
    }

    // サブフォームのボタンがクリックされた時に呼び出されるメソッド
    public void CheckSubForm()
    {
        // メインフォーム上のラベルのテキストを変更
        label1.Text = "サブフォームのボタンが押された";
    }
}

SubForm

デザイン画面

button1コントロールのイベントハンドラの登録

SubForm クラス

  • SubForm はサブフォームを表し、同様に Form クラスから派生しています。
  • このクラスでは、OnButtonClicked という Action 型のイベントを公開しています。これは、サブフォーム上の特定のボタンがクリックされたことを外部に通知するためのイベントです。
  • イベントハンドラ OnSubFormButtonClicked は、サブフォーム上のボタンがクリックされたときに呼び出されるメソッドです。このメソッド内で、OnButtonClicked イベントが null でないかをチェックし(null 条件演算子 ?. を使用)、null でない場合にイベントを発火 (Invoke()) しています。これにより、イベントに登録されたすべてのハンドラ(この場合は Form1CheckSubForm メソッド)が呼び出されます。
// サブフォームのクラス定義
public partial class SubForm : Form
{
    // サブフォームのボタンクリックイベントを外部に公開するイベントデリゲート
    public event Action OnButtonClicked;

    // SubFormのコンストラクタ
    public SubForm()
    {
        InitializeComponent(); // フォームの初期設定を行うメソッド
    }

    // サブフォームのボタンがクリックされた時に呼び出されるイベントハンドラ
    private void OnSubFormButtonClicked(object sender, EventArgs e)
    {
        // イベントがnullでないかチェックしてからイベントを発生
        OnButtonClicked?.Invoke();
    }
}

実行結果

  1. アプリケーションが開始され、Form1 のインスタンスが作成されます。
  2. Form1 のコンストラクタが呼ばれ、InitializeComponent() が実行されます。これにより、フォーム上のコントロール(例えば label1 など)が配置されます。
  3. コンストラクタ内で SubForm のインスタンスが生成され、OnButtonClicked イベントに CheckSubForm メソッドがイベントハンドラとして関連付けられます。
  4. SubForm が表示されます。
  5. ユーザーが SubForm 上のボタン(おそらく button1 と名付けられたボタン)をクリックします。
  6. ボタンのクリックイベントが OnSubFormButtonClicked メソッドをトリガーし、そのメソッド内で OnButtonClicked イベントが発火します(。
  7. OnButtonClicked イベントの発火により、Form1CheckSubForm メソッドが呼び出されます。
  8. CheckSubForm メソッドによって、Form1 上の label1 のテキストが「サブフォームのボタンが押された」という文字列に変更されます。

実行結果として、ユーザーが SubForm のボタンをクリックすると、Form1 上の label1 が新しいテキストで更新され、ユーザーにアクションが行われたことが視覚的にフィードバックされることになります。

サンプルの構成(データを渡したい場合)

このサンプルは、Windows Forms アプリケーションでカスタムイベント引数を持つイベントの使用方法を示すものです。Form1 クラスと SubForm クラス、およびカスタムイベント引数を定義する MyEventArgs クラスで構成されています。

Form1

デザイン画面

Form1 クラス

  • Form1System.Windows.Forms.Form クラスを継承しています。これは、Windows Forms アプリケーションでウィンドウ(フォーム)を作成するための基本クラスです。
  • コンストラクタ public Form1() は、フォームがインスタンス化されたときに呼ばれます。
  • コンストラクタ内で InitializeComponent() メソッドが呼ばれることにより、フォーム上のコントロール(この場合はおそらく label1)の初期設定が行われます。
  • 新たに SubForm インスタンスが作成され、subForm.OnButtonClicked というイベントに CheckSubForm メソッドがイベントハンドラとして登録されます。これにより、サブフォームで定義されたイベントが発生した際に、Form1CheckSubForm メソッドが呼び出されるようになります。
  • subForm.Show(); により、サブフォームが表示されます。
  • CheckSubForm メソッドは、SubForm からのイベントを受け取り、label1 のテキストを更新します。ここでイベントの引数として MyEventArgs クラスのインスタンスが渡され、その Hp プロパティの値をラベルに表示します。
using System.Windows.Forms;

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

            SubForm subForm = new SubForm();
            subForm.OnButtonClicked += CheckSubForm;
            subForm.Show();
        }

        private void CheckSubForm(object sender, MyEventArgs e)
        {
            label1.Text = $"サブフォームのボタンが押された {e.Hp}";
        }
    }
}

SubForm

デザイン画面

このサンプルでは、button1コントロールのイベントハンドラの登録はコードで実現していますので不要です

SubForm クラスと MyEventArgs クラス

  • SubForm もまた Form クラスを継承しています。
  • SubForm には EventHandler<MyEventArgs> デリゲートを使った OnButtonClicked というイベントが定義されています。このイベントは SubForm のボタンがクリックされた時に発生します。
  • button1.Click イベントハンドラは匿名メソッドを使用しており、ボタンがクリックされると MyEventArgs インスタンスを新規作成し、Hp プロパティに 100 を設定してから OnButtonClicked イベントを発火させます。
  • MyEventArgsEventArgs クラスから派生していて、Hp という追加の情報をイベントと共に送ることができます。

このコードの全体的な流れは、Form1 が起動するとすぐに SubForm を表示し、サブフォーム上のボタンがクリックされたときにメインフォームの label1 を更新して、「サブフォームのボタンが押された」と表示するとともに、Hp の値を表示するというものです。

using System;
using System.Windows.Forms;

namespace FormComSample
{
    public partial class SubForm : Form
    {
        public event EventHandler<MyEventArgs> OnButtonClicked;

        public SubForm()
        {
            InitializeComponent();

            button1.Click += (s, e) =>
            {
                MyEventArgs args = new MyEventArgs();
                args.Hp = 100;

                // nullチェックを追加
                OnButtonClicked?.Invoke(this, args);
            };
        }
    }

    public class MyEventArgs : EventArgs
    {
        public int Hp;
    }
}

実行結果

  1. アプリケーションが起動され、Form1 のインスタンスが作成されます。
  2. Form1 のコンストラクタが実行され、InitializeComponent メソッドを通じてフォーム上のコントロールが初期化されます。
  3. SubForm の新しいインスタンスが作成され、OnButtonClicked イベントに対して CheckSubForm メソッドがイベントハンドラとして登録されます。
  4. subForm.Show() が呼び出され、サブフォームが表示されます。
  5. ユーザーが SubForm 上の button1 をクリックすると、button1.Click イベントに関連付けられた匿名メソッドが実行されます。
  6. 匿名メソッド内で MyEventArgs インスタンスが生成され、Hp プロパティに 100 が設定されます。
  7. OnButtonClicked イベントが発火され、Form1CheckSubForm メソッドがコールバックとして呼び出されます。
  8. CheckSubForm メソッドが label1 のテキストを更新し、サブフォームのボタンが押されたことと、Hp の値(この例では 100)が表示されます。

結果として、Form1 上の label1 には "サブフォームのボタンが押された 100" というテキストが表示されることになります。これにより、サブフォームでのユーザーのアクションがメインフォームに伝えられ、その結果がメインフォーム上でユーザーにフィードバックされます。