I have a textbox that has a fairly hefty _TextChanged
event handler. Under normal typing condition the performance is okay, but it can noticeably lag when the u
I don't know about culling the event queue, but I can think of two ways you may be able to handle this.
If you want something quick (and slightly dirty by some people's standards), you could introduce a wait timer of sorts - when the validation function runs, set a flag (static variable within the function should suffice) with the current time. if the function is called again within say 0.5 seconds of the last time it ran and completed, exit the function immediately (dramatically reducing the runtime of the function). This will solve the backlog of events, provided it is the contents of the function that is causing it to be slow rather than the firing of the event itself. The downside to this is that you'd have to introduce a backup check of some sort to ensure that the current state has been validated - i.e., if the last change took place while a 0.5s block was happening.
Alternatively, if your only problem is that you don't want the validation to occur when the user is engaging in a continuous action, you could try modifying your event handler such that it exits without performing the validation if a keypress is in progress, or maybe even bind the validation action to KeyUp rather than TextChanged.
There are many ways in which you could achieve this. For example, if a KeyDown event is performed on a particular key (say backspace for your example, but in theory you should extend it to anything that will type a character), the validation function will exit without doing anything until the KeyUp event of the same key is fired. That way it won't run until the last modification has been made... hopefully.
That may not be the most optimal way of achieving the desired effect (it may not work at all! There's a chance that the _TextChanged event will fire before the user has finished pressing the key), but the theory is sound. Without spending some time playing around I can't be absolutely sure on the behaviour of the keypress - can you just check whether the key is pressed and exit, or do you have to raise a flag manually that will be true between KeyDown and KeyUp? A little bit of playing around with your options should make it pretty clear what the best approach will be for your particular case.
I hope that helps!
Use a combination of TextChanged with a focus check and TextLeave.
private void txt_TextChanged(object sender, EventArgs e)
{
if (!((TextBox)sender).Focused)
DoWork();
}
private void txt_Leave(object sender, EventArgs e)
{
DoWork();
}
Reactive Extensions are dealing with this kind of scenarios very nicely.
So you want to capture TextChanged
event by throttling it for 0.1
seconds and process the input.
You can convert your TextChanged
events to IObservable<string>
and subscribe to it.
Something like this
(from evt in Observable.FromEventPattern(textBox1, "TextChanged")
select ((TextBox)evt.Sender).Text)
.Throttle(TimeSpan.FromMilliSeconds(90))
.DistinctUntilChanged()
.Subscribe(result => // process input);
So this piece of code subscribes to TextChanged
events, throttles it, makes sure you get only distinct values and then pulls Text
values out of event args.
Please note this code is more like a pseudocode, I didn't test it.
In order to use Rx Linq
, you will need to install Rx-Linq Nuget package.
If you like this approach, you can check this blog post that implements auto complete control making use of Rx Linq. I would also recommend great talk of Bart De Smet on Reactive Extensions.
I played with this for a while. To me this was the most elegant (simple) solution I could come up with:
string mostRecentText = "";
async void entry_textChanged(object sender, EventArgs e)
{
//get the entered text
string enteredText = (sender as Entry).Text;
//set the instance variable for entered text
mostRecentText = enteredText;
//wait 1 second in case they keep typing
await Task.Delay(1000);
//if they didn't keep typing
if (enteredText == mostRecentText)
{
//do what you were going to do
doSomething(mostRecentText);
}
}
Jumping off of @lisz's work, it didn't quite work for me in all edge cases but it was close. The UI would fire a false positive sometimes when the user really wasn't finished typing.
Here's the updated code that worked much more smoothly for the user.
private List<Task<bool>> taskTypeQueue = new List<Task<bool>>();
private async void textBox_TextChanged(object sender, EventArgs e)
{
async Task<bool> isStillTyping()
{
Application.DoEvents();
int taskCount = taskTypeQueue.Count;
string oldStr = textBox.Text;
await Task.Delay(1500);
if ((oldStr != textBox.Text) || (taskCount != taskTypeQueue.Count - 1))
{
return true;
}
return false;
}
taskTypeQueue.Add(isStillTyping());
if (await taskTypeQueue[taskTypeQueue.Count - 1])
return;
// typing appears to have stopped, continue
taskTypeQueue.Clear();
}
private async Task ValidateText()
{
if (m_isBusyProcessing)
return;
// Don't validate on each keychange
m_isBusyProcessing = true;
await Task.Delay(200);
m_isBusyProcessing = false;
// Do your work here.
}