2009-06-11 11 views
3

背景:私は、WPFとC#(3.5)を使用していますし、ユーザーが既にコンパイル済みのアセンブリの一部だ形/ウィンドウ/ユーザーコントロールを表示することができますアプリに取り組んでいます。彼らはそれを見ると、コントロール(ボタン、テキストボックス、ラベルさえ)をクリックすることができるはずです。小さなポップアップエディタがコントロールによって表示され、そのコントロールのツールチップ、helpIDなどを入力する必要があります。反射によるルーティングイベントハンドラの削除?

長いとそれの短い:私は、WPFでの基本的なデザインビューを模倣する必要があります。これは、私は、少なくとも次の操作を行う必要があることを意味:

  • 特定のアセンブリ(問題なし)からのUserControl /ウィンドウをロード
  • ユーザーコントロール/ウィンドウ(問題なし)、それをインスタンス化
  • そのすべてのコントロールのをすべてクリア加入のEventHandler
  • 各制御に私自身の「ShowEditorPopup」のEventHandlerを割り当てます(問題になることはありません)

まず第一に誰が取るより簡単またはより良いルートの提案を持っている場合、私に知らせてください。 (明らかに、私は.NET 2が持っているように、WPFのためのDesignHostの種類のコンポーネントはありません)。

私は太字の項目に固執しています - 購読しているEventHandlersをクリアしています。いくつかの掘り起こしとリフレクターに入ると、私はこのクールな危険なコードを思いつきました(ここでは、XAMLで定義されているsomeButtonという単一のButtonのすべてのEventHandlerを取得しようとしています)。

<Button Name="someButton" Click="someButton_Click"/> 
ここで

は(あなたがしたい場合は、someButton_ClickなeventHandlerからそれを実行することができます)コードです:

public void SomeFunction() 
{ 
// Get the control's Type 
Type someButtonType = ((UIElement)someButton).GetType(); 

// Dig out the undocumented (yes, I know, it's risky) EventHandlerStore 
// from the control's Type 
PropertyInfo EventHandlersStoreType = 
     someButtonType.GetProperty("EventHandlersStore", 
     BindingFlags.Instance | BindingFlags.NonPublic); 

// Get the actual "value" of the store, not just the reflected PropertyInfo 
Object EventHandlersStore = EventHandlersStoreType.GetValue(someButton, null); 

// Get the store's type ... 
Type storeType = EventHandlersStore.GetType(); 

// ... so we can pull out the store's public method GetRoutedEventHandlers 
MethodInfo GetEventHandlers = 
     storeType.GetMethod("GetRoutedEventHandlers", 
     BindingFlags.Instance | BindingFlags.Public); 

// Reflector shows us that the method's sig is this: 
// public RoutedEventHandlerInfo[] GetRoutedEventHandlers(RoutedEvent routedEvent); 

// So set up the RoutedEvent param 
object[] Params = new object[] { ButtonBase.ClickEvent as RoutedEvent }; 
// I've also seen this for the param, but doesn't seem to make a difference: 
// object[] Params = new object[] { someButton.ClickEvent }; 

// And invoke it ... and watch it crash! 
GetEventHandlers.Invoke(someButton, Params); 
} 

それを返す呼び出し、最大動作します:オブジェクトがターゲット・タイプ(つまり、私のparamsや目標と一致していませんオブジェクトが混乱している)。

私はGetEventHandlers METHODINFOに時計を設定
GetEventHandlers.Invoke(Activator.CreateInstance(someButton.GetType()), Params); 
// Also doesn't work... 

、それは偉大に見える、それはちょうど私が呼び出しを呼び出すとき、私はそれを渡しているものを好きではない:私はあなたがこの問題を解決することができます発見しました。

RoutedEventハンドラのリストを取得する方法の最後のステップであるようです(明らかにWPF RoutedEventsでは正常に動作しないGetInvocationList()など)。そこから、各コントロールからこれらのハンドラを削除し、無事にフォームを作成するだけで、自分のイベントを追加することができます。

手がかりはありますか?繰り返しますが、タスク全体をより簡単に行う方法があれば、私に教えてください:)

+0

を、私はちょうど同じことをしようとしていた、素晴らしい仕事をしました。あなたは私に必要な出発点を与えました。 – orellabac

答えて

3

別のアプローチをとるとどうなりますか?すべてのイベントに対してEventManager.RegisterClassHandler()に電話をかけてから、ハンドラーで(イベントがデザインサーフェイスのコントロール用であり、UIの一部ではないと仮定して)、イベントを処理済みとしてマークします。クラスハンドラは標準イベントハンドラの前に呼び出されるため、これによりデザインサーフェス上のコントロールに転送されないようにする必要があります。

さらに、コントロールで提供されるイベントのリストを取得するには、まだリフレクションを使用する必要がありますが、少なくともリフレクションを使用してイベントを削除しないでください。また、読み込んだアセンブリでクラスハンドラが登録されている場合(コードの前にある可能性があります)、最初に呼び出されますが、これはまれなことです。

+0

素晴らしい提案。私はHandled = trueを設定することで他のイベントが発生するのをブロックすることが可能かどうか疑問に思っていましたが、 "Class Handlers"が最初に起動されることはわかりませんでした。 私はそれを試してみましょう。ありがとう! – Eddie

+0

純粋な天才!関連する例についてはhttp://www.switchonthecode.com/tutorials/class-event-handlersを見て、私のボタンはもう何もしません。正確に私が必要としたもの。ありがとう!!今では、単純なButton.ClickEventのほかにいくつのRoutedEventsをブロックすべきかと思っています。おそらく、最も安全なのは、EventManager.GetRoutedEvents()から返されたすべてのRoutedEventを「前処理」することでしょう。しかし、それは別の問題です:) もう一度ありがとう! – Eddie

+0

そして、潜在的に問題を引き起こす可能性のある場所で言及した特殊なケースは、私たちの場合は問題ではないはずです。あなたが私に与えてくれたことは、その状況にぴったりです。 – Eddie

3

GetEventHandlers.Invoke(EventHandlersStore , Params)を使用するとうまくいくように見え、クラッシュしません。私は上から

0

あなたのコードを使用してこの:

// Get the control's Type 
Type controlViewType = ((UIElement)control).GetType(); 

// Dig out the undocumented (yes, I know, it's risky) EventHandlerStore 
// from the control's Type 
PropertyInfo EventHandlersStoreType = 
controlViewType.GetProperty("EventHandlersStore", 
BindingFlags.Instance | BindingFlags.NonPublic); 

// Get the actual "value" of the store, not just the reflected PropertyInfo 
Object EventHandlersStore = EventHandlersStoreType.GetValue(tree, null); 
var miGetRoutedEventHandlers 
EventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", 
BindingFlags.Public | BindingFlags.Instance); 
RoutedEventHandlerInfo[] res = 
(RoutedEventHandlerInfo[])miGetRoutedEventHandlers.Invoke(EventHandlersStore, 
new object[] { CheckedTreeViewItem.CheckedEvent }); 

あなたは、唯一の問題は、あなたがいることを実装するオブジェクトにインスタンスを取得する必要があるので、あなたが今、メソッド情報を持っていることであるとしたら方法。通常、イベントハンドラはWindowオブジェクトまたはPageオブジェクトで定義されます。だからそれを得る: var parent = VisualTreeHelper.GetParent(control); (!(制御ウィンドウである)& &(制御ページである)) 一方 {親= VisualTreeHelper.GetParent(親)。 }

そして、そのインスタンスにあなたがして、イベントwtih呼び出すことができます:うわー

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }