У меня есть кнопочный элемент управления, и мне нужно будет удалить все обработчики событий, прикрепленные к его событию Click.
Как это возможно?
Button button = GetButton();
button.Click.RemoveAllEventHandlers();
У меня есть кнопочный элемент управления, и мне нужно будет удалить все обработчики событий, прикрепленные к его событию Click.
Как это возможно?
Button button = GetButton();
button.Click.RemoveAllEventHandlers();
Вы не можете, в основном - по крайней мере, не без размышлений и много грубости.
События строго "подписываются, отписываются" - вы не можете отписаться от другого обработчика, больше, чем вы можете изменить ссылку на кого-то другого.
Примечание. Поскольку вопрос, по которому я опубликовал свой первоначальный ответ, был закрыт как дубликат этого вопроса, опубликовать улучшенную версию моего ответа здесь. Этот ответ применим только к WPF. Он не будет работать в Windows Forms или любой другой инфраструктуре пользовательского интерфейса.
Ниже приведен полезный метод утилиты для удаления всех обработчиков событий, подписанных на маршрутизируемое событие для данного элемента. Вы можете тривиально преобразовать это в метод расширения, если хотите.
/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
{
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);
// If no event handlers are subscribed, eventHandlersStore will be null.
// Credit: /questions/24971/how-would-it-be-possible-to-remove-all-event-handlers-of-the-click-event-of-a-button/181999#181999
if (eventHandlersStore == null)
return;
// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] { routedEvent });
// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
}
Затем вы можете легко вызвать этот метод утилиты для своей кнопки Click event:
RemoveRoutedEventHandlers(button, Button.ClickEvent);
Изменить: я скопировал исправление ошибок реализованное короной, что останавливает метод отбрасывания NullReferenceException, когда нет события обработчики подписываются. Кредит (и upvotes) должен пойти на их ответ.
Просто хотел немного погладить рутину Дугласа, что мне очень понравилось. Я обнаружил, что мне нужно добавить дополнительную проверку null в eventHandlersStore для обработки любых случаев, когда переданный элемент еще не подключался к событиям.
/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
{
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);
if (eventHandlersStore == null) return;
// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] { routedEvent });
// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
}
Я нашел этот ответ здесь в StackOverflow:
Как удалить все обработчики событий из элемента управления
private void RemoveClickEvent(Button b)
{
FieldInfo f1 = typeof(Control).GetField("EventClick",
BindingFlags.Static | BindingFlags.NonPublic);
object obj = f1.GetValue(b);
PropertyInfo pi = b.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
list.RemoveHandler(obj, list[obj]);
}
Какой оригинальный плакат найден здесь:
У меня была ошибка с ошибкой с кодом Jamie Dixon, опубликованным для входа в аккаунт, не имеющий события Click.
private void RemoveClickEvent(Control control)
{
// chenged "FieldInfo f1 = typeof(Control)" to "var f1 = b.GetType()". By changing to
// the type of the passed in control we can use this for any control with a click event.
// using var allows for null checking and lowering the chance of exceptions.
var fi = control.GetType().GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null)
{
object obj = fi.GetValue(control);
PropertyInfo pi = control.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(control, null);
list.RemoveHandler(obj, list[obj]);
}
}
Затем небольшое изменение и оно должно быть для любого события.
private void RemoveClickEvent(Control control, string theEvent)
{
// chenged "FieldInfo f1 = typeof(Control)" to "var f1 = b.GetType()". By changing to
// the type of the passed in control we can use this for any control with a click event.
// using var allows for null checking and lowering the chance of exceptions.
var fi = control.GetType().GetField(theEvent, BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null)
{
object obj = fi.GetValue(control);
PropertyInfo pi = control.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(control, null);
list.RemoveHandler(obj, list[obj]);
}
}
Я предполагаю, что это может быть улучшено, но оно работает для моих текущих потребностей. Надеюсь, это полезно для кого-то.