Prevent class from raising event

Is there generic way to prevent class to raise specific event, execute some code and then restore this event, without knowing event sunscribers For example, this is non-generic way:

checkBox1.CheckedChanged -= checkBox1_OnCheckedChanged;
checkBox1.Checked = true; // doesn't call checkBox1_OnCheckedChanged
checkBox1.CheckedChanged += checkBox1_OnCheckedChanged; // restore subscription

I want to do this by generic way, without knowing anything about CheckedChanged event subscribers. Pseudo-code (not compiled):

EventHandler e = checkBox1.CheckedChanged; // keep existing subscribers
checkBox1.CheckedChanged = null; // cancel notifications
checkBox1.Checked = true;
checkBox1.CheckedChanged = e; // restore subscribers

I beleive that something like this can be done with underlying delegate, but event doesn't allow this.



Answer this question

Prevent class from raising event

  • BrettKamerad

    Nice one!


  • byronfromwesleyan

    As mentioned before, it's *not advisable at all* to do this, but quite possible! (I had some fun doing this Smile)
    Consider the following code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Reflection;

    namespace WindowsApplication1
    {
    public partial class Form1 : Form
    {
    Delegate[] subscribers = new Delegate[10];

    public Form1()
    {
    InitializeComponent();
    }

    private void checkBox1_CheckedChanged(object sender, EventArgs e)
    {
    this.Text = checkBox1.Checked.ToString();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
    subscribers = GetEventSubscribers(checkBox1, "CheckedChanged");
    }

    private void Unsubscribe(object sender, EventArgs e)
    {
    foreach (Delegate subscriber in subscribers)
    {
    checkBox1.CheckedChanged -= (EventHandler)subscriber;
    }
    }

    private void Subscribe(object sender, EventArgs e)
    {
    foreach (Delegate subscriber in subscribers)
    {
    checkBox1.CheckedChanged += (EventHandler)subscriber;
    }
    }

    public Delegate[] GetEventSubscribers(object target, string eventName)
    {
    string EventName = "EVENT_" + eventName.ToUpper();

    Type t = target.GetType();
    do
    {
    FieldInfo[] fields = t.GetFields(
    BindingFlags.Static |
    BindingFlags.Instance |
    BindingFlags.NonPublic);
    foreach (FieldInfo field in fields)
    {
    if (field.Name == EventName)
    {
    EventHandlerList eventHandlers = ((EventHandlerList)(target.GetType().GetProperty("Events", (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic | BindingFlags.Instance))).GetValue(target, null)));
    Delegate d = eventHandlers[field.GetValue(target)];
    if ((!(d == null)))
    {
    return d.GetInvocationList();
    }
    }
    }
    t = t.BaseType;
    } while (t != null);
    return new Delegate[] { };
    }

    }
    }



  • Srdjan

    No, it's not possible. Think about the fact that for each event 2 methods (add_X and remove_X) are exposed and not the event handler itself so you are limited in what you can do with an event to these 2 operations, nothing more, nothing less.

    Also a generic way to do this would be risky because you must know for sure who is listening for an event. Just "hiding" the even blindly from whoever listens to it can lead to problems if one of the listeners relies on the event.


  • desilets

    Great! Reflection is back door. I made wrapper class to simplify client code, but all credit is yours.
    My code is the following:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Reflection;
    using System.ComponentModel;


    namespace RemoveSubscription
    {
    class DontRaiseEvent : IDisposable
    {
    object target;
    string eventName;
    Delegate[] subscribers;
    Type targetType;

    public DontRaiseEvent(object target, string eventName)
    {
    this.target = target;
    this.eventName = eventName;
    Unsubscribe();
    }

    public void Dispose()
    {
    if ( subscribers != null )
    {
    Subscribe();
    }
    }

    void Subscribe()
    {
    foreach (Delegate d1 in subscribers)
    {
    targetType.GetEvent(eventName).AddEventHandler(target, d1);
    }
    }

    /// <summary>
    /// Unsubscribe and keep invocation list
    /// </summary>
    void Unsubscribe()
    {
    string EventName = "EVENT_" + eventName.ToUpper();

    targetType = target.GetType();

    do
    {
    FieldInfo[] fields = targetType.GetFields(
    BindingFlags.Static |
    BindingFlags.Instance |
    BindingFlags.NonPublic);

    foreach (FieldInfo field in fields)
    {
    if (field.Name == EventName)
    {
    EventHandlerList eventHandlers = ((EventHandlerList)(target.GetType().GetProperty("Events",
    (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic | BindingFlags.Instance))).GetValue(target, null)));

    Delegate d = eventHandlers[field.GetValue(target)];

    if ((!(d == null)))
    {
    subscribers = d.GetInvocationList();

    foreach (Delegate d1 in subscribers )
    {
    targetType.GetEvent(eventName).RemoveEventHandler(target, d1);
    }

    return;
    }
    }
    }

    targetType = targetType.BaseType;

    } while (targetType != null);
    }

    }
    }

    And this is client code:

    /// <summary>
    /// Change checkbox state programmatically without raising event.
    /// </summary>
    private void btnTest_Click(object sender, EventArgs e)
    {
    using ( new DontRaiseEvent(checkBox1, "CheckedChanged") )
    {
    checkBox1.Checked = !checkBox1.Checked;
    }
    }

    Ready for use solution for everybody who needs this. I have a lot of situations when I change Windows controls programmatically and don't want to raise events at this time. Unsibscribing and subscribing manually is annoying and error-prone. DontRaiseEvent class is exactly what I need. Thank you.


  • Prevent class from raising event