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

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

新規登録して質問してみよう
ただいま回答率
86.02%
C#

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

Q&A

解決済

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

meshkit
meshkit

総合スコア72

C#

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

2回答

0グッド

1クリップ

23618閲覧

投稿2018/01/31 01:43

前提・実現したいこと

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。

該当のソースコード

C#

1using System; 2using System.Windows.Forms; 3 4namespace WindowsFormsApplication1 5{ 6 public partial class Form1 : Form 7 { 8 public Form1() 9 { 10 InitializeComponent(); 11 } 12 13 private void button1_Click(object sender, EventArgs e) 14 { 15 counter = 0; 16 //if (this.button2.Click==null) 17 this.button2.Click += new System.EventHandler(this.button2_Click); 18 } 19 20 int counter = 0; 21 private void button2_Click(object sender, EventArgs e) 22 { 23 counter++; 24 MessageBox.Show(counter.ToString()); 25 } 26 } 27}

C#

1namespace WindowsFormsApplication1 2{ 3 partial class Form1 4 { 5 /// <summary> 6 /// 必要なデザイナー変数です。 7 /// </summary> 8 private System.ComponentModel.IContainer components = null; 9 10 /// <summary> 11 /// 使用中のリソースをすべてクリーンアップします。 12 /// </summary> 13 /// <param name="disposing">マネージ リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param> 14 protected override void Dispose(bool disposing) 15 { 16 if (disposing && (components != null)) 17 { 18 components.Dispose(); 19 } 20 base.Dispose(disposing); 21 } 22 23 #region Windows フォーム デザイナーで生成されたコード 24 25 /// <summary> 26 /// デザイナー サポートに必要なメソッドです。このメソッドの内容を 27 /// コード エディターで変更しないでください。 28 /// </summary> 29 private void InitializeComponent() 30 { 31 this.button1 = new System.Windows.Forms.Button(); 32 this.button2 = new System.Windows.Forms.Button(); 33 this.SuspendLayout(); 34 // 35 // button1 36 // 37 this.button1.Location = new System.Drawing.Point(207, 168); 38 this.button1.Name = "button1"; 39 this.button1.Size = new System.Drawing.Size(75, 23); 40 this.button1.TabIndex = 0; 41 this.button1.Text = "button1"; 42 this.button1.UseVisualStyleBackColor = true; 43 this.button1.Click += new System.EventHandler(this.button1_Click); 44 // 45 // button2 46 // 47 this.button2.Location = new System.Drawing.Point(146, 99); 48 this.button2.Name = "button2"; 49 this.button2.Size = new System.Drawing.Size(75, 23); 50 this.button2.TabIndex = 1; 51 this.button2.Text = "button2"; 52 this.button2.UseVisualStyleBackColor = true; 53 // 54 // Form1 55 // 56 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 57 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 58 this.ClientSize = new System.Drawing.Size(284, 261); 59 this.Controls.Add(this.button2); 60 this.Controls.Add(this.button1); 61 this.Name = "Form1"; 62 this.Text = "Form1"; 63 this.ResumeLayout(false); 64 65 } 66 67 #endregion 68 69 private System.Windows.Forms.Button button1; 70 private System.Windows.Forms.Button button2; 71 } 72}

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

これを回避するために、

C#

1 if (this.button2.Click==null) 2 this.button2.Click += new System.EventHandler(this.button2_Click);
としたところ、 イベントControl.Clickは+=または-=の左側にのみ使用できます。 とエラーになります。 EventHandlerをコードで登録する場合、多重登録を回避する方法を知りたいです。 よろしくお願いします。

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

Visual Studio 2015 Pro
C#

以下のような質問にはグッドを送りましょう

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

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

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

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

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

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

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

適切な質問に修正を依頼しましょう。

mattn

2018/01/31 01:50

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

2018/01/31 02:22

先のものを生かします。

回答2

3

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

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

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

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

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

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

投稿2018/01/31 04:09

masa_n

総合スコア110

umyu, meshkit, nanana71👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

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

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

このような回答には修正を依頼しましょう。

回答へのコメント

meshkit

2018/01/31 04:15

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

2

ベストアンサー

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

csharp

1using System; 2using System.Linq; 3 4class Publisher 5{ 6 private event EventHandler fireEvent; 7 8 public event EventHandler FireEvent 9 { 10 add 11 { 12 if (fireEvent == null) 13 { 14 fireEvent += value; 15 } 16 else 17 { 18 Console.WriteLine("重複登録ですよ"); 19 } 20 } 21 remove 22 { 23 fireEvent -= value; 24 } 25 } 26 27 public void Fire() 28 { 29 OnFire(new EventArgs()); 30 } 31 32 protected virtual void OnFire(EventArgs e) 33 { 34 if (fireEvent != null) 35 fireEvent(this, e); 36 } 37} 38 39public class Program 40{ 41 static void Main(string[] args) 42 { 43 var publisher = new Publisher(); 44 publisher.FireEvent += (o, e) => 45 { 46 Console.WriteLine("fire event!"); 47 }; 48 publisher.FireEvent += (o, e) => 49 { 50 Console.WriteLine("more fire event!"); 51 }; 52 publisher.Fire(); 53 } 54}

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

投稿2018/01/31 02:55

mattn

総合スコア5028

umyu, nanana71👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

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

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

このような回答には修正を依頼しましょう。

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

ただいまの回答率
86.02%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

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

C#

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