Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 701
Bender the Greatest Avatar asked Dec 27 '22 05:12

Bender the Greatest


1 Answers

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.

like image 136
Pete Baughman Avatar answered Feb 13 '23 22:02

Pete Baughman