TL;DR: Can I make a handler that is attached to many events fire only once, even when I know all of the events will be fired?
I'm trying to listen to a collection of objects, each of which will complete a specific task and then notify the owner that it is completed. To do this I've simply looped over the collection and added an event handler to each object
foreach(var obj in collection)
{
obj.Event += GroupHandler;
}
and that works fine. However since I know the tasks will complete around the same time, and since it really doesn't matter if I queue a bit early I'd like to have a way to ensure that once GroupHandler is raised, it won't be raised again for the current execution set so I could do something like:
private void GroupHandler(object sender, EventArgs e)
{
foreach(var obj in collection)
{
obj.QueueNext();
}
}
Ideally I'd end up with something like
private void GroupHandler(object sender, EventArgs e)
{
if(GroupHandler.HasRun) return;
foreach(var obj in collection)
{
obj.QueueNext();
}
}
I know that I could use some global bool (or more likely an int to track cycles) but I don't like that as a solution. I'm looking for something a bit nicer than that.
As Gusman said in comments, you can have just one of the objects fire off the event. That way you know it executes only once per event set.
Alternatively, you could set up a Timer, a counter and a flag. Set the flag on the first event execution and then start the timer. As long as the flag is set, ignore the rest of the events. Set your counter to the number of objects you're tracking and decrement the counter as each one fires off its event. When either the timer expires or the counter reaches zero, reset the flag, reset the counter and cancel the timer. This could rapidly get complicated and may have a bunch of corner cases, so if you don't care too much about timing, then the first method is much better. Simplify as much as possible.
I ended up working with what Gusman mentioned in the comments an removed the event handlers. The real key though was finding out where to reattach them. So I ended up passing the handler function in the QueueNext call.
Code looks like this
private void GroupHandler(object sender, EventArgs e)
{
foreach(var obj in collection)
{
obj.Event -= GroupHandler;
}
foreach(var obj in collection)
{
obj.QueueNext(GroupHandler);
}
}
and QueueNext looks like
public void QueueNext(EventHandler nextHandler)
{
this.Event += nextHandler
QueueNext();
}
this way the event is only called once, and will be called for each set of execution.
Related
I have a event which in many case fires many times but I want an event to be completed before the next event is ran.
I am trying to create a cryptocurrency trading bot. The event in question is private void CoinInformation_OnNewTradeEvent(object sender, AggregateTradeEventArgs e)
Since that single event fires multiple times, I always thought it would be executed in a synchronous fashion where when one event is running, the next event wont run until this is completed. I tried putting locks but realize that doesnt work.
private void CoinInformation_OnNewTradeEvent(object sender, AggregateTradeEventArgs e)
{
lock(myObject)
{
try
{
Decimal currentTradePrice = e.Trade.Price;
UpdateSMA(currentTradePrice);
TryBuy(currentTradePrice);
TrySell(currentTradePrice);
TryStopLoss(currentTradePrice);
}
catch
{
return;
}
}
}
Sorry seems like my TryBuy and TrySell were async and I didnt await them thus there were reentries.
In my WPF application, I have an event handler that gets called on the MouseEnter event of my UI element:
myUiElement.MouseEnter += myEventHandler
I would like to throttle myEventHandler so it doesn't get called more than once every second. How can I do this? Is Rx the best approach just for this? I'm using .NET 4.0 if it makes a difference.
Also, I need to make sure that the MouseLeave event always gets called before the next MouseEnter event; do I need to manage this on my own? Or is the framework already designed so that MouseLeave events will always be called before the next MouseEnter event? What if I have asynchronous code in these event handlers?
Using Rx, you want to use the Sample method or Throttle.
Something like this should work (untested):
Observable
.FromEventPattern<TextChangedEventArgs>(myUiElement, "MouseEnter")
.Sample(TimeSpan.FromSeconds(1))
.Subscribe(x => ... Do Stuff Here ...);
The difference between Sample and Throttle is that Sample will take a value every 1 second no matter when the last value was taken, whereas Throttle will take a value and then wait another 1 second before taking another.
It probably depends on what you are shooting for...
You could use reactive extensions, but you could accomplish this just as easily with a timer.
Set a flag along with a Timer. When the timer tick event fires, set the flag to false, disable the timer, and run the code for your event. Then, in your control event handlers, have the handler code skipped if the flag is set.
bool flag;
DispatcherTimer timer;
public constructor()
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s,e) => {
flag = false;
timer.Stop()
DoThrottledEvent();
}
}
void mouse_enter(object sender, MouseEventArgs args)
{
if(!flag)
{
flag = true;
timer.Start();
}
}
void DoThrottledEvent()
{
//code for event here
}
Reactive extensions introduces an extra dependency, but they are a bit of fun. If you are interested, go for it!
Another approach would be to use a private field to keep track of the "time" when the last mouse event occurred, and only continue processing if that time was more than one second ago.
DateTime _lastMouseEventTime = DateTime.UtcNow;
void OnMouseEnter(object sender, MouseEventArgs e)
{
DateTime now = DateTime.UtcNow;
if (now.Subtract(_lastMouseEventTime).TotalSeconds >= 1)
{
// do stuff...
}
_lastMouseEventTime = now;
}
This ensures that "stuff" gets done at least one second apart, which is what I think you were asking for.
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.
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.
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);
}
}
}
}
}
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.
I have a treeview with checkboxes and I have the following handler for the "AfterCheck" event:
private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
if (!_isCheckingInProgress)
{
trvAvailableFiles.BeginUpdate();
var nodePath = e.Node.Tag.ToString();
bool isChecked = e.Node.Checked;
e.Node.Nodes.Clear();
try
{
_fileTreeLogic.GetChildNodes(e.Node, true);
e.Node.ExpandAll();
_isCheckingInProgress = true;
SetChildrenCheckState(e.Node, isChecked);
_isCheckingInProgress = false;
}
finally
{
trvAvailableFiles.EndUpdate();
}
}
}
If you look closely you'll see that I'm checking if "_isCheckingInProgress". If it is not, then I proceed and expand all the nodes and call the SetChildrenCheckState() method. The problem I have encountered is that SetChildrenCheckState() will subsequently cause each child node to all fire the AfterCheck event for its own node.
My question is, is there a more clean way to allow the first AfterCheck event to fire but not the subsequent ones? It seems kind of hackish that I have to have an instance bool variable to check and set.
Use: if(e.Action != TreeViewAction.Unknown) instead of if (!_isCheckingInProgress). See TreeViewAction .
When the user uses the keyboard or mouse to check the checkboxes, e.Action will be TreeViewAction.ByKeyboard or TreeViewAction.ByMouse.
The MSDN provides this as example code for the TreeView.AfterCheck Event.
Edit 1: Obviously, if you're setting the checkbox yourself in code, move the code in the event handler to a new function and have the code that sets the checkbox call it directly. The point of this solution is to let you use event handlers for user input without having those events get triggered when you set the checkboxes yourself via code.
Edit 2: See Spencer's answer for an explanation of my comment in Edit 1
One recommendation you'll see occasionally around SO is to not put a lot of code into event handlers themselves. There are a number of reasons for this. First, in your case it would be easier to understand a call like:
private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
if (!_isCheckingInProgress)
{
_isCheckingInProgress = true;
try { GetAvailableFiles(); } catch {}
_isCheckingInProgress = false;
}
}
And to place the rest of your code in GetAvailableFiles(). This creates a separation between event code and action code which most people would agree is a worthwhile distinction to make.
Second, which may or may not be applicable in your case is that multiple events can cause the same action. Such as mnuFileQuit_Click and btnClose_Click as an obvious example. If both make calls to CloseApplication() it removes a lot of redundant code.
Personally, I use a function that removes and then adds the event.
private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
EnableEvents(false);
trvAvailableFiles.BeginUpdate();
var nodePath = e.Node.Tag.ToString();
bool isChecked = e.Node.Checked;
e.Node.Nodes.Clear();
try
{
_fileTreeLogic.GetChildNodes(e.Node, true);
e.Node.ExpandAll();
SetChildrenCheckState(e.Node, isChecked);
}
finally
{
trvAvailableFiles.EndUpdate();
}
EnableEvents(true);
}
private void EnableEvents(bool bEnable)
{
if(bEnable)
cbWhatever.OnChecked += EventHandler;
else
cbWhatever.OnChecked -= EventHandler;
}
No, there's no cleaner way to do what you've shown. I'm not really sure why you feel that variables are a "hack" approach. Setting a flag is a common technique used when writing UI code.
The real hack would be some obscure way to prevent raising the event the first time, but not subsequent times. Future maintenance programmers are guaranteed to understand how setting a flag works; they're not guaranteed to appreciate the "elegance" of your alternative approach.
How can the event System.ComponentModel.CancelEventArgs be used? Suppose we have the following code:
public event CancelEventHandler EventTest = delegate { };
public void MakeSomethingThatRaisesEvent()
{
CancelEventArgs cea = new CancelEventArgs();
EventTest(this, cea);
if (cea.Cancel)
{
// Do something
}
else
{
// Do something else
}
}
What happens if more than one delegate is registered on the event? There is any way to get the results of all the subscribers?
This is used on Winforms (at least) sometimes. If not possible to get all values, they suppose only one subscriber to the event?
To ask each subscriber separately, you need to access the list:
foreach (CancelEventHandler subHandler in handler.GetInvocationList())
{
// treat individually
}
Then you can check each in turn; otherwise you just get the final vote.
Normally, in most cases, the class just allows multiple subscribers, but each gets the same instance of CancelEventArgs.
If any of the subscribers set Cancel to true, the operation will be treated as canceled.
You can work around this by getting the invocation list, and sending an event to each subscriber, but this is not usually necessary.