5

背景:私はWPFとC#(3.5)を使用しており、コンパイル済みアセンブリの一部であるフォーム/ウィンドウ/ユーザーコントロールをユーザーが表示できるようにするアプリに取り組んでいます。表示すると、任意のコントロール(ボタン、テキストボックス、さらにはラベル)をクリックできるはずです。コントロールの横に小さなポップアップエディタが表示され、そのコントロールのツールチップやhelpIDなどを入力できます。

その長所と短所:WPFの基本的なデザインビューを模倣する必要があります。つまり、少なくとも次のことを行う必要があります。

  • 特定のアセンブリからユーザーコントロール/ウィンドウをロードします(問題ありません)
  • ユーザーコントロール/ウィンドウをインスタンス化します(問題ありません)
  • すべてのコントロールのサブスクライブされたすべてのEventHandlerをクリアします
  • 自分の「ShowEditorPopup」EventHandlerを各コントロールに割り当てます(問題はないはずです)

まず、誰かがより簡単またはより良いルートについて提案があれば、私に知らせてください。(どうやら、WPF用のDesignHostの種類のコンポーネントはないので(.NET 2を読んだように)、それはありません。)

私は太字の項目で立ち往生しています-サブスクライブされたEventHandlerをクリアします。いくつかを掘り下げてReflectorに入った後、危険なコードのこのクールなチャンクを思いつきました(ここでは、XAMLで定義されたsomeButtonと呼ばれる単一のボタンのすべての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);
}

Invokeまで機能​​し、次を返します。オブジェクトがターゲットタイプと一致しません(つまり、パラメータまたはターゲットオブジェクトが混乱しています)。私はあなたがこれを解決できることを発見しました:

GetEventHandlers.Invoke(Activator.CreateInstance(someButton.GetType()), Params);
// Also doesn't work...

GetEventHandlers MethodInfoにウォッチを設定すると、見栄えが良くなります。Invokeを呼び出したときに渡すものが気に入らないだけです。

RoutedEventハンドラーのリストを取得する方法の最後のステップにいるように感じます(WPF RoutedEventsでは機能しない古き良きGetInvocationList()のようです)。そこから、各コントロールからこれらのハンドラーを削除し、イベントのないフォームを作成して、独自のイベントを追加するのは簡単です。

手がかりはありますか?繰り返しますが、タスク全体を実行するためのより良い/より簡単な方法がある場合は、私に知らせてください:)

4

3 に答える 3

3

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

コントロールによって提供されるイベントのリストを取得するには、引き続きリフレクションを使用する必要がありますが、少なくともこの方法では、リフレクションを使用してイベントを削除することはありません。また、ロードするアセンブリがクラス ハンドラーも登録する場合 (おそらくコードよりも前に)、そのアセンブリーが最初に呼び出されますが、これはめったに発生しないと思います。

于 2009-06-12T11:11:04.833 に答える
3

使えばGetEventHandlers.Invoke(EventHandlersStore , Params)うまくいくようで、クラッシュもしません。

于 2009-08-01T17:40:07.040 に答える
1

上記のコードを使用して、これを行いました:

// 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); while (!(コントロールはウィンドウ) && !(コントロールはページ)) { 親 = VisualTreeHelper.GetParent(親); }

そして、そのインスタンスを使用して、次の方法でイベントを呼び出すことができます。

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
于 2013-04-06T17:50:47.257 に答える