Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Throttle an Event Handler

Tags:

c#

wpf

throttling

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?

like image 284
Drake Avatar asked Jan 19 '15 03:01

Drake


3 Answers

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...

like image 192
Justin Pihony Avatar answered Oct 19 '22 01:10

Justin Pihony


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!

like image 8
kindasimple Avatar answered Oct 18 '22 23:10

kindasimple


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.

like image 3
Steven Rands Avatar answered Oct 19 '22 00:10

Steven Rands