質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.40%

  • C#

    9447questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C#, EventHandlerの多重登録回避について教えてください。

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 4,314

meshkit

score 62

 前提・実現したいこと

EventHandlerの登録について教えてください。
Windows Formアプリケーションを作っています。
EventHandlerをコードで登録する場合、繰り返し登録すると、イベントハンドラは多重登録できます。

 試したこと

たとえば下記のコードでButton1をクリックすると、Button2のイベントハンドラを多重登録します。
Button1を1回クリック、Button2を1回クリック。-MessageBoxを1回表示。内容は1。
Button1を2回クリック、Button2を1回クリック。-MessageBoxを2回表示。内容は1,2。
Button1を3回クリック、Button2を1回クリック。-MessageBoxを3回表示。内容は1,2,3。

 該当のソースコード

using System;
using System.Windows.Forms;

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

        private void button1_Click(object sender, EventArgs e)
        {
            counter = 0;
            //if (this.button2.Click==null)
                this.button2.Click += new System.EventHandler(this.button2_Click);
        }

        int counter = 0;
        private void button2_Click(object sender, EventArgs e)
        {
            counter++;
            MessageBox.Show(counter.ToString());
        }
    }
}
namespace WindowsFormsApplication1
{
    partial class Form1
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(207, 168);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(146, 99);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(75, 23);
            this.button2.TabIndex = 1;
            this.button2.Text = "button2";
            this.button2.UseVisualStyleBackColor = true;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 261);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
    }
}

 発生している問題・エラーメッセージ

これを回避するために、

    if (this.button2.Click==null)
        this.button2.Click += new System.EventHandler(this.button2_Click);


としたところ、
イベントControl.Clickは+=または-=の左側にのみ使用できます。
とエラーになります。

EventHandlerをコードで登録する場合、多重登録を回避する方法を知りたいです。
よろしくお願いします。

 補足情報(FW/ツールのバージョンなど)

Visual Studio 2015 Pro
C#

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • mattn

    2018/01/31 10:50

    多重登録された場合、先の物を生かすのでしょうか?それとも先の物を取り消し後の物を有効にするのでしょうか?

    キャンセル

  • meshkit

    2018/01/31 11:22

    先のものを生かします。

    キャンセル

回答 2

+3

mattin さんが書かれているようにイベントの add, remove を明示的に実装すれば制御できます。

もし、同じメソッドが重複して登録されないようにしたいのであれば、add に渡された value からそのメソッドのクラス名やメソッド名を取得できます。

既に登録されているメソッドは GetInvocationList で取得できます。
mattin さんの例示コードでは fireEvent.GetInvocationList() です。Delegate の配列が取得できますので、EventHandler にキャストしてください。

とはいえ、イベントハンドラは初期処理やフォームデザイナで定義しておいて、

・その処理を実行するかどうかを制御するフラグを設ける。既定値は無効。
・フラグが有効でない場合は何もしない。
・button2 がクリックされたときにそのフラグを有効にする。

としたほうがよいような気がします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/31 13:15

    ありがとうございます。mattinさんの回答、なるほどと思ったものの、実際にはbuttonではなくハードウェアからやってくるイベントで、ちょっと現在のわたしには参考にはなるものの実装は無理とあきらめてました。
    フラグ方式ならわたしにも使えそうです。こちらでやってみます。ありがとうございます。感謝。

    キャンセル

checkベストアンサー

+2

イベントハンドラを自前で実装すれば出来なくないです。

using System;
using System.Linq;

class Publisher 
{
    private event EventHandler fireEvent;

    public event EventHandler FireEvent
    {
        add
        {
            if (fireEvent == null)
            {
                fireEvent += value;
            }
            else
            {
                Console.WriteLine("重複登録ですよ");
            }
        }
        remove
        {
            fireEvent -= value;
        }
    }

    public void Fire()
    {
        OnFire(new EventArgs());
    }

    protected virtual void OnFire(EventArgs e)
    {
        if (fireEvent != null)
            fireEvent(this, e);
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var publisher = new Publisher();
        publisher.FireEvent += (o, e) =>
        {
            Console.WriteLine("fire event!");
        };
        publisher.FireEvent += (o, e) =>
        {
            Console.WriteLine("more fire event!");
        };
        publisher.Fire();
    }
}


ただしコントロールの Click 等はこれが実装されてはいませんので継承するなりしてカスタムコントロール化しないといけないかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.40%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    9447questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。