views:

127

answers:

4

Did anyone handle such scenario in the code?

User is typing something in the input area (say, rich text box). This causes multiple TextChanged events to fire. We want to capture only last changed event when user stops typing (i.e.: no changed events for 5 seconds).

Does anyone have a clean C# snippet of doing that?

A: 

I haven't used it myself but this article looks promising.

I was creating another control, a pager control to be exact, and ran into a problem. The problem was that when a user would type in for example 100 to navigate to that page, I was trapping the TextChanged event and it would first go to page 1, then page 10, then page 100(get the mental picture). I didn't want this to happen for a couple of reasons but the main one is because I'm not storing data of each page in memory and it gets populated when the page is selected from a SQL server, so I wanted to eliminate 3 queries when one is needed for the example previously mentioned.

So I decided that instead of adding a "go" or "refresh" button to my pager control, I would delay the TextBox TextChanged event so as long as the user was typing it would not fire the event until he/she was done(delay threshold was met).

Andrew Hare
+3  A: 

Make a Timer with an interval of five (or whatever) seconds, and, in your TextChanged event, stop the timer and start it again. Then, move your handler code to the timer's Tick event.

SLaks
+1. I was just typing the same answer. I did this just today. I found that the best delay is 500ms
hmemcpy
The delay would depend on what you're doing in the event.
SLaks
Yes, that's the design I've been considering, too. Unfortunately Timer class is bound to Windows.Forms (and optimized for that, too). And general guideline for multi-UI MVC is that controllers are not bound to the UI framework...
Rinat Abdullin
Use System.Timers.Timer instead; it's not bound to WinForms. Remember, though, that it will raise the event on a different thread.
SLaks
Alternatively, put the timer in the view (or, perhaps, combine it with the textbox in a reusable component) and call the controller from the timer's Tick event.
SLaks
A: 

Consider using the Leave event, which should fire when the user tabs out of the control.

Obviously, this depends on what the control is doing and what the users expect.

SLaks
Nope, unfortunately Leave wouldn't work.
Rinat Abdullin
+1  A: 

Ok, after using System.Threading.Timer as suggested by SLaks, I've got this extremely simple snippet:

public sealed class CallBuffer : IDisposable
{

 private readonly TimeSpan _timeSpan;
 private readonly Timer _timer;

 public CallBuffer(Action call, TimeSpan timeSpan)
 {
  _timeSpan = timeSpan;
  _timer = new Timer(state => call());
 }

 public void Buffer()
 {
  _timer.Change(_timeSpan, TimeSpan.FromMilliseconds(-1));
 }

 void IDisposable.Dispose()
 {
  _timer.Dispose();
  GC.SuppressFinalize(this);
 }
}

Did I miss something here?

One note, is that it does not care about the UI thread (caller should pass proper invocation) and this allows to separate this code from the views.

Rinat Abdullin
Since you don't have a finalizer, you don't need to call GC.SuppressFinalize(this); in Dispose. Also, never implement Dispose explicitly.
SLaks
Also, it might be easier to use System.Timers.Timer (not System.Threading.Timer); if you do, call Stop(), then Start() in Buffer().
SLaks
Slaks - why not IDisposable explicitly?
Rinat Abdullin