C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: イベントを一時的に無効にする方法


(過去ログ 112 を表示中)

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■66573 / inTopicNo.1)  イベントを一時的に無効にする方法
  
□投稿者/ たか (5回)-(2013/05/09(Thu) 12:34:42)

分類:[.NET 全般] 

C#でWindowsアプリを作ってます。

textChangedや selectedIndexChangedといったTextBoxやComboBoxのイベントを一時的に無効にしたいです。

というのは先日質問もしましたがフォームの内容をXMLに保持して次回起動時にその内容を反映する処理を書いてほぼうまく動いているのですが
次回起動時に値を復帰してもChangeイベントが発生して保持した値が変更されてしまうことがあるからです。

とりえあえずXMLから値を復帰する前に該当しそうなイベントを個別に削除して 値を読み込み反映後にイベントを再割り当てしています。
これでも動作は問題ないのですが 対象のイベントが結構多かったりで面倒ですし
またこういう運用ルールは今後の機能拡張を行っていく上でバグの元になりそうなので できるだけ自動化して今後もあまり意識せず使えるような設計で実装したいです。


個別に一つずつ行うのではなく自動で行いたいのですが どう書けばいいのでしょうか?
親フォームから子供Controlを再帰的に取得して その子供Controlごとにイベントの一覧を取得してどこかに保存しておき イベントを一括削除。
その後保存したイベント一覧を復帰したいのですが・・・

イベントの割り当てをまとめて行う関数などを用意していればよさそうですが
VSのデザイナー経由でイベントを色々割り当ててる現状だと難しそうですし 今後もその関数使わずにイベントを割り当ててしまうこともありそうでバグの元になりそうな仕様ではあまりしたくありません。


//影響あるイベントを個別に削除
textBoxaaa.TextChanged -= aaa_Changed;
textBoxbbb.TextChanged -= bbb_Changed;

//XMLから値を復活させる処理をいろいろ
〜〜〜

//影響あるイベントを個別に追加
textBoxaaa.TextChanged += aaa_Changed;
textBoxbbb.TextChanged += bbb_Changed;

よろしくお願いします。
引用返信 編集キー/
■66578 / inTopicNo.2)  Re[1]: イベントを一時的に無効にする方法
□投稿者/ shu (301回)-(2013/05/09(Thu) 14:25:38)
No66573 (たか さん) に返信

以下のような感じすればbool変数の値を変えるだけで希望の動作が実現出来ます。

bool変数を用意(仮にeventflgとする)
eventflg=falseに初期化

eventflg=true;
/*メイン処理*/
〜
eventflg=false;
※finallyで設定するようにした方が確実

各イベント(sender,e)
{
    if (eventflg) return;
    /* イベントメイン処理 */
}

引用返信 編集キー/
■66588 / inTopicNo.3)  Re[2]: イベントを一時的に無効にする方法
□投稿者/ Azulean (156回)-(2013/05/09(Thu) 22:55:11)
単純な処理ならすでに提案されているようにフラグでガードするだけでうまく回ると思います。
ただ、以下のようなケースがある場合はもっと検討しないといけないかもしれません。

・フラグが true のとき(shu さんの例で言うところの eventflg が true のとき)に、再度 true にするような流れになるとき。(循環・再帰など)
・ガードしているときにイベントが起きる条件を満たしていたら、フラグを下ろすときに通知を投げたり、画面を更新したりしないといけないとき。


変化判定が必要かどうかはわかりませんが、後者に相当する処理を実装していないと、表示の不整合が起きるかもしれません。
引用返信 編集キー/
■66603 / inTopicNo.4)  Re[2]: イベントを一時的に無効にする方法
□投稿者/ たか (6回)-(2013/05/10(Fri) 17:10:45)
回答ありがとうございます。

確かにフラグ制御、でもできるとは思いますが その場合既存のイベントに全て制御追加するだけでなく
今後追加するイベントにもフラグ制御を追記してやらねばならず そうすると絶対バグの元になるのであまりやりたくないです。
今はちゃんと覚えているからいいけれど半年とか1年立ったらそんなこと忘れてしまいそうですし
実際忘れて あ、、、ってなることよくあるので 今は大変でもできるだけ後に影響ないような実装にしたくて・・・

今後の機能拡張や意外と便利なので他のソフトへの移植も考えていますが ローカルな運用ルールあると使いにくいので・・・

イベントの内容をプログラムから取得して保存してあとで再割り当てするというのは.netframeworkの仕様的に不可能なのでしょうか?

引用返信 編集キー/
■66605 / inTopicNo.5)  Re[3]: イベントを一時的に無効にする方法
□投稿者/ shu (306回)-(2013/05/10(Fri) 17:43:12)
No66603 (たか さん) に返信

再三書きますが、この手のことを簡単に済まそうとしても無理です。

イベント管理クラスを作成するにしても関連するすべてのイベントに対し登録処理を行い
制御するようにしなければならず結局仕様を忘れてしまったら意味がありません。
あらゆるプログラムに対応させることは出来るわけがなく特定のパターンにしか使用
出来ないものとなるでしょう。
引用返信 編集キー/
■66607 / inTopicNo.6)  Re[1]: イベントを一時的に無効にする方法
□投稿者/ yukihiro (1回)-(2013/05/10(Fri) 18:39:25)
> イベントの一覧を取得してどこかに保存しておき イベントを一括削除。

リフレクションでできると思います。

イベントハンドラリストの取得例です。↓
How to get event handlers list using reflection
http://dkowalski.com/blog/archive/2009/12/22/how-to-get-event-handlers-list-using-reflection.aspx

引用返信 編集キー/
■66608 / inTopicNo.7)  Re[3]: イベントを一時的
□投稿者/ Azulean (158回)-(2013/05/10(Fri) 23:47:58)
2013/05/10(Fri) 23:52:56 編集(投稿者)

No66603 (たか さん) に返信
> 確かにフラグ制御、でもできるとは思いますが その場合既存のイベントに全て制御追加するだけでなく
> 今後追加するイベントにもフラグ制御を追記してやらねばならず そうすると絶対バグの元になるのであまりやりたくないです。
> 今はちゃんと覚えているからいいけれど半年とか1年立ったらそんなこと忘れてしまいそうですし
> 実際忘れて あ、、、ってなることよくあるので 今は大変でもできるだけ後に影響ないような実装にしたくて・・・

フラグ制御に頼らざる得ないような現状の設計・実装になってしまったのが敗因だと思います。
変更イベントが起きると言っても、内部で保持しているデータと一致するわけですから、比較して処理しないという選択ができたはずです。
あるいは、処理しても問題が起きないような設計・実装することもできたはずです。

今回のケースの場合、そういったことができていないので、残念ながらフラグ制御をせざる得なくなっています。
(そうはいっても、フラグ制御を使わずに実装する理想的な状況はコスト面やスキル面で難しいこともあるので、フラグ制御を受容するケースも多いと思います)


> 今後の機能拡張や意外と便利なので他のソフトへの移植も考えていますが ローカルな運用ルールあると使いにくいので・・・

そうであれば、「このスコープを実行中の間だけイベントを実行したくない」というようにならないように作るしかないかと思います。
実際、「このスコープを実行中の間だけイベントを実行したくない」としてすべてのイベントを止めることができたとしても、逆にイベントが起きないことによって困ることがあるかもしれません。対処療法的なやり方には、限界があります。


> イベントの内容をプログラムから取得して保存してあとで再割り当てするというのは.netframeworkの仕様的に不可能なのでしょうか?

イベントハンドラを収納するデリゲートは、原則としてそれを保持するクラスからしか見ることができません。(event キーワードの場合、保持するクラスでも見えません)
外から勝手に見えて、操作できたら、そのオブジェクトを壊せる、カプセル化できていないことになりますよね。

別の方の助言にもあるように、リフレクションで無理矢理取り出せばできるかもしれませんが、将来メンテナンスできないという意味では同じことかと思います。
リフレクションで書いたコードの意味を、1年後、2年後に理解してくれることは期待できませんので。


// 万能なものはあり得ませんので、今回の事例を踏み台にして、今後、同じような事態に陥らないような設計・実装を目指すしかありません。
引用返信 編集キー/
■66619 / inTopicNo.8)  Re[4]: イベントを一時的
□投稿者/ 渋木宏明 (28回)-(2013/05/11(Sat) 22:28:06)
渋木宏明 さんの Web サイト
> フラグ制御に頼らざる得ないような現状の設計・実装になってしまったのが敗因だと思います。

同感です。

「アプリケーションが保持するデータ」の保存・復帰を安直に各 UI オブジェクトの状態のシリアライズ/デシリアライズに頼っているのもイマイチです。

「安直な手法に頼りすぎたためしわ寄せが出始めた」のが現状と思います。

小規模なアプリであれば問題なくても、アプリケーションが高度化するのに応じて考え方を変えていかないとダメな場合は多々あります。

引用返信 編集キー/
■66671 / inTopicNo.9)  Re[2]: イベントを一時的に無効にする方法
□投稿者/ たか (7回)-(2013/05/14(Tue) 18:12:11)
皆さん回答ありがとうございました。

>yukihiroさん
ありがとうございます。この回答を探してました。
Control関係見ていてもイベントの取得方法がわからず手をつけられずにいました。
リフレクションはあまり好きじゃないですが他に方法がないようですので こちらを使って試してみたいと思います。
ありがとうございました!


>shuさん
今の実装が大変でも 後々の実装が楽になればと思ってます。
あと少し語弊がありましたが一般公開する物でもないのでよく使うイベントに対してのみ実装すれば十分だと思ってます。
実装していないイベントがあったらエラーを出す用にしておき エラーが出たらそのとき追加実装するとかで・・・

やってみてエラーが出るのであれば対処できますが 上のフラグの方法などコンパイルも通ってるけどなぜか想定通りの動作をしない
という問題は潜在バグで残り続ける可能性が高いですし 運用ルールに頼っている方法ですと慣れた人以外使えないシステムになってしまいますし。

> Azuleanさん
> 渋木宏明さん
> フラグ制御に頼らざる得ないような現状の設計・実装になってしまったのが敗因だと思います。

では逆に一般的にはどうやってフォームの値保持をしているのでしょう?

よく言われるように個別に1つずつPropertyに保存してと当初はやっていましたが 保存対象が100件近くになってきて管理できなくなってきたため
今回のようにFormコントロールから再帰的に保存する実装をしたわけです。
ProperyではなくXMLやSQLITEなどに保存すれば管理という面では少し楽になるかもしれませんが それでも100件あれば100件保存する処理と
値を戻す処理を書かないといけませんしあまり楽とは言えません。
実際自分が 前バージョンの段階で50件以上個別に保存して、復帰させて、、、という処理を書いていましたがかなり大変で
テストの際に あ、ここは保存してない、とか復帰の処理が抜けてる、というようなバグもよくあり
小規模な値保持にはいいですが 大量のデータ保持には向いている実装方法ではないです。

もしもっといい実装方法・設計があればぜひ教えてください。
今のまとめて再帰的に保存・復帰の実装も数時間で作ったものですので もしより良い方法があれば乗り換えたいですので。


解決済みにしますがもしいい案あったらお願いします。
解決済み
引用返信 編集キー/
■66672 / inTopicNo.10)  Re[3]: イベントを一時的に無効にする方法
□投稿者/ たか (8回)-(2013/05/14(Tue) 19:45:24)
無事リフレクション使ってイベントの取得、削除、 別処理をさせてからのイベント復活とサンプル上で実装できました。
あとはこれを再帰的に実行して保存・復活できるようなクラスにしてまとめますが まぁあとは力業で今日中にはなんとかなると思います。
ありがとうございました。

ちなみに全イベントについて網羅できる書き方なので .netframeworkの内部仕様が変わらない限り修正は必要なさそうです。
内部仕様の変更は怖いところですが まぁリフレクション使ってる人いっぱいいるから変更したらあちこちから文句出るだろうし
やるとしてもメジャーバージョンアップだろうでだろうから 既存ソフトには影響出ないでしょう。

解決済み
引用返信 編集キー/
■66674 / inTopicNo.11)  Re[3]: イベントを一時的に無効にする方法
□投稿者/ Azulean (162回)-(2013/05/14(Tue) 23:12:40)
No66671 (たか さん) に返信
> では逆に一般的にはどうやってフォームの値保持をしているのでしょう?

一般的かどうかは別として、理想的には View のデータをマスターにしないということでしょうか。

今回の事例は View、つまり、コントロールのデータをマスターデータとしてしまっているため、そこにプログラムから設定する流れと、ユーザーが入力する流れを区別できなくなっており、これによって問題が生じていると予想しています。
本来、これらは明確に区別できるのが理想であり、ユーザーの入力に対して何らかの処理を実行することが必要なのだと考えています。
View 以外にマスターがあれば、View の変化はマスターと変化しているかを照らすことで、少なくとも区別可能なはずです。


> よく言われるように個別に1つずつPropertyに保存してと当初はやっていましたが 保存対象が100件近くになってきて管理できなくなってきたため
(略)
> 小規模な値保持にはいいですが 大量のデータ保持には向いている実装方法ではないです。

あなたが抱えている設計・実装の課題については正直言及しかねます。

現状の構造が適正なのか、あなたが抱える要件や環境が見えない以上は検証できませんし、それを提示してもらって検証するとしても量が多いので”コンサルタントの仕事”と言える状況になってしまうため、手に負えません。
(少なくとも回答側に益がないです…。せめて、数個レベルでの実証サンプルで議論できる状況であれば、「理想の設計・実装とは」を議論する・結果を享受できるという意味で参加者に益はあると思います)


先にも書きましたが、理想は理想です。
あなたが置かれている現状(要件・環境・既存成果物・経験・スキル・人的リソース・スケジュールなど)を踏まえて判断した(手間に対して効果が見合わないなど)結果、今の設計・実装が最適となったのであれば、フラグ管理なり、リフレクションなりに、その副作用として受容するしかありません。
解決済み
引用返信 編集キー/
■66675 / inTopicNo.12)  Re[4]: イベントを一時的に無効にする方法
□投稿者/ 渋木宏明 (29回)-(2013/05/15(Wed) 03:19:03)
渋木宏明 さんの Web サイト
>では逆に一般的にはどうやってフォームの値保持をしているのでしょう?

まず最初に処理の対象となるデータ構造があるはずで、データの保存と復帰はその構造をベースに考えるべきです。

「表示しているモノを保存・復帰する」という戦略では、表示方法を変更した場合に保存データの互換性を保つことが困難です。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -