WindowsFormsアプリでButtonクラスを継承して自作のボタンを作成する方法

2023年2月7日

ツールボックスを使わずに実行時にボタンを作成

コード

namespace WeatherNet7
{
    public partial class Form1 : Form
    {
        MyButton myButton = new MyButton();

        public Form1()
        {
            InitializeComponent();

            myButton.Parent = this;
            myButton.Text = "HelloButton";
            myButton.AutoSize= true;
            myButton.MyClick += ShowMyMessage;
        }

        private void ShowMyMessage(MyEventArgs e)
        {
            MessageBox.Show(e.Message);
        }
    }

    internal class MyButton : Button
    {
        public event Action<MyEventArgs> MyClick;

        protected virtual void OnClick(MyEventArgs e)
        {
            MyClick?.Invoke(e); 
        }
        protected override void OnClick(EventArgs e)
        {
            MyEventArgs myEventArgs = new MyEventArgs();
            myEventArgs.Message = "Hello";

            OnClick(myEventArgs);

            base.OnClick(e);
        }
    }

    class MyEventArgs : EventArgs
    {
        public string Message { get; set; }
    }
}

解説

このコードは、ユーザー定義のボタンクラスMyButtonを使って、WinFormsアプリケーションを作成するためのものです。

まず、Form1クラスはWinFormsのFormクラスを拡張したものです。このクラスでは、ユーザー定義のボタンMyButtonを作成し、フォームに追加し、必要なプロパティ(テキスト、自動サイズなど)を設定します。また、ボタンのクリックイベントを登録するためのShowMyMessageメソッドも定義しています。

次に、MyButtonクラスはWinFormsのButtonクラスを継承したものです。このクラスでは、新しいクリックイベント(MyClick)を定義しています。このイベントは、MyEventArgsクラスを引数として受け取ります。このクラスには、クリックされたときに表示するメッセージ("Hello")が含まれています。

このMyButtonクラスは、WinFormsのButtonクラスに実装されているOnClickメソッドをオーバーライドしています。このメソッドは、MyEventArgsクラスを使って、新しいクリックイベント(MyClick)を発生させます。また、WinFormsのOnClickメソッドを呼び出すことで、通常のクリックイベントも発生させます。

最後に、MyEventArgsクラスは、新しいクリックイベント(MyClick)に使用する引数を表すものです。このクラスには、クリックされたときに表示するメッセージ(Messageプロパティ)が含まれています。

このコードを実行すると、ユーザー定義のボタンをクリックすることで、"Hello"というメッセージが表示されます。

実行結果

クラス図

eventキーワードの効果

myButton.MyClick += ShowMyMessage;

class MyButton : Button
{
    public event Action<MyEventArgs> MyClick;
}

このコードで、+=のところを=にすると次のエラーになります

しかし、eventキーワードを省くとエラーになりません
では、どのような効果があるのでしょう
イベントは、複数のメソッドを順次実行できます
+=でどんどんメソッドを追加していけます
しかし、=でも代入できるとなるとそれまで追加してきたメソッドを上書きすることになりますので、全ての登録メソッドも無くなってしまいます
つまり、不用意な上書きを防ぐ効果があるのです
削除する場合は、-=を使います

delegateとActionについて

次の3つのコードは同じものとなります

public event Action<MyEventArgs> MyClick ;

この行のpublic event Action<MyEventArgs> MyClick;は、カスタムボタンクラスMyButton内で定義されているイベントを示しています。

  • public: 外部からアクセス可能なアクセス修飾子
  • event: イベントを定義するキーワード
  • Action<MyEventArgs>: イベントデリゲートのタイプ。ここでは、引数がMyEventArgs型であるデリゲートを指しています。
  • MyClick: イベント名

このイベントは、Form1のコンストラクタ内で設定されます。myButton.MyClick += ShowMyMessage;という形式で、イベントに処理を追加することができます。このイベントが発生したときに、追加された処理ShowMyMessageが呼び出されます。

public event EventHandler<MyEventArgs> MyClick ;

この行のpublic event EventHandler<MyEventArgs> MyClick ;は、カスタムボタンクラスMyButton内で定義されているイベントを示しています。

  • public: 外部からアクセス可能なアクセス修飾子
  • event: イベントを定義するキーワード
  • EventHandler<MyEventArgs>: イベントデリゲートのタイプ。ここでは、引数がMyEventArgs型のイベントデリゲートを指しています。
  • MyClick: イベント名

このイベントは、Form1のコンストラクタ内で設定されます。myButton.MyClick += ShowMyMessage;という形式で、イベントに処理を追加することができます。このイベントが発生したときに、追加された処理 ShowMyMessageが呼び出されます。

public delegate void MyEventHandler(MyEventArgs e);
public event MyEventHandler MyClick;

このコードは、カスタムイベントデリゲートを定義し、イベントを作成しています。

  • public delegate void MyEventHandler(MyEventArgs e): カスタムデリゲートを定義しています。名前はMyEventHandler、引数はMyEventArgs eとなっています。
  • public event MyEventHandler MyClick: カスタムデリゲートMyEventHandlerを使ってイベントを定義しています。名前はMyClickです。

このイベントは、以降のコード内で処理を追加することができます。例えば、MyClick += ShowMyMessage;という形式で、イベントに処理を追加することができます。このイベントが発生したときに、追加された処理 ShowMyMessageが呼び出されます。

ActionのSystem名前空間での定義

namespace System
{
    //
    // 概要
    // パラメータを1つ持ち、値を返さないメソッドをカプセル化します。
    //
    // パラメーター
    // obj:
    // このデリゲートがカプセル化するメソッドのパラメータ。
    //
    // 型パラメーター。
    // T:
    // このデリゲートがカプセル化するメソッドのパラメータの型。
    public delegate void Action<in T>(T obj);
}