문제
컨트롤에 새 이벤트 핸들러를 생성하려면 다음을 수행하십시오.
c.Click += new EventHandler(mainFormButton_Click);
아니면 이거
c.Click += mainFormButton_Click;
이벤트 핸들러를 제거하려면 다음을 수행하십시오.
c.Click -= mainFormButton_Click;
하지만 이벤트에서 모든 이벤트 핸들러를 어떻게 제거합니까?
해결책
에서 해결책을 찾았습니다. MSDN 포럼.아래 샘플 코드는 모든 항목을 제거합니다. Click
의 이벤트 button1
.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += button1_Click;
button1.Click += button1_Click2;
button2.Click += button2_Click;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello");
}
private void button1_Click2(object sender, EventArgs e)
{
MessageBox.Show("World");
}
private void button2_Click(object sender, EventArgs e)
{
RemoveClickEvent(button1);
}
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]);
}
}
}
다른 팁
너희들은 이 길을 너희 자신에게 너무 힘들게 만들고 있다.이것은 쉽습니다:
void OnFormClosing(object sender, FormClosingEventArgs e)
{
foreach(Delegate d in FindClicked.GetInvocationList())
{
FindClicked -= (FindClickedHandler)d;
}
}
에서 모든 이벤트 핸들러 제거:
이벤트를 단순히 null로 설정할 수 없기 때문에 직접적으로.
간접적으로, 실제 이벤트를 비공개로 만들고 주변에 부동산을 만들 수 있습니다.
다음을 수행하십시오.
List<EventHandler> delegates = new List<EventHandler>(); private event EventHandler MyRealEvent; public event EventHandler MyEvent { add { MyRealEvent += value; delegates.Add(value); } remove { MyRealEvent -= value; delegates.Remove(value); } } public void RemoveAllEvents() { foreach(EventHandler eh in delegates) { MyRealEvent -= eh; } delegates.Clear(); }
허용된 답변이 완전하지 않습니다.{add;로 선언된 이벤트에는 작동하지 않습니다.제거하다;}
작업 코드는 다음과 같습니다.
public static void ClearEventInvocations(this object obj, string eventName)
{
var fi = obj.GetType().GetEventField(eventName);
if (fi == null) return;
fi.SetValue(obj, null);
}
private static FieldInfo GetEventField(this Type type, string eventName)
{
FieldInfo field = null;
while (type != null)
{
/* Find events defined as field */
field = type.GetField(eventName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null && (field.FieldType == typeof(MulticastDelegate) || field.FieldType.IsSubclassOf(typeof(MulticastDelegate))))
break;
/* Find events defined as property { add; remove; } */
field = type.GetField("EVENT_" + eventName.ToUpper(), BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
break;
type = type.BaseType;
}
return field;
}
존재하지 않는 이벤트 핸들러를 삭제해도 아무런 해를 끼치지 않습니다.따라서 어떤 핸들러가 있는지 알고 있다면 간단히 모두 삭제할 수 있습니다.방금 비슷한 경우가 있었어요.어떤 경우에는 도움이 될 수 있습니다.
좋다:
// Add handlers...
if (something)
{
c.Click += DoesSomething;
}
else
{
c.Click += DoesSomethingElse;
}
// Remove handlers...
c.Click -= DoesSomething;
c.Click -= DoesSomethingElse;
저는 실제로 이 방법을 사용하고 있는데 완벽하게 작동합니다.Aeonhack이 작성한 코드에서 '영감'을 받았습니다. 여기.
Public Event MyEvent()
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If MyEventEvent IsNot Nothing Then
For Each d In MyEventEvent.GetInvocationList ' If this throws an exception, try using .ToArray
RemoveHandler MyEvent, d
Next
End If
End Sub
MyEventEvent 필드는 숨겨져 있지만 존재합니다.
디버깅을 하면 어떻게 되는지 알 수 있습니다 d.target
실제로 이벤트를 처리하는 개체이며, d.method
그 방법.제거하기만 하면 됩니다.
잘 작동합니다.이벤트 핸들러로 인해 더 이상 객체가 GC되지 않습니다.
나는 여기에 표시된 완전한 솔루션이 싫었고 지금 혼합하고 테스트했으며 모든 이벤트 핸들러에서 작업했습니다.
public class MyMain()
public void MyMethod() {
AnotherClass.TheEventHandler += DoSomeThing;
}
private void DoSomething(object sender, EventArgs e) {
Debug.WriteLine("I did something");
AnotherClass.ClearAllDelegatesOfTheEventHandler();
}
}
public static class AnotherClass {
public static event EventHandler TheEventHandler;
public static void ClearAllDelegatesOfTheEventHandler() {
foreach (Delegate d in TheEventHandler.GetInvocationList())
{
TheEventHandler -= (EventHandler)d;
}
}
}
쉬운!스티븐 푸낙에게 감사드립니다.
저는 일반 로컬 메서드를 사용하여 대리자를 제거했고, 다른 대리자가 설정될 때 다른 경우에 로컬 메서드가 호출되었기 때문에 이 방법을 사용했습니다.
만약 너라면 정말로 이걸 해야해...이를 수행하는 데는 숙고와 시간이 꽤 걸릴 것입니다.이벤트 핸들러는 컨트롤 내부의 이벤트-대리자 맵에서 관리됩니다.당신은
- 컨트롤 인스턴스에서 이 맵을 반영하고 가져옵니다.
- 각 이벤트를 반복하고 대리인을 얻습니다.
- 각 대리자는 차례로 연결된 일련의 이벤트 핸들러가 될 수 있습니다.따라서 obControl.RemoveHandler(event, handler)를 호출하세요.
한마디로 일이 많다.이론상으로는 가능합니다..나는 이런 것을 시도한 적이 없습니다.
제어에 대한 구독-구독 취소 단계에 대해 더 나은 제어/규제를 가질 수 있는지 확인하십시오.
스티븐이 옳습니다.많이 쉽다:
public event EventHandler<Cles_graph_doivent_etre_redessines> les_graph_doivent_etre_redessines;
public void remove_event()
{
if (this.les_graph_doivent_etre_redessines != null)
{
foreach (EventHandler<Cles_graph_doivent_etre_redessines> F_les_graph_doivent_etre_redessines in this.les_graph_doivent_etre_redessines.GetInvocationList())
{
this.les_graph_doivent_etre_redessines -= F_les_graph_doivent_etre_redessines;
}
}
}
방금 찾았어요 WinForms 컨트롤의 속성을 설정할 때 이벤트를 일시 중단하는 방법.컨트롤에서 모든 이벤트가 제거됩니다.
namespace CMessWin05
{
public class EventSuppressor
{
Control _source;
EventHandlerList _sourceEventHandlerList;
FieldInfo _headFI;
Dictionary<object, Delegate[]> _handlers;
PropertyInfo _sourceEventsInfo;
Type _eventHandlerListType;
Type _sourceType;
public EventSuppressor(Control control)
{
if (control == null)
throw new ArgumentNullException("control", "An instance of a control must be provided.");
_source = control;
_sourceType = _source.GetType();
_sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
_sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
_eventHandlerListType = _sourceEventHandlerList.GetType();
_headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
}
private void BuildList()
{
_handlers = new Dictionary<object, Delegate[]>();
object head = _headFI.GetValue(_sourceEventHandlerList);
if (head != null)
{
Type listEntryType = head.GetType();
FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
BuildListWalk(head, delegateFI, keyFI, nextFI);
}
}
private void BuildListWalk(object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI)
{
if (entry != null)
{
Delegate dele = (Delegate)delegateFI.GetValue(entry);
object key = keyFI.GetValue(entry);
object next = nextFI.GetValue(entry);
Delegate[] listeners = dele.GetInvocationList();
if(listeners != null && listeners.Length > 0)
_handlers.Add(key, listeners);
if (next != null)
{
BuildListWalk(next, delegateFI, keyFI, nextFI);
}
}
}
public void Resume()
{
if (_handlers == null)
throw new ApplicationException("Events have not been suppressed.");
foreach (KeyValuePair<object, Delegate[]> pair in _handlers)
{
for (int x = 0; x < pair.Value.Length; x++)
_sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
}
_handlers = null;
}
public void Suppress()
{
if (_handlers != null)
throw new ApplicationException("Events are already being suppressed.");
BuildList();
foreach (KeyValuePair<object, Delegate[]> pair in _handlers)
{
for (int x = pair.Value.Length - 1; x >= 0; x--)
_sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
}
}
}
}
우와.이 솔루션을 찾았지만 원하는 대로 작동하는 것은 없습니다.하지만 이건 너무 좋아요:
EventHandlerList listaEventos;
private void btnDetach_Click(object sender, EventArgs e)
{
listaEventos = DetachEvents(comboBox1);
}
private void btnAttach_Click(object sender, EventArgs e)
{
AttachEvents(comboBox1, listaEventos);
}
public EventHandlerList DetachEvents(Component obj)
{
object objNew = obj.GetType().GetConstructor(new Type[] { }).Invoke(new object[] { });
PropertyInfo propEvents = obj.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList eventHandlerList_obj = (EventHandlerList)propEvents.GetValue(obj, null);
EventHandlerList eventHandlerList_objNew = (EventHandlerList)propEvents.GetValue(objNew, null);
eventHandlerList_objNew.AddHandlers(eventHandlerList_obj);
eventHandlerList_obj.Dispose();
return eventHandlerList_objNew;
}
public void AttachEvents(Component obj, EventHandlerList eventos)
{
PropertyInfo propEvents = obj.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList eventHandlerList_obj = (EventHandlerList)propEvents.GetValue(obj, null);
eventHandlerList_obj.AddHandlers(eventos);
}
이 페이지는 나에게 많은 도움이 되었습니다.여기에서 얻은 코드는 버튼에서 클릭 이벤트를 제거하기 위한 것입니다.일부 패널에서는 두 번 클릭 이벤트를 제거하고 일부 버튼에서는 클릭 이벤트를 제거해야 합니다.그래서 특정 이벤트에 대한 모든 이벤트 핸들러를 제거하는 컨트롤 확장을 만들었습니다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
public static class EventExtension
{
public static void RemoveEvents<T>(this T target, string eventName) where T:Control
{
if (ReferenceEquals(target, null)) throw new NullReferenceException("Argument \"target\" may not be null.");
FieldInfo fieldInfo = typeof(Control).GetField(eventName, BindingFlags.Static | BindingFlags.NonPublic);
if (ReferenceEquals(fieldInfo, null)) throw new ArgumentException(
string.Concat("The control ", typeof(T).Name, " does not have a property with the name \"", eventName, "\""), nameof(eventName));
object eventInstance = fieldInfo.GetValue(target);
PropertyInfo propInfo = typeof(T).GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)propInfo.GetValue(target, null);
list.RemoveHandler(eventInstance, list[eventInstance]);
}
}
이제 이 확장 기능의 사용법입니다.버튼에서 클릭 이벤트를 제거해야 하는 경우,
Button button = new Button();
button.RemoveEvents(nameof(button.EventClick));
패널에서 doubleclick 이벤트를 제거해야 하는 경우,
Panel panel = new Panel();
panel.RemoveEvents(nameof(panel.EventDoubleClick));
저는 C# 전문가가 아니기 때문에 버그가 있으면 양해해주시고 친절하게 알려주세요.
이것은 OP에 대한 답변은 아니지만 다른 사람들에게 도움이 될 수 있도록 여기에 게시하겠다고 생각했습니다.
/// <summary>
/// Method to remove a (single) SocketAsyncEventArgs.Completed event handler. This is
/// partially based on information found here: http://stackoverflow.com/a/91853/253938
///
/// But note that this may not be a good idea, being very .Net implementation-dependent. Note
/// in particular use of "m_Completed" instead of "Completed".
/// </summary>
private static void RemoveCompletedEventHandler(SocketAsyncEventArgs eventArgs)
{
FieldInfo fieldInfo = typeof(SocketAsyncEventArgs).GetField("m_Completed",
BindingFlags.Instance | BindingFlags.NonPublic);
eventArgs.Completed -= (EventHandler<SocketAsyncEventArgs>)fieldInfo.GetValue(eventArgs);
}
때때로 우리는 ThirdParty 컨트롤을 사용하여 작업해야 하며 이러한 어색한 솔루션을 구축해야 합니다.@Anoop Muraleedharan 답변을 기반으로 추론 유형 및 ToolStripItem 지원을 사용하여 이 솔루션을 만들었습니다.
public static void RemoveItemEvents<T>(this T target, string eventName)
where T : ToolStripItem
{
RemoveObjectEvents<T>(target, eventName);
}
public static void RemoveControlEvents<T>(this T target, string eventName)
where T : Control
{
RemoveObjectEvents<T>(target, eventName);
}
private static void RemoveObjectEvents<T>(T target, string Event) where T : class
{
var typeOfT = typeof(T);
var fieldInfo = typeOfT.BaseType.GetField(
Event, BindingFlags.Static | BindingFlags.NonPublic);
var provertyValue = fieldInfo.GetValue(target);
var propertyInfo = typeOfT.GetProperty(
"Events", BindingFlags.NonPublic | BindingFlags.Instance);
var eventHandlerList = (EventHandlerList)propertyInfo.GetValue(target, null);
eventHandlerList.RemoveHandler(provertyValue, eventHandlerList[provertyValue]);
}
그리고 이렇게 사용할 수도 있어요
var toolStripButton = new ToolStripButton();
toolStripButton.RemoveItemEvents("EventClick");
var button = new Button();
button.RemoveControlEvents("EventClick");
연결된 이벤트를 제거하는 또 다른 솔루션이 있습니다(컨트롤에 대한 이벤트를 처리하는 방법이 이미 있는 경우).
EventDescriptor ed = TypeDescriptor.GetEvents(this.button1).Find("MouseDown",true);
Delegate delegate = Delegate.CreateDelegate(typeof(EventHandler), this, "button1_MouseDownClicked");
if(ed!=null)
ed.RemoveEventHandler(this.button1, delegate);
나는 이 답변을 찾았고 그것은 내 요구에 거의 맞았습니다.수업을 진행해주신 SwDevMan81에게 감사드립니다.개별 메서드를 억제하고 재개할 수 있도록 수정했으며 여기에 게시해야겠다고 생각했습니다.
// This class allows you to selectively suppress event handlers for controls. You instantiate
// the suppressor object with the control, and after that you can use it to suppress all events
// or a single event. If you try to suppress an event which has already been suppressed
// it will be ignored. Same with resuming; you can resume all events which were suppressed,
// or a single one. If you try to resume an un-suppressed event handler, it will be ignored.
//cEventSuppressor _supButton1 = null;
//private cEventSuppressor SupButton1 {
// get {
// if (_supButton1 == null) {
// _supButton1 = new cEventSuppressor(this.button1);
// }
// return _supButton1;
// }
//}
//private void button1_Click(object sender, EventArgs e) {
// MessageBox.Show("Clicked!");
//}
//private void button2_Click(object sender, EventArgs e) {
// SupButton1.Suppress("button1_Click");
//}
//private void button3_Click(object sender, EventArgs e) {
// SupButton1.Resume("button1_Click");
//}
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;
namespace Crystal.Utilities {
public class cEventSuppressor {
Control _source;
EventHandlerList _sourceEventHandlerList;
FieldInfo _headFI;
Dictionary<object, Delegate[]> suppressedHandlers = new Dictionary<object, Delegate[]>();
PropertyInfo _sourceEventsInfo;
Type _eventHandlerListType;
Type _sourceType;
public cEventSuppressor(Control control) {
if (control == null)
throw new ArgumentNullException("control", "An instance of a control must be provided.");
_source = control;
_sourceType = _source.GetType();
_sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
_sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
_eventHandlerListType = _sourceEventHandlerList.GetType();
_headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
}
private Dictionary<object, Delegate[]> BuildList() {
Dictionary<object, Delegate[]> retval = new Dictionary<object, Delegate[]>();
object head = _headFI.GetValue(_sourceEventHandlerList);
if (head != null) {
Type listEntryType = head.GetType();
FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
retval = BuildListWalk(retval, head, delegateFI, keyFI, nextFI);
}
return retval;
}
private Dictionary<object, Delegate[]> BuildListWalk(Dictionary<object, Delegate[]> dict,
object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI) {
if (entry != null) {
Delegate dele = (Delegate)delegateFI.GetValue(entry);
object key = keyFI.GetValue(entry);
object next = nextFI.GetValue(entry);
if (dele != null) {
Delegate[] listeners = dele.GetInvocationList();
if (listeners != null && listeners.Length > 0) {
dict.Add(key, listeners);
}
}
if (next != null) {
dict = BuildListWalk(dict, next, delegateFI, keyFI, nextFI);
}
}
return dict;
}
public void Resume() {
}
public void Resume(string pMethodName) {
//if (_handlers == null)
// throw new ApplicationException("Events have not been suppressed.");
Dictionary<object, Delegate[]> toRemove = new Dictionary<object, Delegate[]>();
// goes through all handlers which have been suppressed. If we are resuming,
// all handlers, or if we find the matching handler, add it back to the
// control's event handlers
foreach (KeyValuePair<object, Delegate[]> pair in suppressedHandlers) {
for (int x = 0; x < pair.Value.Length; x++) {
string methodName = pair.Value[x].Method.Name;
if (pMethodName == null || methodName.Equals(pMethodName)) {
_sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
toRemove.Add(pair.Key, pair.Value);
}
}
}
// remove all un-suppressed handlers from the list of suppressed handlers
foreach (KeyValuePair<object, Delegate[]> pair in toRemove) {
for (int x = 0; x < pair.Value.Length; x++) {
suppressedHandlers.Remove(pair.Key);
}
}
//_handlers = null;
}
public void Suppress() {
Suppress(null);
}
public void Suppress(string pMethodName) {
//if (_handlers != null)
// throw new ApplicationException("Events are already being suppressed.");
Dictionary<object, Delegate[]> dict = BuildList();
foreach (KeyValuePair<object, Delegate[]> pair in dict) {
for (int x = pair.Value.Length - 1; x >= 0; x--) {
//MethodInfo mi = pair.Value[x].Method;
//string s1 = mi.Name; // name of the method
//object o = pair.Value[x].Target;
// can use this to invoke method pair.Value[x].DynamicInvoke
string methodName = pair.Value[x].Method.Name;
if (pMethodName == null || methodName.Equals(pMethodName)) {
_sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
suppressedHandlers.Add(pair.Key, pair.Value);
}
}
}
}
}
}