codewario
codewario

Reputation: 21418

Temporarily stop form events from either being raised or being handled?

I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:

private bool myOpRunning = false;

private void OpFunction()
{
    myOpRunning = true;
    // do stuff
    myOpRunning = false;
}

private void someHandler(object sender, EventArgs e)
{
    if (myOpRunning) return;
    // otherwise, do things
}

But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.

Upvotes: 4

Views: 12609

Answers (3)

M.A de Vreugd
M.A de Vreugd

Reputation: 1

You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.

Upvotes: 0

Pete Baughman
Pete Baughman

Reputation: 3034

You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:

public sealed class FilteredEventHandler
{
    private readonly Func<bool> supressEvent;
    private readonly EventHandler realEvent;

    public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
    {
        this.supressEvent = supressEvent;
        this.realEvent = eventToRaise;
    }

    //Checks the "supress" flag and either call the real event handler, or skip it
    public void FakeEventHandler(object sender, EventArgs e)
    {
        if (!this.supressEvent())
        {
            this.realEvent(sender, e);
        }
    }
}

Then when you hook up the event, do this:

this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;

When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.

Edit to answer question in the comments:

Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:

this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!

because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.

Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:

FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;

What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:

public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);

or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it

public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
  //. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler

Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.

My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.

Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.

Upvotes: 10

Patrick
Patrick

Reputation: 5846

You can do it with reflection ...

public static void UnregisterAllEvents(object objectWithEvents)
{
    Type theType = objectWithEvents.GetType();

    //Even though the events are public, the FieldInfo associated with them is private
    foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
    {
        //eventInfo will be null if this is a normal field and not an event.
        System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
        if (eventInfo != null)
        {
            MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
            if (multicastDelegate != null)
            {
                foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
                {
                    eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
                }
            }
        }
    }
}

Upvotes: 1

Related Questions