Greetings all,
Is there a built in way to know when a user is done typing into a textbox? (Before hitting tab, Or moving the mouse) I have a database query that occ
I ended up trying Scott Weinstein answer although it required some deeper knowledge about threading, delegates and basic lambda syntax. And it worked pretty well. His orginal answer wasn't pure copy-paste so I had to do some playing around to get it work.
I added very little time to Thread.Sleep, since I noticed invoke method can happen twice if user is typing really quickly but with tiny random delay between some of keystrokes. You have to also add reference to WindowsBase assembly for using Dispatcher.
I use 1,5 seconds to wait user end typing.
// use manual reset event to Q up waiting threads.
// each new text changed event clears the Q
// only the last changed will hit the timeout, triggering the action
private ManualResetEvent _delayMSE;
private Func<bool> TBDelay;
private delegate void ActionToRunWhenUserStopstyping();
public Form1()
{
InitializeComponent();
_delayMSE = new ManualResetEvent(false);
TBDelay = () => !_delayMSE.WaitOne(1500, false);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
_delayMSE.Set();
// open the ResetEvent gate, to discard these delays
Thread.Sleep(20);
// let all pending through the gate
_delayMSE.Reset();
// close the gate
TBDelay.BeginInvoke(res =>
{
// callback code
// check how we exited, via timeout or signal.
bool timedOut = TBDelay.EndInvoke(res);
if (timedOut)
Dispatcher.CurrentDispatcher.Invoke(
new ActionToRunWhenUserStopstyping(DoWhatEverYouNeed),
DispatcherPriority.Input);
}, null);
}
private void DoWhatEverYouNeed()
{
MessageBox.Show(textBox1.Text);
}
It depends on what you mean by "done typing." There is an event to let you know when the user has left focus of that particular control. Also, there is a changed even that tells you when the text changes. What you could do is trap two things:
1) Lost focus
2) Every time the user changes the text, start a timer for say, 20 seconds, and if the user is done within that time, then the user is "done" typing. That is if the user has not done anything within that time then assume the user is "done."
If either of those two things occur then the user is done, make sure to stop and restart the timer appropriately. Obviously, you could change the timeout.
It all depends on how you want to define it.
'vb.net language 'global declaration
Public Tawal as integer
'on timer1 properties
Timer1.enable = true
Timer1.inverval =1000
'on procedure
Timer1.start()
if Tawal <2 then T awal =0
if Tawal >3 then
Call FillGrid
Timer1.stop()
end if
The approach I've used successfully in the past uses a manual reset event and asynchronous invocations to detect when the user has stopped typing. The code looks something like this
// use manual reset event to Q up waiting threads.
// each new text changed event clears the Q
// only the last changed will hit the timeout, triggering the action
private ManualResetEvent _delayMSE;
private Func<bool> TBDelay = () => !_delayMSE.WaitOne(600, false);
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SendOrPostCallback ActionToRunWhenUserStopsTyping = o =>
{
// ...
};
_delayMSE.Set(); // open the ResetEvent gate, to discard these delays
Thread.Sleep(0); // let all pending through the gate
_delaySearchMSE.Reset(); // close the gate
TBDelay.BeginInvoke(res =>
{
// callback code
// check how we exited, via timeout or signal.
bool timedOut = TBDelay.EndInvoke(res);
if (timedOut)
Dispatcher.Invoke(DispatcherPriority.Input,
ActionToRunWhenUserStopstyping,null);
}, null);
}
One approach:
Create a Timer
with an Interval
of X milliseconds
The interval should be about 300ms; more than a normal time between keystrokes, and also reasonable time to wait between finishing and the update occurring
In the input's TextChanged
event, Stop()
and then Start()
the Timer
This will restart the Timer
if it is already running, so if the user keeps typing at a normal rate, each change will restart the timer.
In the timer's Tick
event, Stop()
the Timer
and do the long transaction
Optional: Handle the Leave
and KeyDown
events so that leaving the control or pressing Enter will Stop()
the Timer
and do the long transaction.
This will cause an update if the text has changed, and the user hasn't made any changes in X milliseconds.
One problem with the "Measure the time since the last update" approach you're considering is that if the last change is made quickly, the update won't happen, and there won't be any subsequent changes to trigger another check.
Note: There must be a one to one pairing between TextBox
es and Timer
s; if you're planning on doing this with more than one input, I'd consider building a UserControl
that wraps this functionality.
For those who need something like this in .NET 2.0, here I made a Control that derives from TextBox and uses the same approach.. Hope this help
public partial class TextBox : System.Windows.Forms.TextBox
{
private ManualResetEvent _delayMSE;
public event EventHandler OnUserStopTyping;
private delegate bool TestTimeout();
public TextBox()
{
_delayMSE = new ManualResetEvent(false);
this.TextChanged += new EventHandler(TextBox_TextChanged);
}
void TextBox_TextChanged(object sender, EventArgs e)
{
_delayMSE.Set();
Thread.Sleep(20);
_delayMSE.Reset();
TestTimeout tester = new TestTimeout(TBDelay);
tester.BeginInvoke(new AsyncCallback(Test), tester);
}
private void Test(IAsyncResult pResult)
{
bool timedOut = (bool)((TestTimeout)pResult.AsyncState).EndInvoke(pResult);
if (timedOut)
{
if (OnUserStopTyping != null)
OnUserStopTyping(this, null);
}
}
private bool TBDelay()
{
return !_delayMSE.WaitOne(500, false);
}
}