Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / C#
Technical Blog

Temporarily disable event handler

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
20 Jul 2013CPOL 26.3K   4   1
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:

C#
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:

C#
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;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThe real solution Pin
FatCatProgrammer22-Jul-13 10:45
FatCatProgrammer22-Jul-13 10:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.