2

I have this set of functions to unregister the event listBox1_SelectedValueChanged for some amount of time.

The function is called: Pause().

Is there any way to do this generally as a function, that can do the same for variety of event like:

Pause(listBox1.SelectedValueChanged)

or

Pause(button1.Click)

etc.?

    private System.Windows.Forms.Timer disableEvent = new System.Windows.Forms.Timer();
    private void Pause(int forTime = 200)
    {
        listBox1.SelectedValueChanged -= new EventHandler(listBox1_SelectedValueChanged);

        disableEvent.Tick += new EventHandler(disableEvent_Tick);
        disableEvent.Interval = (forTime);
        disableEvent.Enabled = true;
        disableEvent.Start();
    }
    private void disableEvent_Tick(object sender, EventArgs e)
    {
        if (disableEvent.Enabled == true)
        { 
            disableEvent.Tick -= new EventHandler(disableEvent_Tick);                
            disableEvent.Stop();
            disableEvent.Enabled = false;

            listBox1.SelectedValueChanged += new EventHandler(listBox1_SelectedValueChanged);
        }
    }
Josef Kvita
  • 156
  • 1
  • 13
  • You want to pause given event for ALL handlers, not just one (listBox1_SelectedValueChanged in your example)? – Evk Nov 10 '16 at 13:21
  • Not all, but specific. For example in one function I wanna pause listBox1_SelectedValueChanged, in second function I wanna pause button1_Click etc. – Josef Kvita Nov 10 '16 at 13:24
  • You can't make a 'general' method with such functional, bc it must get some event as a param to (un)subscribe, but you can't `event`is't object. You can only do such a function for different object's of the same Type, for `Button` to 'stop' click event as example. Just send 2 params, object (Button as example) and some delegate for (un)subscribed method. – Shakra Nov 10 '16 at 13:29
  • And I thought there is everything possible with programming. :-) – Josef Kvita Nov 10 '16 at 13:42

5 Answers5

2

I would use a DateTime field for that. I would check in the SelectedValuedChanged() even if it is allowed to run. (do not unregister the event)

For example: (pseudo)

public class Class1
{
    private DateTime _isEnabledAfter = DateTime.MinValue;

    public Class()
    {
        listBox1.SelectedValueChanged += new EventHandler(listBox1_SelectedValueChanged);
    }

    public void Pause(int timeMS)
    {
        // set the _isEnabledAfter in the future.
        _isEnabledAfter = DateTime.Now.AddMilliseconds(timeMS);
    }


    public void listBox1_SelectedValueChanged(object sender, EventArgs e)
    {
        // is it after _isEnabledAfter?
        if(DateTime.Now < _isEnabledAfter)
            // nope... do nothing.
            return;

        // do your thing.
    }

}

It will save some you a timer and complexity.


Could be something like this:

public class Class1
{
    private TimeEnabledEvent _selectedValueChanged = new TimeEnabledEvent();

    public Class1()
    {
        listBox1.SelectedValueChanged += (s, e) =>
        {
            if (_selectedValueChanged.IsEnabled)
                listBox1_SelectedValueChanged(s, e);
        };


        _selectedValueChanged.Pause(200);
    }


    public void listBox1_SelectedValueChanged(object sender, EventArgs e)
    {
        // do your thing.
    }

}

public class TimeEnabledEvent
{
    private DateTime _isEnabledAfter = DateTime.MinValue;

    public void Pause(int timeMS)
    {
        // set the _isEnabledAfter in the future.
        _isEnabledAfter = DateTime.Now.AddMilliseconds(timeMS);
    }

    public bool IsEnabled
    {
        get { return (DateTime.Now >= _isEnabledAfter); }
    }
}  

Update 2:

public partial class Form1 : Form
{
    private TimeEnabledEvent _event = new TimeEnabledEvent();

    public Form1()
    {
        InitializeComponent();
        listBox1.SelectedValueChanged += _event.Check(ListBox1_SelectedValueChanged);
        _event.Pause(1000);
    }

    private void ListBox1_SelectedValueChanged(object sender, EventArgs e)
    {
        // do your thing
    }
}

internal class TimeEnabledEvent
{
    internal EventHandler Check(EventHandler listBox1_SelectedValueChanged)
    {
        return new EventHandler((ss, ee) =>
        {
            if (DateTime.Now >= _isEnabledAfter)
                listBox1_SelectedValueChanged(ss, ee);
        });
    }

    private DateTime _isEnabledAfter = DateTime.MinValue;

    public void Pause(int timeMS)
    {
        // set the _isEnabledAfter in the future.
        _isEnabledAfter = DateTime.Now.AddMilliseconds(timeMS);
    }

}
Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57
0

I would use Microsoft's Reactive Framework - NuGet "System.Reactive" - then you can do this:

bool pause = false;

IObservable<EventPattern<EventArgs>> observable =
    Observable
        .FromEventPattern<EventHandler, EventArgs>(
            h => listBox1.SelectedIndexChanged += h,
            h => listBox1.SelectedIndexChanged -= h)
        .Where(ep => pause != true);

IDisposable subscription =
    observable
        .Subscribe(ep => listBox1_SelectedValueChanged(ep.Sender, ep.EventArgs));

Now just changing the value of pause from false to true will pause the event handling.

When you want to detached the handler just call subscription.Dispose().

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

I'd use a WaitHandle to do that, for example a ManualResetEvent. If you want to suspend multiple events independently, I'd use different ManualResetEvents.

I'd implement it like this:

private ManualResetEvent pauseListBox1;
private ManualResetEvent pauseButton1;

To start a pause, I'd use:

pauseListBox1.Set();

To end a pause, I'd use:

pauseListBox1.Reset();

In the event handler I'd use this

// Return from the event handler of the even is set
if (WaitHandle.WaitOne(1))
    return;
Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
0

I will answer specifically about how you can do that in general way. How to improve pause procedure itself has already been answered.

It's true that you cannot pass event reference to another method, but you can do that with a bit of reflection. For example

private static void Pause<TSource, TEvent>(TSource source, Expression<Func<TSource, TEvent>> eventRef, TEvent handler, int forTime = 200) {
    var ev = source.GetType().GetEvent(((MemberExpression)eventRef.Body).Member.Name);
    // source.eventRef -= handler;
    ev.RemoveMethod.Invoke(source, new object[] { handler });
    // do some stuff
    // source.eventRef += handler;
    ev.AddMethod.Invoke(source, new object[] { handler });
}

Usage is

Pause(listBox1, c => c.SelectedValueChanged, listBox1_SelectedValueChanged);

Unfortunately that works only if event is implemented like this:

public event SomeDelegate MyEvent;

If it is implemented like this (and all winform control events are implemented like this)

public event SomeDelegate MyEvent {
    add {
        // do something
    }
    remove {
        // do something
    }
}

It no longer works, because you cannot pass such event reference via expression. However, expression is used only for convenience, to get event name. So you can pass event name explicitly:

private static void Pause<TSource, TEvent>(TSource source, string eventName, TEvent handler, int forTime = 200) {
    var ev = source.GetType().GetEvent(eventName);
    // source.eventRef -= handler;
    ev.RemoveMethod.Invoke(source, new object[] { handler });
    // do some stuff
    // source.eventRef += handler;
    ev.AddMethod.Invoke(source, new object[] { handler });
}

Usage then becomes

Pause<ListBox, EventHandler>(listBox1, nameof(listBox1.SelectedValueChanged), listBox1_SelectedValueChanged);

Less pretty but still works.

Evk
  • 98,527
  • 8
  • 141
  • 191
  • Looks good, exactly as what I want, but it gives me this error: http://imgur.com/a/m9V3O – Josef Kvita Nov 10 '16 at 14:31
  • Are you sure Pause implemented exactly as described? I tested it and quite sure it works. – Evk Nov 10 '16 at 14:34
  • @JosefKvita you are right, for certain events (like those used by WinForms controls) that indeed does not work. Updated my answer with an option which does work with those events. – Evk Nov 11 '16 at 09:58
  • Thanks. I've found a way in the meantime, it's much more pretty to call, just: listBox1.Pause(3000); or listBox1.Pause("listBox1_SelectedValueChanged", 3000); but It's much more complicated. I'm just a beginner, so I don't know what's better. – Josef Kvita Nov 11 '16 at 10:44
0

I've found a way (used some code from this forum), it works, but it's little complicated (ok, maybe very complicated) here is the code:

Usage (to pause for some time):

listBox1.Pause("listBox1_SelectedValueChanged", 3000);
listBox1.Pause(3000);  // to pause all events of listbox1
button3.Pause("button3_Click", 10000);

Usage (to suppress until resumed):

cEventSuppressor temp = listBox1.Suppress("listBox1_SelectedValueChanged");
cEventSuppressor temp = listBox1.Suppress(); //to suppress all

Usage (after suppressing - for resuming):

temp.Resume("listBox1_SelectedValueChanged");
temp.Resume();  //To resume all

The rest:

#region Events
public static class Events
{        
    public static void Pause(this Control control, string eventName, int forTime)
    {
        EventTimers et = new EventTimers();
        et.PauseEvent(control, eventName, forTime);
    }

    public static void Pause(this Control control, int forTime)
    {
        EventTimers et1 = new EventTimers();
        et1.PauseEvent(control, forTime);
    }

    public static cEventSuppressor Suppress(this Control control, string eventName)
    {
        cEventSuppressor newControl = null;
        newControl = new cEventSuppressor(control);
        newControl.Suppress(eventName);
        return newControl;
    }
    public static cEventSuppressor Suppress(this Control control)
    {
        cEventSuppressor newControl = null;
        newControl = new cEventSuppressor(control);
        newControl.Suppress();
        return newControl;
    }
}

public class EventTimers
{             
    private System.Windows.Forms.Timer disableEvent = new System.Windows.Forms.Timer();
    private cEventSuppressor suppressedControl { get; set; }

    private static string eventName { get; set; }

    //Pause specific Event
    public void PauseEvent(Control control, string eventName, int forTime)
    {
        suppressedControl = new cEventSuppressor(control);
        suppressedControl.Suppress(eventName);          

        disableEvent.Tick += new EventHandler(disableEvent_Tick);
        disableEvent.Interval = (forTime);
        disableEvent.Enabled = true;
        disableEvent.Start();
    }
    private void disableEvent_Tick(object sender, EventArgs e)
    {
        if (disableEvent.Enabled == true)
        {
            disableEvent.Tick -= new EventHandler(disableEvent_Tick);
            disableEvent.Stop();
            disableEvent.Enabled = false;

            suppressedControl.Resume(eventName);
        }
    }

    //Pause All Events
    public void PauseEvent(Control control, int forTime)
    {
        suppressedControl = new cEventSuppressor(control);
        suppressedControl.Suppress();

        disableEvent.Tick += new EventHandler(disableEvent_Tick2);
        disableEvent.Interval = (forTime);
        disableEvent.Enabled = true;
        disableEvent.Start();
    }
    private void disableEvent_Tick2(object sender, EventArgs e)
    {
        if (disableEvent.Enabled == true)
        {
            disableEvent.Tick -= new EventHandler(disableEvent_Tick2);
            disableEvent.Stop();
            disableEvent.Enabled = false;

            suppressedControl.Resume();
        }
    }

}    
public class cEventSuppressor
{
    Control _source;
    EventHandlerList _sourceEventHandlerList;
    FieldInfo _headFI;
    Dictionary<object, Delegate[]> suppressedHandlers = new Dictionary<object, Delegate[]>();
    PropertyInfo _sourceEventsInfo;
    Type _eventHandlerListType;
    Type _sourceType;

    public cEventSuppressor(Control control)
    {
        if (control == null)
            throw new ArgumentNullException("control", "An instance of a control must be provided.");

        _source = control;
        _sourceType = _source.GetType();
        _sourceEventsInfo = _sourceType.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
        _sourceEventHandlerList = (EventHandlerList)_sourceEventsInfo.GetValue(_source, null);
        _eventHandlerListType = _sourceEventHandlerList.GetType();
        _headFI = _eventHandlerListType.GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
    }
    private Dictionary<object, Delegate[]> BuildList()
    {
        Dictionary<object, Delegate[]> retval = new Dictionary<object, Delegate[]>();
        object head = _headFI.GetValue(_sourceEventHandlerList);
        if (head != null)
        {
            Type listEntryType = head.GetType();
            FieldInfo delegateFI = listEntryType.GetField("handler", BindingFlags.Instance | BindingFlags.NonPublic);
            FieldInfo keyFI = listEntryType.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic);
            FieldInfo nextFI = listEntryType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic);
            retval = BuildListWalk(retval, head, delegateFI, keyFI, nextFI);
        }
        return retval;
    }

    private Dictionary<object, Delegate[]> BuildListWalk(Dictionary<object, Delegate[]> dict,
                                object entry, FieldInfo delegateFI, FieldInfo keyFI, FieldInfo nextFI)
    {
        if (entry != null)
        {
            Delegate dele = (Delegate)delegateFI.GetValue(entry);
            object key = keyFI.GetValue(entry);
            object next = nextFI.GetValue(entry);

            if (dele != null)
            {
                Delegate[] listeners = dele.GetInvocationList();
                if (listeners != null && listeners.Length > 0)
                {
                    dict.Add(key, listeners);
                }
            }
            if (next != null)
            {
                dict = BuildListWalk(dict, next, delegateFI, keyFI, nextFI);
            }
        }
        return dict;
    }
    public void Resume()
    {
        Resume(null);
    }
    public void Resume(string pMethodName)
    {
        //if (_handlers == null)
        //    throw new ApplicationException("Events have not been suppressed.");
        Dictionary<object, Delegate[]> toRemove = new Dictionary<object, Delegate[]>();

        // goes through all handlers which have been suppressed.  If we are resuming,
        // all handlers, or if we find the matching handler, add it back to the
        // control's event handlers
        foreach (KeyValuePair<object, Delegate[]> pair in suppressedHandlers)
        {

            for (int x = 0; x < pair.Value.Length; x++)
            {

                string methodName = pair.Value[x].Method.Name;
                if (pMethodName == null || methodName.Equals(pMethodName))
                {
                    _sourceEventHandlerList.AddHandler(pair.Key, pair.Value[x]);
                    toRemove.Add(pair.Key, pair.Value);
                }
            }
        }
        // remove all un-suppressed handlers from the list of suppressed handlers
        foreach (KeyValuePair<object, Delegate[]> pair in toRemove)
        {
            for (int x = 0; x < pair.Value.Length; x++)
            {
                suppressedHandlers.Remove(pair.Key);
            }
        }
        //_handlers = null;
    }
    public void Suppress()
    {
        Suppress(null);
    }
    public void Suppress(string pMethodName)
    {
        //if (_handlers != null)
        //    throw new ApplicationException("Events are already being suppressed.");

        Dictionary<object, Delegate[]> dict = BuildList();

        foreach (KeyValuePair<object, Delegate[]> pair in dict)
        {
            for (int x = pair.Value.Length - 1; x >= 0; x--)
            {
                //MethodInfo mi = pair.Value[x].Method;
                //string s1 = mi.Name; // name of the method
                //object o = pair.Value[x].Target;
                // can use this to invoke method    pair.Value[x].DynamicInvoke
                string methodName = pair.Value[x].Method.Name;

                if (pMethodName == null || methodName.Equals(pMethodName))
                {
                    _sourceEventHandlerList.RemoveHandler(pair.Key, pair.Value[x]);
                    suppressedHandlers.Add(pair.Key, pair.Value);
                }
            }
        }
    }
}
#endregion
Josef Kvita
  • 156
  • 1
  • 13