• 締切済み

実行時にコントロールのイベントの登録/解除を、イベントハンドラのメソッドをあらかじめ知っていないロジックから実施する方法

実行時にコントロールのイベントの登録/解除を、イベントハンドラのメソッドをあらかじめ知っていないロジックから実施する方法 通常、実行時にコントロールにイベントの登録/解除 を行う際は。 AddHandler 対象コントロール.イベント名, AddressOf メソッド RemoveHandler 対象コントロール.イベント名, AddressOf メソッド や AddHandler 対象コントロール.イベント名, メソッドのデリゲート型 RemoveHandler 対象コントロール.イベント名, メソッドのデリゲート型 を行うと思います。 しかし、この方法では、これを実施するロジックが AddressOf メソッド や メソッドのデリゲート型 を知っていなければなりません。 そうではなく、それをあらかじめ、知っていなくても 対象コントロール から AddressOf メソッド や メソッドのデリゲート型 に該当するオブジェクトを取得するなどして、 実施できる方法を教えてください。 言語は、.NET系であれば。。 VB.NET, C#, C++のいづれかでもよいです。 できさえすればよいです。 J#とか、Delphi.NETとかマイナーなのは、厳しいです。 なお、C++でもしできるのであれば。 2003のマネージC++と、 2005のC++/CLIの両方について知りたいです。 以上、よろしくお願いします。

みんなの回答

回答No.4

う~ん。何だか厳しいような気がしてきました・・・ このソースの全容を理解しないままの回答なので、間違いがあるかもしれませんので、疑いながら読んでください。 EventHandlerListについて、MSDNライブラリを読んでみると、 ( http://msdn2.microsoft.com/ja-JP/library/8843a9ch.aspx ) イベントを大量にもっているクラスの場合、ハンドルされない可能性の高いイベントをひとつずつ変数で管理するのはメモリの無駄だから用意されている機構のようで、これのキーになっているオブジェクトは一意でさえあれば何でも良いように見えますね・・・ それで、このキーを特定するのが最初のソースの private static FieldInfo GetEventIDField(Type objType, string eventName) の中身だと思うんですが、取得するフィールド名を "Event"+eventName と決め打ちしてしまっているので、.Net Frameworkに標準のクラスはこの命名規則に従っていそうな雰囲気ではありますが、これを守る義務のないサードパーティ製のものだとダメですよね、多分。 時間を無駄にさせてしまいまして申し訳ありませんが、どうも何か別な手段が必要そうです・・・

回答No.3

試していませんが、おっしゃる内容に近いのではないかというをやっているコードがあります。 http://www.technewsgroups.net/group/microsoft.public.dotnet.general/topic4196.aspx 中身を良く読んでいないので、これでうまくいくかわかりませんし、サードパーティ製のコンポーネントの場合、同じ方法が使えるのかも不明です。 ひょっとすると、System.Reflection名前空間を調べてみると良い方法が見つかるかもしれませんよ。

参考URL:
http://www.technewsgroups.net/group/microsoft.public.dotnet.general/topic4196.aspx
lawson
質問者

お礼

回答ありがとうございます。さっそく、 ↑のサイトのソースを参考にして、 EventDescriptorCollection events = TypeDescriptor.GetEvents(myComponent); foreach (System.ComponentModel.EventDescriptor myEvent in events) { のループの中で EventDescriptorのNameプロパティと 自分が現在、注目しているイベントの名前 ("Click"などの文字列)とを比較すると、 自分が注目している該当イベントの EventHandlerListオブジェクトジェクトの取得まで できるところまでこぎつけました。 EventHandlerListのVBでいうところのデフォルトプロパティとして定義されたitemプロパティ、C#でいうところのにインデクサに相当するものは、 object keyを引数にとり、 System.Delegateを取得できるようです。 なので、このSystem.Delegateを取得できれば、 AddHandlerやRemoveHandlerのカンマの後ろにくる 披演算子として使えます。 ところが、このobject keyに何を与えても Nothingしか帰って来ない、いったい何をキーとして 要求するかの仕様がいまいちわからないです。 メソッド名や、イベント名やクラス名などおもいあたるものをいろいろ与えて試してもNothingしかかえってこないです。 しょうがないのでEventHandlerListオブジェクトそのものをforeach構文のinの後ろにもっていって、 System.Delegate型で取得しながら、ループをまわして、AddHandlerなり、RemoveHandlerをやってやろうと おもいましたが、EventHandlerListオブジェクトが コレクションではないという理由でコンパイルエラーになってしまいました。 ○○Listって名前からして、いけそうに見えたのですが残念です。 EventHandlerListオブジェクトのヘルプを見てみましたが、インデクサのobject keyになにを入れたら、 System.Delegate型を取得できるか?の仕様や? System.Delegate型のコレクションをあらわす、プロパティやそれを取得できるメソッドといったメンバが見当たらず。 お手上げになってしまいました。・・・・・

回答No.2

お世話になります。 AddHandler 及び RemoveHandler で追加したり削除したりする対象のイベントを 知らないでやる事は出来ないと思います。 私も前に、サードパーティ製品を使って どうにもそのコントロールの挙動が 望むものと違ったとき、 そのコントロールを継承したクラスを作って 挙動を変えたいイベントを突き詰めて オーバーライドして逃げた記憶があります。 > とりあえずは、グローバル領域にフラグをたてて、 > メソッドの先頭で、フラグをたてて、後処理でおろす この方法と似ていますが、 その自身で拡張したクラスに独自プロパティを設けて そのプロパティの値を見て オーバーライドしたイベントハンドラ内で 何かする、という方法です。

回答No.1

お世話になります。 > そうではなく、それをあらかじめ、知っていなくても > 対象コントロール から > AddressOf メソッド > や > メソッドのデリゲート型 > に該当するオブジェクトを取得するなどして、 > 実施できる方法を教えてください。 ? それだったら、普通に、そのオブジェクトの振る舞いとして、 AddressOf 等で定義付けるのではなくて 初めから実装しておけばよいのでは? その該当クラスの該当メソッドが 外部クラスから操作することありきでやるのであれば インターフェースや抽象クラスを使った上で そのような実装を行うような設計を した方がよさそうな感じはしますけれども。

lawson
質問者

補足

回答ありがとうございます。 説明不足でしたので補足します。 >その該当クラスの該当メソッドが >外部クラスから操作することありきでやるのであれば >インターフェースや抽象クラスを使った上で >そのような実装を行うような設計を >した方がよさそうな感じはしますけれども イベントの設定/解除を行いたい対象オブジェクトが 自作クラスのオブジェクトであればもちろんそのように致します。 外部のサードパーティー製のユーザコントロール(DLLとNDoc提供のみ)のオブジェクトを使っている部分のプログラムのお話でして、そのユーザコントロール のあるプロパティ値を変更した時、意図しないイベントが走ってしまいます。 通常は、ユーザが画面操作を行った時に変更される プロパティなのですが、その際には、イベント処理が走ります。 問題は、プログラムからそのプロパティを変更した時にイベントが走ってしまう。 のを回避したかったんです。 実は、現在開発しているシステムの一部は、 多言語(.NET系ではない)で作られた旧システムの ソースをコンバートすることで作ってる部分があります。 旧システムで使ってるコントロールと近いサードパーティー製のユーザコントロールを使っていたのですが。 旧システムの言語仕様では、↑のプロパティに該当するプロパティをプログラムから変更した時に、 イベントが発生しない仕様になっています。 旧システムのコントロールを使い勝手と、 新システムのコントロールの使い勝手が異なるため、 その違いを吸収したり、言語間の仕様の違いを吸収するために、プロパティの値の設定や、解除に対応する ユーティリティメソッドを作って、その中で、 出てきた問題などを吸収して、開発をすすめていました。 そして、今回のイベントが発生する/しないの 問題をそのメソッドの中で吸収したいのですが そのメソッドの中には、コントロールと、設定値 の2つの引数しかとるようになっていないです。 実際に該当する画面で、該当インスタンスに関連づいたコントロールのメソッドを知ることができないんです。 じゃー、そのメソッドがそれを引数にとればいいじゃないですか? といわれそうですが。。。 プログラムのコードからその種類のプロパティを 設定/解除しているコードは大量にあります。 また、今後も似たような問題はでるかもしれないです。 メソッドの中は、 イベントの解除をおこない、 プロパティ値の設定をおこない、 イベントをまた、元に戻す。 ようにしたいです。 ある程度のモジュール数を作らないと、動作ができない物理的な事情や、工数の問題から旧システムからの移行部分に関してはこのような手法で、作ってます。 なので、既に、この問題を解決しなければならない多くのコードに、対して、引数追加はせず、完全にイベントハンドラのメソッドを知らないロジックから できる方法が知りたいんです。 とりあえずは、グローバル領域にフラグをたてて、 メソッドの先頭で、フラグをたてて、後処理でおろす イベントハンドラ側の先頭ではフラグがたっていれば なにもしない というロジックをいれることで回避できるみたいです。 プログラムのコードからプロパティ値を設定している コードよりも、イベントハンドラの定義の数のほうが 少ないのでその方法で逃げれそうですが。 本当は、それもしたくないんです。 そういう経緯があって今回の質問をしました。

関連するQ&A

  • 実行時に確定するメソッドを呼び出す方法について

    invoke()メソッドや、Methodクラスを 使用するなど。以前に小耳にはさんだ。記憶があります。 以下の3つの引数から 動的に該当メソッドを呼び出す。 処理を実装コードがすぐだせる方がいると 助かります。 Object foo(Object obj, String methodName, Collection param) (1)該当オブジェクト getClass()などで、該当クラスを動的に  特定。 (2)、(1)が保有しているメソッドのうち、 発行したいメソッドの名前 (3) parmには1要素ごとに2要素のObject[]型を   を格納して、 Object[0]の要素にかんしてはが引数の型についての完全修飾クラス名をあらわすString型。   Object[1]については実際に入っている値。 上記3つの引数をうけとって内部で getClass()とかつかって 必要な情報はすべて実行時に動的に 取得して objの該当オブジェクトを 発行し、foo()メソッド自体の 返却値はそのメソッドの返却値とする という ロジックの実装のサンプルがあると。 とても、うれしいです。 じゃ、void型が返却の時どうすんねん。 とか、staticの時どうすんねん。 とか、その変のアイデア持ってる 人がいるととてもうれしい。 以上

    • ベストアンサー
    • Java
  • VBAでイベント発生時に自分の呼ばれたコマンドを知る方法

    イベントが発生したときに自分がどのオブジェクトから呼ばれているイベントか知る方法ってありますか?あるいは上から何番目のコマンド(縦に同じコマンドがズラッと並んでいるイメージ)から呼ばれているかをイベント内部から知る方法ってありますか? 今コーディングで新規フォームを作ってそこにレコード数だけコントロールを配置するというコーディングを考えております。 そのコマンドにクリックイベントも発生させたいので、コントロールを作成してイベントを埋め込むという作業まではできたのですが、不特定多数のコマンドボタンの作成設置方法に悩んでおります。 VBAではコントロールの配列はできないので、コントロールの名前などを全て変えて作らなければなりません。そこをどうするかで頭を悩ませています。 しかも後でどのコントロールがクリックされて呼ばれたのかも知りたいので、コントロール配列のindex番号みたいなものを取得しておく必要もありますが、何か良い方法はないでしょうか? いっそのことコマンドの名前を数字にして、後でそのコントロール名とかオブジェクトのメソッドを参照してインデックス番号を取得・・・みたいなことを考えてたのですが、たしか既存の(目に見えて形となっている)(オブジェクト名自体を持っている)コントロールのメソッドを使用するのはかなり不可能だったような気がするので、タグやオブジェクト名も拾えない気がします。 VBなら同じイベント内容のコマンドを全て配列にしてまとめてクリックイベントにしてそのIndexを見ればプロシージャ1つで済みますが、VBAで似たような方法をする方法を教えてください。 ループで回しながらオブジェクト名の違うコントロールを配置する方法は自分でなんとかやってみますのでindex番号の取得の方法だけお願いします。

  • VB6でClickイベントを一時的に削除したい

    お世話になります VB6で二つのコンボックス内でお互いのリストを編集する処理を作りたいのですが、 イベントを止めないと、編集時に相手のコンボックスにClickイベントが発生して無限ループになってしまいます。 そこでRemoveHandler、AddHandlerで一時的にイベントを停止させたいのですが、 今度は「メソッドまたはデータメンバが見つかりません。」のエラーが出てしまいます。 ※Me.を付けても同じ結果でした どのように記述したらClickイベントを止められますか? Private Sub Combo1_Click() RemoveHandler Combo2.Click, AddressOf Combo2_Click ここでCombo2の編集 AddHandler Combo2.Click, AddressOf Combo2_Click End Sub Private Sub Combo2_Click() RemoveHandler Combo1.Click, AddressOf Combo1_Click ここでCombo1の編集 AddHandler Combo1.Click, AddressOf Combo1_Click End Sub

  • デリゲートとイベントの関係(VB.NET2005)

    VB2005の本に書いてある内容が理解できません。 誰か助けてください! ---------- イベントとデリゲートの仕組み  .NETイベントが内部ではデリゲートとして処理されることを理解すれば、その威力を存分に引き出すことができます。 もう少し厳密に言うと、イベントを発行するクラスには、そのイベントをサブスクライブするクライアントすべてをポイントする、プライベートデリゲートフィールドが定義されています。 イベントが発行されると、.NETのイベントインフラストラクチャによって該当するデリゲートフィールドのInvokeメソッドが呼び出されます。そして、Invokeメソッドからすべてのクライアントにイベントが通知されます。  このことは、.NET Frameworkで定義されているイベントごとに対応するデリゲートが存在することからもうかがい知ることができます。 [オブジェクトブラウザ]を使用すれば、それを確認できます。 たとえば、第2引数としてEventArgsオブジェクトを受け取るイベントはすべて、内部ではSystem.EventHandlerデリゲートを通じて管理されます。 ほとんどのWindowsフォームコントロールが公開するKeyPressイベントは、KeyPressEventHandlerデリゲートによって処理されますが、このデリゲートは、第1引数としてObject、第2引数としてKeyPressEventArgsオブジェクトを受け取るSubプロシージャをポイントします。 ---------- 全体的にちんぷんかんぷんですが特に「サブスクライブ」などがわかりません よろしくお願いします。

  • VB2010でイベントハンドラの動的登録

    VB2010で目標はフォーム上で作成したLINESHAPEをクリックして消去する事です 下記のようにコードを書いてみました Imports Microsoft.VisualBasic.PowerPacks Public Class Form1 Private px1 As Integer = 0 Private px2 As Integer = 0 Private py1 As Integer = 0 Private py2 As Integer = 0 Private curv As LineShape Private a As Boolean = True Private flg As Boolean = True Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load // 座標と始点・終点フラグの初期化 px1 = 0 px2 = 0 py1 = 0 py2 = 0 a = True End Sub Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click     // SHAPECONTAINER の用意 Dim canvasx As New ShapeContainer    // LINESHAPE の用意 Dim ls1 As New LineShape      // LineShape インスタンス設定 canvasx.Parent = Me ls1.Name = "curv" ls1.Parent = canvasx ls1.BorderColor = Color.Lime ls1.SelectionColor = Color.Green ls1.X1 = px1 ls1.X2 = px2 ls1.Y1 = py1 ls1.Y2 = py2    // ShapeContainer のコントロールコレクションへの追加 Me.Controls.Add(canvasx) End Sub Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click     // 座標と始点・終点フラグの初期化 px1 = 0 px2 = 0 py1 = 0 py2 = 0 a = True End Sub Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click //CURV_CLiCKのイベントの登録 AddHandler curv.Click, AddressOf curv_Click End Sub Private Sub Form1_Click(sender As System.Object, e As System.EventArgs) Handles MyBase.Click     // Lineの座標を拾う Dim Pos As Point = Me.PointToClient(Windows.Forms.Cursor.Position) If a = True Then px1 = Pos.X py1 = Pos.Y a = False Else px2 = Pos.X py2 = Pos.Y End If End Sub Private Sub curv_Click(sender As System.Object, e As System.EventArgs) // クリックされたLineShapeの消去のつもり、テスト未完 For Each obj In Me.Controls If obj = sender Then obj = Nothing End If Me.Refresh() Next End Sub End Class 書かれた直線をクリックしても何も起きません。LineShape は確かにハイライト色に変化するのですが、イベントが発生しません なにとぞご教授を

  • イベントハンドラメソッドが実行される?

    環境 Flash8 / FlashPlayer8 lis.onLoadInitが何故実行されるのか理解できないので 教えてください。 私の考えは、lisはローカル変数なので、startLoadの実行が終わると lisが破棄されるので、list.onLoadInitが実行されないと思ったのですが list.onLoadInitが実行されてます。 *サンプルコード function startLoad():Void { var container:MovieClip = createEmptyMovieClip("container", getNextHighestDepth()); var mcLoader:MovieClipLoader = new MovieClipLoader(); var lis = new Object(); lis.onLoadInit = function() { trace(1); } mcLoader.addListener(lis); mcLoader.loadClip("***", container); } startLoad();

    • ベストアンサー
    • Flash
  • c# イベント用デリゲートの引数

     こんにちは。c#初心者です。  今回はイベントに用いるためのデリゲートの引数に関する質問です。  通常、「System.Windows.Forms.Control」のイベントで用いられているデリゲートの引数は全て  void SampleHandler(object sender, SampleEventArgs e)  のようになっていますが、コントロール以外のクラス(System.Windows.Forms.Controlから派生しないクラス)、例えばコレクションクラスのようなものでも同じように「EventHandler<T>」デリゲートを使わなければならないのでしょうか?  それともう一つ質問です。コントロールクラスでのイベントの引数は  SampleHandler(object sender, …  のようにせずに  SampleHandler(SampleControl sender, …   としてはいけないのでしょうか? (いけないと思いますが…)  質問は以上の2点です。どなたか詳しい方がいらっしゃいましたら教えていただけませんか?

  • フォーム上の任意のコントロール上でのイベントをまとめてハンドリングする方法

    .NETにおいて、フォーム上に配置されたコントロールで発生したイベントを、各コントロールごとではなくて、まとめてハンドルする方法はないでしょうか。 例えば次のようなフォームがあります。 (Button1, Button2, Button3は、フォームに配置されたボタンとします) Public Class MyForm   Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click, Button2.Click, Button3.Click   End Sub End Class ボタンが3つ程度の場合はこれで良いと思うのですが、例えば(極端に言えば)ボタンが100個ある場合、 Handles Button1.Click, Button2.Click, (途中省略.....) Button100.Click と書くしかないのでしょうか。 後で対象コントロールが増えたり減ったりした場合に間違いが起こりそうなので、このような書き方は避けたいです。 フォーム上のどのコントロールでも構わないからとにかくクリックイベントが発生したことを知るための方法はないでしょうか。 例えば次のようなかんじでハンドリングできないでしょうか。 Private Sub All_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles フォーム上の任意のコントロールでのClickイベント End Sub (環境はVB.NET2005です)

  • イベントハンドラメソッドに関して教えてください

    お世話になります。イベントハンドラメソッドについて教えてください。 -----JS----- function f() { window.alert("Click"); } ---(1) document.myform.mybtn2 = function(){ ---(2) window.alert("Click"); } -----HTML----- <form id="myform" name="myform"> <input type="button" id="mybtn1" name="mybtn1" value="Click" onclick="f()" /> ---(1) <input type="button" id="mybtn2" name="mybtn2" value="Click" /> ---(2) </form> Q:上記のコードで(1)の場合だと正常にalert画面が出ますが、(2)の場合だと何も起こりません。 FirefoxのJavaScriptコンソールで調べると下記のエラーが表示されます。 documnet.myform has no propaties なぜエラーが発生するのか分からずに困っています。 解決の手がかりでもよいので教えてください。

  • Excel・VBA Openメソッド実行時にファイル名を取得出来ません。

    いつもお世話になっております。 ネットワークコンピュータ上(共有フォルダにパスワードを設定済み)の Excelブックに対してWorkbooks.Open パス名 & ファイル名を実行すると 下記の実行時エラーが出てしまい開く事が出来ません。 パスワード無しの参照のみ共有にすれば開く事が出来るのですが Openメソッド実行時、または実行前に共有フォルダのパスワードをセットする 方法は無いでしょうか?自分が知る限りではExcelブックのパスワードの設定 はOpenメソッドのオプションに有るようなのですが共有フォルダに関しては 見つける事が出来ませんでした。 OS:Win98,95 Excel:97 ネットワークはTCP/IPを使用しています。 ご存知の方、どうか宜しくお願いします。 エラー内容  実行時エラー'1004':  'パス名\ファイル名.xls'が見つかりません。ファイル名および  ファイルの保存場所が正しいかどうか確認して下さい。  [ファイル]メニューの最近使用したファイルの一覧からファイルを開こうとしている場合は、  そのファイルの名前が変更されていないこと、移動または削除されていないことを  確認して下さい。

専門家に質問してみよう