Temporarily disable event handler
Temporarily disable event handler.
I was just working on a project where I had to temporarily disable event handler. In my case it was property change notification handler, I wanted to disable it when it was me who changed the property. This is the automatic solution that disables and re-enables event handler:
public class Subscriber
{
private EventSource _eventSource;
public Subscriber(EventSource source)
{
_eventSource = source;
_eventSource.PropertyChanged += Source_PropertyChanged;
}
private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//
}
public void DoWithoutEvents()
{
using (new DisableEvent(_eventSource, this, "Source_PropertyChanged"))
{
// Source_PropertyChanged function will not be called inside this block
_eventSource.Property = 1;
}
}
}
It works on single objects and on collections.
And here is the code:
internal class ResubscribeInfo
{
public EventInfo Evnt { get; private set; }
public object EventSource { get; private set; }
public Delegate ToBeResubscribed { get; private set; }
public ResubscribeInfo(EventInfo evnt, object eventSource, Delegate toBeResubscribed)
{
Evnt = evnt;
EventSource = eventSource;
ToBeResubscribed = toBeResubscribed;
}
}
public class DisableEvent : IDisposable
{
private static BindingFlags _privatePublicStaticInstance =
BindingFlags.IgnoreCase |
BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.FlattenHierarchy;
private List<ResubscribeInfo> _resubscribeInfo = new List<ResubscribeInfo>();
public DisableEvent(object eventSource, object eventSubscriber, string handlerName)
{
if (eventSource == null || eventSubscriber == null ||
string.IsNullOrEmpty(handlerName))
throw new ArgumentNullException();
var tSource = eventSource.GetType();
var tSubscriber = eventSubscriber.GetType();
//
MethodInfo targetMethod = GetMethod(tSubscriber, handlerName);
if (targetMethod == null)
throw new InvalidOperationException("Method " + handlerName + " was not found");
//
if (!InternalDisable(eventSource, eventSubscriber, targetMethod, tSource))
{
// if it is enumerable, disable event on all elements
if (eventSource is IEnumerable)
{
tSource = null;
foreach (var el in eventSource as IEnumerable)
{
if (tSource == null) tSource = el.GetType();
InternalDisable(el, eventSubscriber, targetMethod, tSource);
}
}
}
}
public void Dispose()
{
if (_resubscribeInfo != null)
{
foreach (var resub in _resubscribeInfo)
{
resub.Evnt.GetAddMethod(true).Invoke(
resub.EventSource, new object[] {resub.ToBeResubscribed});
}
}
_resubscribeInfo = null;
}
private bool InternalDisable(object eventSource,
object eventSubscriber, MethodInfo targetMethod, Type tSource)
{
bool found = false;
foreach (var e in tSource.GetEvents())
{
var field = GetField(tSource, e.Name);
var deleg = (Delegate) field.GetValue(eventSource);
if (deleg != null)
{
var invocList = deleg.GetInvocationList();
var toBeUnsubscribed = invocList.FirstOrDefault(
x => x.Target == eventSubscriber && x.Method == targetMethod);
if (toBeUnsubscribed != null)
{
e.GetRemoveMethod(true).Invoke(eventSource, new object[] {toBeUnsubscribed});
_resubscribeInfo.Add(new ResubscribeInfo(e, eventSource, toBeUnsubscribed));
found = true;
break;
}
}
}
return found;
}
private FieldInfo GetField(Type type, string name)
{
if (type.IsInterface)
{
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0)
{
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces())
{
if (considered.Contains(subInterface)) continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var prop = subType.GetField(name, _privatePublicStaticInstance);
if (prop != null) return prop;
}
}
var retval = type.GetField(name, _privatePublicStaticInstance);
if (retval == null)
{
var btype = type.BaseType;
if (btype != null) retval = GetField(btype, name);
}
return retval;
}
private MethodInfo GetMethod(Type type, string name)
{
while (type != null)
{
var method = type.GetMethod(name, _privatePublicStaticInstance);
if (method != null) return method;
type = type.BaseType;
}
return null;
}
}