I'm trying to delay an action on a textbox. I want to search a large quantity of text for a word. Now I do this with the "textchanged" event. The problem here is, when he reaches a word of more then 3 letters (I've prevented 0-2 wordsearches) the process becomes intensive.
Question:
What are my possibilities here?
ex: Putting a timer on the textchanged-event? If so, how do I do this?
Code:
private void txtSearch_TextChanged(object sender, EventArgs e)
{
// delay here
dosearch(searchbox.text);
}
(Re)start a dispatcher timer every time a key is pressed, and do the search when the timer elapses. Around 200-300 ms delay is usually pretty good.
private DispatcherTimer _searchTimer;
// Initialize timer in constructor with 200 ms delay and register tick event.
private void txtSearch_TextChanged(object sender, EventArgs e)
{
_searchTimer.Stop();
_searchTimer.Start();
}
private void OnSearchTimerTick(object sender, EventArgs e)
{
_searchTimer.Stop()
Search(searchBox.Text);
}
private void Search(string searchTxt)
{
// Do search
}
UPDATE:
To improve the responsiveness (the above example will lock the UI while searching because the dispatcher timer callback runs on the UI thread), you can execute the search in a separate Task. When the search completes you need to ensure that the result is not stale (that the user has not modified the search text while searching):
private void Search(string searchText)
{
Task.Run(() =>
{
// Execute search
Dispatcher.Invoke(() =>
{
if (searchText == searchBox.Text)
{
// Result is good
}
});
});
}
Related
I have a question about async\await in a C# .NET app. I'm actually trying to solve this problem in a Kinect based application but to help me illustrate, I've crafted this analogous example:
Imagine that we have a Timer, called timer1 which has a Timer1_Tick event set up. Now, the only action I take on that event is to update the UI with the current date time.
private void Timer1_Tick(object sender, EventArgs e)
{
txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture);
}
This is simple enough, my UI updates every few hundredths of seconds and I can happily watch time go by.
Now imagine that I also want to also calculate the first 500 prime numbers in the same method like so:
private void Timer1_Tick(object sender, EventArgs e)
{
txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture);
List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
PrintPrimeNumbersToScreen(primeNumbersList);
}
private List<int> WorkOutFirstNPrimeNumbers(int n)
{
List<int> primeNumbersList = new List<int>();
txtPrimeAnswers.Clear();
int counter = 1;
while (primeNumbersList.Count < n)
{
if (DetermineIfPrime(counter))
{
primeNumbersList.Add(counter);
}
counter++;
}
return primeNumbersList;
}
private bool DetermineIfPrime(int n)
{
for (int i = 2; i < n; i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
private void PrintPrimeNumbersToScreen(List<int> primeNumbersList)
{
foreach (int primeNumber in primeNumbersList)
{
txtPrimeAnswers.Text += String.Format("The value {0} is prime \r\n", primeNumber);
}
}
This is when I experience the problem. The intensive method that calculates the prime numbers blocks the event handler from being run - hence my timer text box now only updates every 30 seconds or so.
My question is, how can I resolve this while observing the following rules:
I need my UI timer textbox to be as smooth as it was before, probably by pushing the intensive prime number calculation to a different thread. I guess, this would enable the event handler to run as frequently as before because the blocking statement is no longer there.
Each time the prime number calculation function finishes, it's result to be written to the screen (using my PrintPrimeNumbersToScreen() function) and it should be immediately started again, just in case those prime numbers change of course.
I have tried to do some things with async/await and making my prime number calculation function return a Task> but haven't managed to resolve my problem. The await call in the Timer1_Tick event still seems to block, preventing further execution of the handler.
Any help would be gladly appreciated - I'm very good at accepting correct answers :)
Update: I am very grateful to #sstan who was able to provide a neat solution to this problem. However, I'm having trouble applying this to my real Kinect-based situation. As I am a little concerned about making this question too specific, I have posted the follow up as a new question here: Kinect Frame Arrived Asynchronous
May not be the best solution, but it will work. You can create 2 separate timers. Your first timer's Tick event handler only needs to deal with your txtTimerValue textbox. It can remain the way you had it originally:
private void Timer1_Tick(object sender, EventArgs e)
{
txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture);
}
For your 2nd timer's Tick event handler, define the Tick event handler like this:
private async void Timer2_Tick(object sender, EventArgs e)
{
timer2.Stop(); // this is needed so the timer stops raising Tick events while this one is being awaited.
txtPrimeAnswers.Text = await Task.Run(() => {
List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
return ConvertPrimeNumbersToString(primeNumbersList);
});
timer2.Start(); // ok, now we can keep ticking.
}
private string ConvertPrimeNumbersToString(List<int> primeNumbersList)
{
var primeNumberString = new StringBuilder();
foreach (int primeNumber in primeNumbersList)
{
primeNumberString.AppendFormat("The value {0} is prime \r\n", primeNumber);
}
return primeNumberString.ToString();
}
// the rest of your methods stay the same...
You'll notice that I changed your PrintPrimeNumbersToScreen() method to ConvertPrimeNumbersToString() (the rest remains the same). The reason for the change is that you really want to minimize the amount of work being done on the UI thread. So best to prepare the string from the background thread, and then just do a simple assignment to the txtPrimeAnswers textbox on the UI thread.
EDIT: Another alternative that can be used with a single timer
Here is another idea, but with a single timer. The idea here is that your Tick even handler will keep executing regularly and update your timer value textbox every time. But, if the prime number calculation is already happening in the background, the event handler will just skip that part. Otherwise, it will start the prime number calculation and will update the textbox when it's done.
// global variable that is only read/written from UI thread, so no locking is necessary.
private bool isCalculatingPrimeNumbers = false;
private async void Timer1_Tick(object sender, EventArgs e)
{
txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture);
if (!this.isCalculatingPrimeNumbers)
{
this.isCalculatingPrimeNumbers = true;
try
{
txtPrimeAnswers.Text = await Task.Run(() => {
List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
return ConvertPrimeNumbersToString(primeNumbersList);
});
}
finally
{
this.isCalculatingPrimeNumbers = false;
}
}
}
private string ConvertPrimeNumbersToString(List<int> primeNumbersList)
{
var primeNumberString = new StringBuilder();
foreach (int primeNumber in primeNumbersList)
{
primeNumberString.AppendFormat("The value {0} is prime \r\n", primeNumber);
}
return primeNumberString.ToString();
}
// the rest of your methods stay the same...
You should avoid using async/await (despite how good they are) because Microsoft's Reactive Framework (Rx) - NuGet either "Rx-WinForms" or "Rx-WPF" - is a far better approach.
This is the code you would need for a Windows Forms solution:
private void Form1_Load(object sender, EventArgs e)
{
Observable
.Interval(TimeSpan.FromSeconds(0.2))
.Select(x => DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture))
.ObserveOn(this)
.Subscribe(x => txtTimerValue.Text = x);
txtPrimeAnswers.Text = "";
Observable
.Interval(TimeSpan.FromSeconds(0.2))
.Select(n => (int)n + 1)
.Where(n => DetermineIfPrime(n))
.Select(n => String.Format("The value {0} is prime\r\n", n))
.Take(500)
.ObserveOn(this)
.Subscribe(x => txtPrimeAnswers.Text += x);
}
That's it. Very simple. It all happens on background threads before being marshalled back to the UI.
The above should be fairly self explanatory, but yell out if you need any further explanation.
So you want to start a Task without waiting for the result. When the task has finished calculating it should update the UI.
First some things about async-await, later your answer
The reason that your UI isn't responsive during the long action is because you didn't declare your event handler async. The easiest way to see the result of this is by creating an event handler for a button:
synchronous - UI is blocked during execution:
private void Button1_clicked(object sender, EventArgs e)
{
List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
PrintPrimeNumbersToScreen(primeNumbersList);
}
asynchronous - UI is responsive during execution:
private async void Button1_clicked(object sender, EventArgs e)
{
List<int> primeNumbersList = await Task.Run( () => WorkOutFirstNPrimeNumbers(500));
PrintPrimeNumbersToScreen(primeNumbersList);
}
Note the differences:
The function is declared async
Instead of calling the function, a Task is started using Task.Run
the await statement makes sure your UI-thread returns and keeps handling all UI requests.
once the task is finished, the UI thread continues with the next part of the await.
the value of the await is the return of the WorkOutFirstNPrimeNumber function
Note:
normally you'll see async functions return a Task instead of void and Task<TResult> instead of TResult.
The await Task is a void and await Task<TResult> is a TResult.
To start a function as a separate task use Task.Run ( () => MyFunction(...))
The return of Task.Run is an awaitable Task.
Whenever you want to use await, you'll have to declare your function async, and thus return Task or Task<TResult>.
So your callers have to be async and so forth.
The only async function that may return void is the event handler.
Your problem: timer tick reported when calculations still busy
The problem is that your timer is faster than your calculations. What do you want if a new tick is reported when the previous calculations are not finished
Start new calculations anyhow. This might lead to a lot of threads doing calculations at the same time.
Ignore the tick until no calculations are busy
You could also choose to let only one task do the calculations and start them as soon as they are finished. In that case the calculations run continuously
(1) Start the Task, but do not await for it.
private void Button1_clicked(object sender, EventArgs e)
{
Task.Run ( () =>
{ List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
PrintPrimeNumbersToScreen(primeNumbersList);
});
}
(2) ignore the tick if the task is still busy:
Task primeCalculationTask = null;
private void Button1_clicked(object sender, EventArgs e)
{
if (primeCalculationTask == null || primeCalculationTask.IsCompleted)
{ // previous task finished. Stat a new one
Task.Run ( () =>
{ List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
PrintPrimeNumbersToScreen(primeNumbersList);
});
}
}
(3) Start a task that calculates continuously
private void StartTask(CancellationToken token)
{
Task.Run( () =>
{
while (!token.IsCancelRequested)
{
List<int> primeNumbersList = WorkOutFirstNPrimeNumbers(500);
PrintPrimeNumbersToScreen(primeNumbersList);
}
})
}
I just had an interview 5 minutes back. I was given this scenario and he asked me the question on how to handle this in C# 1.0 or 2.0. He said there is a basic feature to handle this, I wasn't sure. Can somebody please help :(
Question:
There are 2 text boxes in Winform application and a button. One text box takes input value and when button is clicked, it process input in server which takes hell lot of time and finally displays the result in another textbox. As it takes very long time in server, the UI shouldn't be interupted, how do you handle this scenario he asked me :(
Answer I gave:
I told multithreading and said about async and await. He was expecting a simple way of handling this in C# 2.0 thing. I was guessing about asynchronous delegates. He wasn't convinced. Please someone explain me with little understandble code :)
You would run the long running process in a separate thread to the UI to prevent it hanging, perhaps a BackgroundWorker would be used for example: -
bgWorker.RunWorkerAsync([DATA TO PROCESS])//Passing in data to operate on
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
MyObject obj = (MyObject)sender;
//
// Do some work with the data on the server
//
// Report on progess, is it done?
bgWorker.ReportProgress();
}
You could also have a work complete method
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Work is complete, notify user
}
You could also use a ThreadPool, which requires a little more set up but i find is more flexible. For example: -
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), [DATA TO PROCESS]);
Interlocked.Increment(ref workItems);
workItems would be a count of the number of items left to process, this could be used to keep track of whether the task is complete or not. We then define a DoWork method where we process the data and decrement our workItems counter. Once an item has been processed, we report progress via delegates such as: -
private delegate void ThreadDone(MyObject obj);
private delegate void TaskDone();
public void DoWork(object sender)
{
MyObject obj = (MyObject)sender;
this.Invoke(new ThreadDone(ReportProgress), result);
Interlocked.Decrement(ref workItems);
if (workItems == 0)
{
this.Invoke(new TaskDone(WorkComplete));
}
}
Report progress might look like this: -
private void ReportProgress(MyObject obj)
{
if (workItems >= 0)
{
//Job isn't complete, report progress
}
}
private void WorkComplete()
{
}
In .Net 2 winform applications, the simplest solution is to use a BackgroundWorker. Its events are raised on the UI thread so you don't need to do any marshalling yourself.
The usual pattern looks like:
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += delegate(object sender, DoWorkEventArgs e) {
try {
e.Result = server.LongRunningOperation();
}
catch(Exception ex) {
e.Error = ex;
}
};
bg.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
//update UI
};
bg.RunWorkerAsync();
I am making an app for wp 7.x/8.
There is a function like -
public void draw()
{
.............
ImageBrush imgbrush = new ImageBrush();
imgbrush.ImageSource = new BitmapImage(...);
rect.Fill = imgbrush; //rect is of type Rectangle that has been created
//and added to the canvas
........
}
and another function
private void clicked(object sender, RoutedEventArgs e)
{
draw();
........
if (flag)
{
Thread.sleep(5000);
draw();
}
}
But when the button is clicked the result of both the draw operation appear simultaneously on the screen.
How to make the result of second draw() operation to appear after some delay?
Or is there something like buffer for the screen, until the buffer is not filled the screen will not refresh?
In that case, how to FLUSH or Refresh the screen explicitly or force the .fill() method of Rectangle to make the changes on the screen?
Any help in this regard would be highly appreciated.
As pantaloons says, since all of your actions are on the same thread (the first draw, the sleep, and the second draw), the UI itself never gets a chance to update. However, there is a slightly better implementation (though it follows the same principal as the aforementioned suggestion).
By using a timer, you can let it kick the wait to another thread, allowing the UI to update from the first draw before doing the second, like so:
private void clicked(object sender, RoutedEventArgs e)
{
draw();
........
if (flag)
{
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
timer.Tick += (sender, args) => { timer.Stop(); draw(); };
timer.Start();
}
}
In this solution, all the invocation is handled by the DispatcherTimer (which will automatically call back to the UI thread). Also, if draw needs to be called more than twice in a row, the timer will continue to tick until stopped, so it would be very straightforward to extend to include a count.
The problem is that you are blocking the UI thread by sleeping, so the draw messages are never pumped until that function returns, where the changes happen synchronously. A hacky solution would be something like the following, although really you should change your app design to use the async/await patterns.
private void clicked(object sender, RoutedEventArgs e)
{
draw();
if (flag)
{
System.Threading.Tasks.Task.Run(() =>
{
System.Threading.Thread.Sleep(5000);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
draw();
});
});
}
}
I have a Grid control and clicking on each row does some background job to load the data. Each background job is performed on a thread pool thread. When user clicks on the items quickly, lot of requests to load data will be queued. I want to minimize this by providing a delay after clicking each row. There will be some delay before firing the request to load the data.
I am thinking about using DispatcherTimer class. Something like,
readonly DispatcherTimer dt = new DispatcherTimer();
private void Clicked(object sender, RoutedEventArgs e)
{
dt.Interval = TimeSpan.FromSeconds(2);
dt.Stop();
dt.Start();
}
private void DtOnTick(object sender, EventArgs args)
{
// Fire a thread and do data loading
}
Is this the correct way to approach the problem?
Any suggestions would be appreciated!
How about disabling the control until the job is finished? Or disabling once the queue of jobs to do reaches a certain size? This would be a simple solution to prevent users from "clicking too much". And this way the delay would scale with the efficiency of your solution/speed of the computer.
The way you're trying to do it would just delay the problem itself for 2 seconds. All the clicks would just be handled two seconds later.
You might try to use a worker thread. Lets say you use a queue which takes information about each item that was clicked at the time it was clicked. An existing thread, created when the class is created, is notified when new items are added to the queue. The thread takes the first item, processes it, updates the UI. If there are more items, it takes the next one, processes it, etc. When there are no more items, the thread goes to sleep until new items are available (ManualResetEvent will help here).
The pattern would be:
void ItemClicked(...)
{
lock (WorkQueue)
{
QueueNewClickItem(...);
m_workToDo.Set();
}
}
void WorkerThread(...)
{
bool threadShouldEnd = false;
while (!threadShouldEnd)
{
if (WaitHandle.WaitAny(m_workToDo, m_endThread) == 0)
{
lock (WorkQueue)
{
CopyAllPendingWorkItemsToListInThread();
ClearWorkQueue();
m_workToDo.Reset();
}
while (!AllLocalItemsProcessed)
{
ProcessNextWorkItem();
}
}
else
{
threadShouldEnd = true;
}
}
}
What you actually want to do is something like this:
private DateTime? _NextAllowedClick;
private void Clicked(object sender, RoutedEventArgs e)
{
if (_NextAllowedClick != null && DateTime.Now < _NextAllowedClick)
{
return;
}
_NextAllowedClick = DateTime.Now + new TimeSpan(0, 0, 0, 2);
...
}
Is there a way to directly "restart" a background worker?
Calling CancelAsync() followed by RunWorkerAsync() clearly won't do it as their names imply.
Background info:
I have a background worker which calculates a total in my .net 2.0 Windows Forms app.
Whenever the user modifies any value which is part of this total I'd like to restart the background worker in case it would be running so that directly the latest values are considered.
The backgriound work itself does not do any cancleing.
When you call bgw.CancelAsync it sets a flag on the background worker that you need to check yourself in the DoWork handler.
something like:
bool _restart = false;
private void button1_Click(object sender, EventArgs e)
{
bgw.CancelAsync();
_restart = true;
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 300; i++)
{
if (bgw.CancellationPending)
{
break;
}
//time consuming calculation
}
}
private void bgw_WorkComplete(object sender, eventargs e) //no ide to hand not sure on name/args
{
if (_restart)
{
bgw.RunWorkerAsync();
_restart = false;
}
}
There are a couple of options, it all depends on how you want to skin this cat:
If you want to continue to use BackgroundWorker, then you need to respect the model that has been established, that is, one of "progress sensitivity". The stuff inside DoWork is clearly required to always be aware of whether or not the a pending cancellation is due (i.e., there needs to be a certain amount of polling taking place in your DoWork loop).
If your calculation code is monolithic and you don't want to mess with it, then don't use BackgroundWorker, but rather fire up your own thread--this way you can forcefully kill it if needs be.
You can hook the change event handler for the controls in which the values are changed and do the following in the handler:
if(!bgWrkr.IsBusy)
//start worker
else if(!bgWrkr.CancellationPending)
bgWrkr.CancelAsync();
Hope it helps you!
I want to leave my requests running, but no longer care about the results. I override the value of the background worker (my busy spinner is using the isBusy flag).
private void SearchWorkerCreate() {
this.searchWorker = new BackgroundWorker();
this.searchWorker.DoWork += this.SearchWorkerWork;
this.searchWorker.RunWorkerCompleted += this.SearchWorkerFinish;
}
private void SearchWorkerStart(string criteria){
if(this.searchWorker.IsBusy){
this.SearchWorkerCreate();
}
this.searchWorker.RunWorkerAsync(criteria);
this.OnPropertyChanged(() => this.IsBusy);
this.OnPropertyChanged(() => this.IsIdle);
}
May this method help someone... I've created a function to reset the backgroundworker in one method. I use it for task to do periodically.
By creating a Task, the backgroundworker is can be stopped with the CancelAsync and restarted inside the Task. Not making a Task wil start the backgroundworker again before it is cancelled, as the OP describes.
The only requirement is that your code runs through some loop, which checks the CancellationPending every period of time (CheckPerMilliseconds).
private void ResetBackgroundWorker()
{
backgroundWorker.CancelAsync();
Task taskStart = Task.Run(() =>
{
Thread.Sleep(CheckPerMilliseconds);
backgroundWorker.RunWorkerAsync();
});
}
Inside the backgroundworker I use a for-loop that checks the CancellationPending.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while(true)
{
if (backgroundWorker.CancellationPending)
{
return;
}
//Do something you want to do periodically.
for (int i = 0; i < minutesToDoTask * 60; i++)
{
if (backgroundWorker.CancellationPending)
{
return;
}
Thread.Sleep(CheckPerMilliseconds);
}
}
}