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

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

新規登録して質問してみよう
ただいま回答率
85.50%
WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

2回答

4091閲覧

WPFでButtonのEventが存在するか知りたい

cancat

総合スコア313

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

0クリップ

投稿2016/11/07 05:37

こんにちは。
Windows10でWPFのアプリケーションを開発しています。
Visual Studio 2015 Communityを使っています。

###前提・実現したいこと
event handlerを.csのコードで割り当てる場合、複数回割り当てるとeventを複数回実行してしまいます。
if (button.Clickのeventhandlerの設定をしていなければ)と条件づけたいです。

###試したこと
button.Click -= Button_Click;
回数が不明なので、これでは不十分。

###発生している問題・エラーメッセージ
event handlerを.csのコードで割り当てる場合、複数回割り当てるとeventを複数回実行してしまう。

###該当のソースコード

xaml

1<Window x:Class="MultiEvent.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:MultiEvent" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded"> 9 <Grid> 10 <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="229,260,0,0" VerticalAlignment="Top" Width="75"/> 11 12 </Grid> 13</Window>

C#

1using System.Windows; 2 3namespace MultiEvent { 4 public partial class MainWindow : Window { 5 public MainWindow() { 6 InitializeComponent(); 7 } 8 9 private void Window_Loaded(object sender, RoutedEventArgs e) { 10 button.Click += Button_Click; 11 } 12 13 int counter = 0; 14 private void Button_Click(object sender, RoutedEventArgs e) { 15 //if (button.Clickのeventhandlerの設定をしていなければ) 16 button.Click += Button_Click; 17 MessageBox.Show(counter.ToString()); 18 } 19 } 20}

###補足情報(言語/FW/ツール等のバージョンなど)
Microsoft Visual Studio Community 2015
Version 14.0.25424.00 Update 3
Microsoft .NET Framework
Version 4.6.01038

インストールしているバージョン:Community

Visual C# 2015 00322-20000-00000-AA575
Microsoft Visual C# 2015

です。
よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

eventに対して可能な操作は+=/-=のみですが、自分はそれを「その操作だけあればeventへの操作は十分であると言語仕様策定者が想定している」と解釈します。つまりeventへハンドラーを登録する契機はそのイベントを持つオブジェクトが活動を開始する以前のタイミングで一度だけ登録すれば十分であり、登録されていなければ登録するという操作は典型的な使い方では必要ないという判断なのだと思います。

ハンドラーが登録済みかどうかの判断機能は提供されてないので、必要ならば自分自身で管理するためのコードを書くことになろうかと思います。


多重にハンドラーを登録してしまうことを防ぐ管理コードの一例。

あくまで実装のイメージを示すだけのものです。本来は登録解除の機能やDisposeしたコントローラーに対して(WeakReferenceを使って)自動的に登録解除したりする仕組みを実装しないと使い物にならないと思います。下記の例では簡単のためTargetEventをstructにしてますがvalur type/classのどちらが適切かは最終実装によってかわるかもしれません。また当然ながらxamlで登録されたハンドラーについては対処のしようがないため、この仕組みを使うコントローラー(インスタンス)のイベントはxamlで定義しないことが前提になります(xamlで静的に記述したインスタンスのイベントは動的にハンドラーを追加することはないと思えるのでおそらく質問者さんが取り組んでおられるプログラムでは問題にならないと推測します)。

先にも書いたようにeventは+=/-=の左辺にしか使えず(他のメソッドの引数に渡すことさえできない)のでどうしてもこのような迂遠な実装になると思います。(リフレクションとunsafeコードを使うともう少しましにできるのかも知れませんが自分には可能かどうかわかりません)

c#

1public enum EventCode { 2 CLICKED, 3 ... 4} 5public static class HandlerManager { 6 private struct TargetEvent { 7 public Control TargetControl; 8 public EventCode EventCode; 9 } 10 11 private static Dictionary<TargetEvent, EventHandler> registory 12 = new Dictionary<TargetEvent, EventHandler>(); 13 14 public static bool RegisterIfAny(Control c, EventCode ec, EventHandler handler) { 15 TargetEvent te = new TargetEvent { TargetControl = c, EventCode = ec }; 16 EventHandler h; 17 if (registory.TryGetValue(te, out h)) { 18 return false; 19 } else { 20 registory.Add(te, handler); 21 return true; 22 } 23 } 24} 25 26// 上記を使ったハンドラー登録の例 27 28public partial class Form1 : Form { 29 public Form1() { 30 InitializeComponent(); 31 // 以下のように2回登録しても最初しか登録されない 32 if (HandlerManager.RegisterIfAny(button1, EventCode.CLICKED, button1_Click)) 33 button1.Click += button1_Click; 34 if (HandlerManager.RegisterIfAny(button1, EventCode.CLICKED, button1_Click)) 35 button1.Click += button1_Click; 36 } 37 } 38 ... 39}

投稿2016/11/07 09:07

編集2016/11/08 05:24
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cancat

2016/11/07 11:45

必要なのです。 どのように書くものでしょう?
KSwordOfHaste

2016/11/07 12:26

なぜ必要なのかその目的によって実装方法は変わるとおもいます。少なくとも上にかいたようにC#側でのサポートはありません。目的を教えてください。
cancat

2016/11/07 13:41

どういう目的によってどう実装が変わるのでしょう? よく目的目的と質問されることがありますが、目的は、上記のとおりで、動的にボタンを生成しイベントハンドラを動的にC#で割り当てたいということです。
KSwordOfHaste

2016/11/07 15:00

んー「上記のとおり」とおっしゃってますが、質問文や他のコメントを拝見しても自分には「動的にボタンを生成したい」という意図はくみ取れませんでした。それはともかく、動的にボタンを生成するとしてもなおハンドラー登録済みかどうか判断する必要を自分は感じません。動的にボタンを生成したタイミングで同時にハンドラーを登録すればよいと思うからです。 質問者さんは明確に目的を述べているつもりなのに私が目的は?と聞いたので少々イライラさせてしまったかもしれません。そうだとしたら失礼しました。失礼ながらやはり私には質問者さんがやりたいことがわからないようです。
cancat

2016/11/08 01:22

こちらこそすみません。目的というのは漠然としていて簡単に説明できることではなく、途方に暮れている、というほうが正しいかもしれません。 そもそも、動的に追加するときに多重に追加される、などということを、わたしも想像していたり、想定していたわけではないのです。 ただ実際のプロジェクトで動的にやっていたら多重に登録されてしまって、何度も同じ処理をするようになり、困っているので質問した、ということです。 実際のプロジェクトは大きい(.csファイルが500くらい)ので、質問用にごく簡単なソースを書きました。それを説明しきるのは困難で、トートロジーのような説明になりそうで、途方に暮れるわけです。 繰り返しになりますが、C#で動的にeventhandlerを割り当てる場合、+=だと多重に登録できるので、1回だけしか登録できないようにしたいです。
KSwordOfHaste

2016/11/08 04:57 編集

なるほど目的がわかりました。これから設計するならハンドラーの動的追加しないようにするのがよいと思ったのですが、問題の焦点はそこではなく「既にそうしてしまっているソースをいかに直すか」ということなのですね。わかってみるとなかなかに難物です。あまりすっきりした解決法はなさそうですが。うーむ・・・ ヒントになるかどうかわかりませんが管理コードのアイデアを追記してみました。
cancat

2016/11/08 07:28

ありがとうございます。 おっしゃるとおり、これから設計するなら、そういう挙動/仕様だとわかって作れるのです。 実におっしゃるとおりで、直面している課題は多重起動していて大変困っている、ということなのです。 アイデア、ありがとうございます。すぐに役立てられそうにないのですが、試行錯誤してみます。
KSwordOfHaste

2016/11/08 10:00

EventTargetにhandlerのメンバー入ってませんでした・・・うっかりしてました。
guest

0

質問文のコードだとButton_Clickが呼ばれている時点ですでに
button.ClickButton_Clickが登録されていると思うので、
こういうことでいいのか大変不安ですが、

if(button.Click == null) button.Click += Button_Click;

そういえばクラスの外側ですね。
すみません。

C#的に単純には無理です。
ある言語でやりづらいことは、
その言語的にはおかしいコードを組んでいるということだと思っているので、
可能ならコードをバッサリ投げ捨てますが、それが無理なら

一つしか登録できないボタンを継承して作るか
イベントの登録はボタンを生成するタイミングだけにするよう気をつけるか、
イベントを登録する側がイベントを追加したかを覚えておくかです。

投稿2016/11/07 05:45

編集2016/11/08 02:31
ozwk

総合スコア13512

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cancat

2016/11/07 05:49

単にnullを見るだけよいのですね!
cancat

2016/11/07 06:00

とおもったら、確認したところ、イベントButtonBase.Clickは+=または-=の左側にのみ配置できます、とエラーになります。
dn315

2016/11/07 06:10

単に2行書けばいいんじゃないでしょうか nullで"-="しても例外は発生しません button.Click -= Button_Click; button.Click += Button_Click;
cancat

2016/11/07 08:09

単に2行書くだけでは応用できないのでだめです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問