【C#】WindowsFormsアプリで秒針が0に来た時にイベントを実行する(カスタムイベント、タイマーコントロール版)

2023年9月1日

前回までの2つのサンプルでは、イベントの登録をForm1クラスで行っていました
今回は、タイマーをコントロールするクラスを作成し、そこでイベントの登録をします

前回までのサンプル

タイマーコントロールクラスでイベントを登録する

Action型でeventを使う場合

using System;
using System.Windows.Forms;

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

            // TimerControllerクラスのインスタンスを作成し、1000ミリ秒(1秒)ごとにイベントを発生させるタイマーを設定します
            TimerController timerController = new TimerController(1000);

            // タイマーイベントが発生した際に、OnTimerEventメソッドを実行するイベントハンドラを登録します
            timerController.TimerEvent += OnTimerEvent;
        }

        // タイマーイベントが発生した際に実行されるメソッドです
        private void OnTimerEvent()
        {
            // コンソールに"時間経過"というメッセージを表示します
            Console.WriteLine("時間経過");
        }
    }
}

このコードは、C#プログラムでWindowsフォームアプリケーションを作成するためのものです。以下はコードの主要な部分と、それが何をしているかに関する説明です。

  1. using ステートメント:
    • System 名前空間と System.Windows.Forms 名前空間を使用しています。これらの名前空間には、Windowsアプリケーションの開発に必要なクラスや機能が含まれています。
  2. namespace 宣言:
    • TimerControlSample という名前の新しい名前空間を定義しています。名前空間は、プログラムのさまざまな部分を分離し、名前の競合を防ぐのに役立ちます。
  3. Form1 クラス:
    • Form1 は、Windowsフォームアプリケーションのメインウィンドウを表すクラスです。Form クラスを継承しています。
  4. public Form1() メソッド:
    • Form1 クラスのコンストラクタです。ウィンドウが初めて生成されるときに呼び出されます。
  5. InitializeComponent() メソッド:
    • このメソッドは、Windowsフォームデザイナによって自動生成されたコードを呼び出します。これにより、ウィンドウのデザインやコントロールが初期化されます。
  6. TimerController インスタンスの作成:
    • TimerController クラスの新しいインスタンスを作成しています。このクラスは、1000ミリ秒(1秒)ごとにタイマーイベントを発生させるために使用されます。
  7. timerController.TimerEvent += OnTimerEvent;:
    • TimerController インスタンスの TimerEvent イベントに、OnTimerEvent メソッドをイベントハンドラとして追加しています。つまり、1秒ごとにタイマーが経過すると、OnTimerEvent メソッドが呼び出されます。
  8. OnTimerEvent() メソッド:
    • これはタイマーイベントのハンドラメソッドです。タイマーが経過したときに実行され、コンソールに “時間経過" というメッセージを出力します。

このコードは、Windowsフォームアプリケーションの基本的な構造を示しており、タイマーを使用して定期的な処理を行う方法を示しています。特に、タイマーのイベントハンドリングの仕組みが重要です。タイマーが経過するたびに、指定されたメソッド(この場合は OnTimerEvent)が呼び出され、特定の処理が実行されます。

using System;
using System.Windows.Forms;

namespace TimerControlSample
{
    internal class TimerController
    {
        // タイマーイベントが発生したときに他のクラスから通知を受けるためのイベント
        public event Action TimerEvent;

        public TimerController(int interval)
        {
            // タイマーコントロールのインスタンスを作成
            Timer timer = new Timer();

            // タイマーのインターバル(時間間隔)を設定
            timer.Interval = interval;

            // タイマーが Tick イベントを発生させたときに Timer_Tick メソッドを呼び出すイベントハンドラを追加
            timer.Tick += Timer_Tick;

            // タイマーを開始して、タイマーイベントを有効にする
            timer.Start();
        }

        // タイマーの Tick イベントが発生したときに呼び出されるメソッド
        private void Timer_Tick(object sender, EventArgs e)
        {
            // 登録されたイベントハンドラを呼び出して、タイマーイベントを通知
            TimerEvent?.Invoke();
        }
    }
}

このコードは、C#言語を使用してWindowsフォームアプリケーションのタイマーコントロールを制御するためのクラスを定義しています。以下、コードの要点を説明します。

  1. using System; および using System.Windows.Forms; は、C#の標準ライブラリとWindowsフォームのライブラリを使用するために必要な using ディレクティブです。
  2. namespace TimerControlSample は、このクラスが属する名前空間を定義しています。名前空間は、関連するクラスや要素をグループ化するために使用されます。
  3. internal class TimerController は、TimerControllerという名前の内部クラスを宣言しています。内部クラスは、同じ名前空間内の他のクラスからアクセスできますが、外部からはアクセスできません。
  4. public event Action TimerEvent; は、TimerEventという名前のイベントを宣言しています。イベントは、特定のアクションが発生したときに外部のコードに通知するために使用されます。この場合、タイマーが特定の間隔で発生するイベントを表しています。
  5. public TimerController(int interval) は、TimerControllerクラスのコンストラクタです。コンストラクタは、オブジェクトが作成される際に自動的に呼び出されます。このコンストラクタは、タイマーの間隔を指定する整数値 interval を受け取ります。
  6. コンストラクタ内部で、新しいTimerオブジェクトを作成し、指定された interval の間隔でTickイベントが発生するように設定しています。そして、Tickイベントが発生したときに Timer_Tick メソッドが呼び出されるようにイベントハンドラを登録し、タイマーを開始しています。
  7. private void Timer_Tick(object sender, EventArgs e) は、Tickイベントが発生したときに呼び出されるイベントハンドラメソッドです。このメソッド内で、TimerEventイベントを呼び出しています。TimerEvent?.Invoke(); の部分は、TimerEventがnullでない場合にイベントを発生させる安全な方法です。これにより、イベントが登録されていない場合にnull参照エラーが発生しなくなります。

このクラスを使用すると、タイマーイベントを監視し、特定の間隔でコードを実行することができます。外部のコードは、TimerEventイベントを購読して、タイマーが発生したときに実行したいアクションを指定できます。

クラス図

シーケンス図

EventHandlerクラスでeventを使う場合

登録済みにEventHandlerクラスを使うと、引数を2つ渡すことができます

using System;
using System.Windows.Forms;

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

            TimerController timerController = new TimerController(1000);
            timerController.TimerEvent += OnTimerEvent;
        }

        private void OnTimerEvent(object sender, EventArgs e)
        {
            Console.WriteLine("時間経過");
        }
    }
}
using System;
using System.Windows.Forms;

namespace TimerControlSample
{
    internal class TimerController
    {
        public event EventHandler TimerEvent;

        public TimerController(int interval)
        {
            Timer timer = new Timer();
            timer.Interval = interval;
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            TimerEvent?.Invoke(this, EventArgs.Empty);
        }
    }
}

.NETで登録済みのEventHandlerとEventArgsについて

EventHandler

EventHandler は、C#プログラムでイベントを定義および処理するためのデリゲート型の一つです。イベントは、オブジェクト間の通知と通信に使用され、一つのオブジェクトが別のオブジェクトに何かが起こったことを通知するために使用されます。通常、GUIアプリケーションや多くのアプリケーションフレームワークで広く利用されています

.Netの逆アセンブルで確認したコード

.NetのSystem名前空間で定義されているEventHandlerになります

#region アセンブリ mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\mscorlib.dll
// Decompiled with ICSharpCode.Decompiler 7.1.0.6543
#endregion

using System.Runtime.InteropServices;

namespace System
{
    //
    // 概要:
    //     イベント データを含まないイベントを処理するメソッドを表します。
    //
    // パラメーター:
    //   sender:
    //     イベントのソース。
    //
    //   e:
    //     イベント データを格納していないオブジェクト。
    [Serializable]
    [ComVisible(true)]
    [__DynamicallyInvokable]
    public delegate void EventHandler(object sender, EventArgs e);
}
#if false // 逆コンパイルのログ
キャッシュ内の '12' 個の項目
#endif

EventHandlerの定義

public delegate void EventHandler(object sender, EventArgs e);

このコードは、C#プログラムでイベントハンドラを宣言するためのコードです。以下にコードの要素を説明します。

  1. public キーワード: このキーワードは、このデリゲート型が他のクラスからアクセス可能であることを示しています。つまり、他のクラスからこのデリゲート型を使用できます。
  2. delegate キーワード: delegate キーワードは、イベントハンドラのデリゲート型を宣言します。デリゲートは、メソッドへの参照を格納し、そのメソッドを呼び出すために使用されます。
  3. void キーワード: このデリゲート型が戻り値を持たないことを示します。つまり、イベントハンドラが何か値を返す必要はありません。
  4. EventHandler イディオム: EventHandler はイベントハンドラの一般的な名前です。この名前を使用することで、他のプログラマーがイベントハンドラデリゲートを理解しやすくなります。
  5. (object sender, EventArgs e) パラメーター: このデリゲート型は、イベントハンドラとして使用されるメソッドが2つのパラメーターを受け取ります。一つ目のパラメーター sender は、イベントの発生元を示すオブジェクトです。二つ目のパラメーター e は、イベントに関連する情報を含む EventArgs クラスのオブジェクトです。通常、これらのパラメーターはイベントが発生したときに自動的に渡されます。

このコードは、イベントが発生したときに呼び出すためのイベントハンドラを表すデリゲート型を宣言しています。このデリゲート型を使用することで、特定のイベントに対するメソッドを指定できます。そして、そのメソッドは指定されたイベントが発生したときに呼び出され、sendere パラメーターを受け取ることができます。

EventArgs

.Netの逆アセンブルで確認したコード

.NetのSystem名前空間で定義されているEventArgsになります

#region アセンブリ mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\mscorlib.dll
// Decompiled with ICSharpCode.Decompiler 7.1.0.6543
#endregion

using System.Runtime.InteropServices;

namespace System
{
    //
    // 概要:
    //     イベント データを格納するクラスの基底クラスを表し、イベント データを含まないイベントに使用する値を提供します。
    [Serializable]
    [ComVisible(true)]
    [__DynamicallyInvokable]
    public class EventArgs
    {
        //
        // 概要:
        //     イベント データのないイベントに使用する値を提供します。
        [__DynamicallyInvokable]
        public static readonly EventArgs Empty = new EventArgs();

        //
        // 概要:
        //     System.EventArgs クラスの新しいインスタンスを初期化します。
        [__DynamicallyInvokable]
        public EventArgs()
        {
        }
    }
}
#if false // 逆コンパイルのログ
キャッシュ内の '12' 個の項目
#endif

EventHandlerの定義

public class EventArgs
{
    // イベント データのないイベントに使用する値を提供します。
    public static readonly EventArgs Empty = new EventArgs();
}

このコードは、C#プログラミング言語でイベントハンドリングに関連する概念をサポートするための基本的なクラスを定義しています。具体的には、EventArgsという名前のクラスを定義しています。

このクラスに含まれている要素は以下です:

  1. public static readonly EventArgs Empty プロパティ:
    • このプロパティは、EventArgsクラスの静的なフィールドとして宣言されています。
    • readonly修飾子が付いているため、一度初期化された後は変更できない定数となります。
    • Empty プロパティは new EventArgs() を使って初期化されており、このクラスの新しいインスタンスを生成しています。
    • Empty プロパティは、イベントハンドラがイベントデータを伴わない場合に使用されます。つまり、イベントが何か特定の情報を持たない場合に、代替の EventArgs オブジェクトとして使用されることがあります。

このクラスは、主にイベントハンドリングにおいて、イベントデータを伴わない場合に使われます。一般的なイベントハンドラの宣言では、通常 EventHandler デリゲートと一緒に使用され、以下のようになります:

public event EventHandler MyEvent;

// イベントを発生させるコード
if (MyEvent != null)
{
    MyEvent(this, EventArgs.Empty); // イベントデータが不要な場合、EventArgs.Emptyを使用
}

このように、EventArgs.Empty オブジェクトは、イベントハンドラに渡されるデフォルトのイベントデータとして使用されます。