Async Await to Keep Event Firing - c#

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);
}
})
}

Related

Counting how many times an async button has been pressed (or if some operation is going on)

I know that for async operations it is possible to track its progress, but I will try that later. Now I have a simple window forms apply with a button (or a pair of buttons - the number does not matter). The buttons call an async operation
public async void Button1_Click(...)
{
await Button1_OperationAsync(...);
}
If I don't press the button nothing is going on but if I press it once the Button1_OperationAsync starts (and is awaited). (I am not really sure if to call it "a thread").
But what happens if I press the button twice? Well then before the first async operation finishes, the Button1_OperationAsync is called again. (Or if another similar button is pressed then a Button2_OperationAsync is called)
Maybe even the second async operation would finish before the first one.
What I want is a simple way of knowing if any operation is going on. So what I thought is to have a variable and increment it when an operation is called and decrement it when an operation is finished. Something like
int numberOfOps=0;
public async void Button1_Click(...)
{ numberOfOps++;
textBox1.Text="Started!";
await Button1_OpeationAsync(...);
numberOfOps--;
if(numberOfOps<=0)
{
textBox1.Text="Done!";
}
}
Be aware that this code would go in the other button (Button2) too. Or many other buttons.
I am aware that issues of synchronization might be involved. So I would appreciate advice on what I am trying to do in order to do correctly
When using async/await you're not using any threads for the UI code other than the UI-thread. It's possible that the code that gets called in the Button1_OpeationAsync method might use a separate thread, but the calling code will remain on the UI thread.
Try having a play with this code:
private int numberOfOps = 0;
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = $"Started! {++numberOfOps}";
await Task.Delay(TimeSpan.FromSeconds(5.0));
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
It works just fine. You can use the numberOfOps variable across multiple buttons.
If you'd like to make it easy to re-use the code, try it this way:
int numberOfOps = 0;
private async Task RunOp(Func<Task> op)
{
textBox1.Text = $"Started! {++numberOfOps}";
await op();
textBox1.Text = $"Started! {--numberOfOps}";
if (numberOfOps == 0)
{
textBox1.Text = "Done!";
}
}
private async void button1_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button1_OpeationAsync(...));
}
private async void button2_Click(object sender, EventArgs e)
{
await this.RunOp(() => Button2_OpeationAsync(...));
}
Have a task array, and a task object at class level:
private List<Task> tasks = new List<Task>();
private Task task = null;
In each of your click handlers do something like this:
var operationTask = SomeOperationAsync(...);
tasks.Add(operationTask);
task = Task.WhenAll(tasks);
if (task.IsCompleted)
{
// no operation is going on
tasks.Clear();
// do what ever you want to do further
}
else
{
//some operation is going on
}

WPF/C# Don't block the UI

I've an existing WPF application, which has several sections. Every section is a UserControl, that implements an interface.
The interface specify two methods: void LoadData([...]) and bool UnloadData().
Those method are called by the UI thread, so we need to do our work in backgroundworker if it's time consuming.
No problems with LoadData since we can update the UI asynchronously. The problem is with UnloadData().
This should return if we can really leave the current view.
This is computed with the current status of data(Saved/modified/Invalid):
Saved return true,
Invalid asks if you want to stay to save some
correct data or leave without saving
Modified tell you that you can
either cancel your change(return true), either continue to
edit(return false), either save you current data(return true)
The problem is with the "Modified -> Save". This is a time consuming method, so to respect the philosophy of the application, we should run this in a background thread(with a busy indicator).
But if we just launch the thread and go to the next section, it will return "true" to the method call, and we will directly launch the next view.
In my case, loading the next view before our local data is saved can be a problem.
So:
Is there a way to wait on the background thread to finish before returning "true", WITHOUT blocking the UI?
public bool UnloadData(){
if(...){
LaunchMyTimeConsumingMethodWithBackgroundWorker();
return true;//Only when my time consuming method ends
}
//[...]
}
Important EDIT
Maybe I wasn't clear enought: I know how to use a BackgroundWorker, or TPL. My problem is that the parent class(the one which call the UnloadData()" is a class that I cannot edit(for multiple reasons: It's in another DLL that will not be reloaded, it already works with 70+ userControls, all in separate projects(dll), loaded by reflection.
This wasn't my choice, I don't find it good, but I've to deal with it now. I'm mostly looking for way to make my method wait on the return of my method. I'm not sure if it is possible. But I'm looking for a workaround, it will spare me weeks of works.
Ok now I'm excited, because I think I may have discovered something on my own...
So, what you do is this: You create a DispatcherFrame, push that frame onto the Dispatcher, and in the RunWorkerCompleted you set the Continue of the Frame to false.
This is the code so far:
public void Function()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += TimeConsumingFunction;
var frame = new DispatcherFrame();
worker.RunWorkerCompleted += (sender, args) =>
{
frame.Continue = false;
};
worker.RunWorkerAsync();
Dispatcher.PushFrame(frame);
}
private void TimeConsumingFunction(object sender, DoWorkEventArgs doWorkEventArgs)
{
Console.WriteLine("Entering");
for (int i = 0; i < 3; i++)
{
Thread.Sleep(1000);
}
Console.WriteLine("Exiting");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Function();
Console.WriteLine("Returns");
}
You should implement a dependency property "IsBusy" of type bool, that you set to TRUE before starting the BackgoundWorker, and then to FALSE when the work is complete.
On the UI, you bind to that property whatever functionality you want disabled during the processing(like the button for loading the next view, etc.); or maybe showing a "Cancel" button.
You should not "wait" for the operation to complete, you can retrieve the result in an additional variable, that the BackgroundWorker will set:
BackgroundWorker _bw;
bool _returnValue = false;
private void button_Click(object sender, RoutedEventArgs e)
{ // if starting the processing by clicking a button
_bw = new BackgroundWorker();
IsBusy = true;
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
_bw.RunWorkerAsync();
}
void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
IsBusy = false;
// retrieve the result of the operation in the _returnValue variable
}
void _bw_DoWork(object sender, DoWorkEventArgs e)
{
_returnValue = UnloadData();
}
private bool UnloadData()
{
if (...)
{
LaunchTimeConsumingMethod();
return true;
}
else
return false;
//etc ...
}
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
// Using a DependencyProperty as the backing store for IsBusy. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register( ... )
You may be able to try using the new "await" features of .NET 4.5.
The await keyword allows you to await the completion of a Task object, without blocking the UI.
Try this modification:
public async bool UnloadData()
{
if(...)
{
await Task.Factory.StartNew(() =>
{
LaunchMyTimeConsumingMethod();
});
return true;//Only when my time consuming method ends
}
//[...]
}
Treat UnloadData as a async operation and let the async/await features handle both the case when it completes synchronously and when it needs to complete asynchronously:
public async Task<bool> UnloadData(){
if(...){
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool
await Task.Factory.StartNew(()=>LaunchMyTimeConsumingMethodWithBackgroundWorker());
// The return statement is the continuation and will run in the UI thread after the consuming method is executed
return true;
}
// If it came down this path, the execution is synchronous and is completely run in the UI thread
return false;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
bool result = await this.UnloadData();
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
EDIT
If you can't modify the UnloadData, then just execute it on the ThreadPool, as #BTownTKD noted:
private async void button_Click(object sender, RoutedEventArgs e)
{
// Put here your logic to prevent user interaction during the operation's execution.
// Ex: this.mainPanel.IsEnabled = false;
// Or: this.modalPanel.Visibility = Visible;
// etc
try
{
// The await keyword will segment your method execution and post the continuation in the UI thread
// The Task.Factory.StartNew will run the time consuming method in the ThreadPool, whether it takes the long or the short path
bool result = await The Task.Factory.StartNew(()=>this.UnloadData());
// Do whatever with the result
}
finally
{
// Reenable the user interaction
// Ex: this.mainPanel.IsEnabled = true;
}
}
You probably should use TPL if your framework version is 4.0:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // this will work only if you're running this code from UI thread, for example, by clicking a button
Task.Factory.StartNew(() => UnloadData()).ContinueWith(t => /*update ui using t.Result here*/, uiScheduler);
Hope this helps.
You have to implement a callback function (RunWorkerCompleted), this is called when the background worker finishes.
Check out an example here:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

C#: Measure time taken and exit function when time elapsed

I would like to run a function (funcA) and use another function (timerFunc) as a timer. If the running function (funcA) has run for 10 seconds, I would like to exit it using the timer function (timerFunc). Is this possible? Basically what I am trying to do:
void funcA() {
// check event 1
// check event 2
// check event 3
// time reaches max here! --exit--
//check event 4
}
If not, what is the best way to handle such scenarios? I have considered using a stop-watch but I'm not sure if that is the best thing to do, mainly because I do not know after what event the timeout will be reached.
Thread t = new Thread(LongProcess);
t.Start();
if (t.Join(10 * 1000) == false)
{
t.Abort();
}
//You are here in at most 10 seconds
void LongProcess()
{
try
{
Console.WriteLine("Start");
Thread.Sleep(60 * 1000);
Console.WriteLine("End");
}
catch (ThreadAbortException)
{
Console.WriteLine("Aborted");
}
}
You could put all of the events into an array of Action or other type of delegate, then loop over the list and exit at the appropriate time.
Alternately, run all of the events in a background thread or Task or some other threading mechanism, and abort/exit the thread when you get to the appropriate time. A hard abort is a bad choice, as it can cause leaks, or deadlocks, but you could check CancellationToken or something else at appropriate times.
I would create a list and then very quickyl:
class Program
{
static private bool stop = false;
static void Main(string[] args)
{
Timer tim = new Timer(10000);
tim.Elapsed += new ElapsedEventHandler(tim_Elapsed);
tim.Start();
int eventIndex = 0;
foreach(Event ev in EventList)
{
//Check ev
// see if the bool was set to true
if (stop)
break;
}
}
static void tim_Elapsed(object sender, ElapsedEventArgs e)
{
stop = true;
}
}
This should work for a simple scenario. If it's more complex, we might need more details.

Task Parallel with .net framework 4

I am studying parallelism and would like to know which way do you recommend for me to access other thead elements, for example, imagima I'll fill a combobox with some names, query the database I would do in parallel but I could not do a combobox.add (result) from within the task, which way do you recommend me?
a simple example to understand my question:
private void button1_Click (object sender, EventArgs e)
{
Task task = new Task (new Action (Count));
task.Start ();
}
void Count ()
{
for (int i = 0; i <99; i + +)
{
Thread.Sleep (1);
progressBar1.Value = i;
}
}
time to pass the value for the progressbar result in error
If you want to schedule a task that access UI controls, you need to pass the current synchronization context to the scheduler. If you do that the scheduler will make sure your task is executed on the correct thread. E.g.
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => {
// code that access UI controls
}, uiScheduler);
For more info see http://msdn.microsoft.com/en-us/library/dd997402.aspx
You cannot access controls on another thread directly. You must invoke them first. Read this article: http://msdn.microsoft.com/en-us/library/ms171728.aspx
This is about what is would look like if you took the article and translated it for your own use: (NOT TESTED)
delegate void SetProgressBarCallback();
private void SetProgressBar()
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.progressBar1.InvokeRequired)
{
SetProgressBarCallback d = new SettProgressBarCallback(SetProgressBar);
this.Invoke(d);
}
else
{
for(int i=0; i<99; i++)
{
Thread.Sleep(1);
progressBar1.Value = i;
}
}
}
Just a quick note... the UI in WinForms can only be updated from the UI thread. Perhaps you should consider using Control.Invoke to update your progressBar1.
Ryan's answer was correct but he put the sleep inside the invoke, that caused the program to hang. Here is a example that uses the same thing he did but it does not put the sleep in the invoke.
private void button1_Click (object sender, EventArgs e)
{
Task task = new Task (new Action (Count));
task.Start ();
}
void Count ()
{
for (int i = 0; i <99; i + +)
{
Thread.Sleep (1);
if(progressBar1.InvokeRequired)
{
int j = i; //This is required to capture the variable, if you do not do this
// the delegate may not have the correct value when you run it;
progressBar1.Invoke(new Action(() => progressBar1.Value = j));
}
else
{
progressBar1.Value = i;
}
}
}
You must do the int j = i to do variable capture, otherwise it could bring up the wrong value for i inside the loop.

Restart background worker

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);
}
}
}

Categories

Resources