Удаление Маршрутизируемых обработчиков событий с помощью Отражения?

StackOverflow https://stackoverflow.com/questions/982709

Вопрос

Предыстория:Я использую WPF и C # (3.5) и работаю над приложением, которое позволяет пользователю просматривать форму / окно / usercontrol, которое уже является частью скомпилированной сборки.Когда они просматривают его, они должны иметь возможность нажимать на любой элемент управления (кнопки, текстовые поля, даже метки), рядом с элементом управления должен появиться небольшой всплывающий редактор, где они затем могут ввести всплывающую подсказку, helpID и т.д. Для этого элемента управления.

Долго и коротко об этом:Мне нужно имитировать базовый вид дизайна в WPF.Это означает, что мне нужно сделать, по крайней мере, следующее:

  • Загрузите пользовательский элемент управления / window из заданной сборки (без проблем)
  • Создайте его в usercontrol / window (без проблем)
  • Очистите все подписанные обработчики событий для всех его элементов управления
  • Назначьте мой собственный обработчик событий "ShowEditorPopup" для каждого элемента управления (не должно быть проблемой)

Во-первых, если у кого-нибудь есть предложения по более легкому или лучшему маршруту, пожалуйста, дайте мне знать.(По-видимому, для WPF не существует компонента типа DesignHost (как я читал в .NET 2), так что это исключено.)

Я застрял на выделенном жирным шрифтом элементе - очистка всех подписанных обработчиков событий.Покопавшись немного и изучив Reflector, я пришел к этому классному фрагменту опасного кода (здесь я просто пытаюсь получить все обработчики событий для одной кнопки с именем someButton, определенной в XAML):

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

Вот код (вы можете запустить его из обработчика событий someButton_Click, если хотите):

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);
}

Это работает вплоть до вызова, который возвращает:Объект не соответствует целевому типу (т. Е. Мои параметры или целевой объект перепутаны).Я обнаружил, что вы можете решить эту проблему с помощью:

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

Когда я устанавливаю наблюдение на GetEventHandlers MethodInfo, это выглядит великолепно, ему просто не нравится то, что я передаю ему при вызове Invoke .

Я чувствую, что нахожусь на самом последнем шаге получения списка обработчиков RoutedEvent (например, старого доброго GetInvocationList(), который, по-видимому, не работает для WPF RoutedEvents).Оттуда будет достаточно просто удалить эти обработчики из каждого элемента управления и создать форму без событий, в которую я затем смогу добавлять свои собственные события.

Есть какие-нибудь зацепки?Опять же, если есть лучший / более простой способ выполнить задачу в целом, дайте мне знать :)

Это было полезно?

Решение

Что, если вы воспользуетесь другим подходом?Ты мог бы позвонить Менеджер событий.RegisterClassHandler() для всех событий, а затем в вашем обработчике (при условии, что событие предназначено для элемента управления в design surface, а не для части вашего пользовательского интерфейса) отметьте событие как обработанное.Это должно предотвратить его пересылку в элементы управления на вашей поверхности разработки, поскольку обработчики классов вызываются перед стандартными обработчиками событий.

Вам все равно нужно было бы использовать отражение, чтобы получить список событий, предоставляемых элементом управления, но, по крайней мере, таким образом, вы не использовали бы отражение для удаления событий.Кроме того, если загружаемая вами сборка также регистрирует обработчик класса (вероятно, до того, как это сделает ваш код), их обработчик будет вызван первым, но я бы предположил, что это будет редким случаем.

Другие советы

Если вы используете GetEventHandlers.Invoke(EventHandlersStore , Params) кажется, он работает хорошо и не выходит из строя.

Используя ваш приведенный выше код, я сделал это:

// 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.Итак, чтобы получить это:переменный родитель = VisualTreeHelper.getParent(элемент управления);while (!(элемент управления - это окно) && !(элемент управления - это страница)) { parent = VisualTreeHelper.getParent(родительский);}

И с помощью этого экземпляра вы можете затем вызвать событие с:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top