Robby Smet
Robby Smet

Reputation: 4661

Stop running backgroundworker and start new one.

I have a window with a calendar and datagrid.
When the user selects a new date in the calendar I want to query the database for calls made on that date.

public HistoryDialog()
{
    InitializeComponent();

    worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

    HistoryGrid.SelectionChanged += new SelectionChangedEventHandler(HistoryGrid_SelectionChanged);
}

void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
    startDate = calendar.SelectedDates.OrderBy(x => x.Date).FirstOrDefault();
    endDate = calendar.SelectedDates.OrderByDescending(x => x.Date).FirstOrDefault();

    if (endDate != startDate)
        SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate) + " - " + String.Format("{0:d MMMM}", endDate);
    else
        SelectedDateTextBlock.Text = String.Format("{0:d MMMM}", startDate);

    SearchInDatabase();
}

private void SearchInDatabase()
{
    if (worker.IsBusy)
        worker.CancelAsync();

    worker.RunWorkerAsync();
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    if ((worker.CancellationPending == true))
    {
        e.Cancel = true;
        return;
    }

    var CallLog = ct.GetCalllogs(startDate, endDate, userID);   // Database query
    e.Result = CallLog;
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    IList CallLog = e.Result as IList;

    foreach (CalllogInfo callInfo in CallLog)
    {
        chvm.CallHistoryList.Add(callInfo);
    }
}

But when the user selects a new date while the backgroundworker is still running my program crashes.

How can I stop a running background worker and start a new one ?

Upvotes: 3

Views: 10077

Answers (4)

Alex
Alex

Reputation: 23300

Disallowing selections while the worker is doing work would remove the issue without the need to tackle it:

void calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
    // .. cut ..
    // disable the calendar and go on
    calendar.IsEnabled = false;
    SearchInDatabase();
}

private void SearchInDatabase()
{
    // No longer needed: selection processing is now "atomic"
    // if (worker.IsBusy) worker.CancelAsync();
    worker.RunWorkerAsync();
}

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // .. cut ..
    calendar.IsEnabled = true;        
}

Or you could bind worker.IsBusy to calendar.IsEnabled to have it handled "automagically" (while still not having to worry about concurrent executions of the worker method).

Upvotes: 0

Markus
Markus

Reputation: 22456

In addition to the other answers, I want to add the following. The following block of code is responsible for raising the error:

private void SearchInDatabase()
{
    if (worker.IsBusy)
        worker.CancelAsync();

    worker.RunWorkerAsync();
}

As you call CancelAsync, execution of your code is continued immediately without waiting until the BackgroundWorker is really stopped. This is the reason why RunWorkerAsync is called while the BackgroundWorker is still running resulting in the error you describe. You should change your code as follows:

private void SearchInDatabase()
{
    if (worker.IsBusy)
        worker.CancelAsync();
    while(worker.IsBusy)
        System.Threading.Thread.Sleep(100);

    worker.RunWorkerAsync();
}

This assures that the BackgroundWorker finishes its work before starting a new one. In addition, you need to enhance your DoWork-method to check the CancellationPending property more often in order to really stop the BackgroundWorker soon after a cancellation request.

Upvotes: 1

Nico
Nico

Reputation: 12683

Now I see you have set the WorkerSupportsCancellation property to true.

Now this doesnt actually cancel your BackgroundWorker it simply allows you to call the CancelAsync() method.

What you need to do is in your method processing periodically check to ensure the working is not pending cancellation from the CancellationPending property. As you check this property when you find it true you can set the Cancel property of the event arguments to true. This will then be available in your RunWorkerCompleted event. At this point (in your RunWorkerCompleted event handler) you can then restart the BackgroundWorker.

Here is an example using very basic background worker that supports cancellation and responds to the cancel request.

public MainWindow()
{
    InitializeComponent();
    this.DataContext = dataModel;

    worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += (o, e) =>
    {
        //do a long running task
        for (int i = 0; i < 10; i++)
        {
            System.Threading.Thread.Sleep(500);
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }

    };
    worker.RunWorkerCompleted += (o, e) =>
    {
        if (e != null && e.Cancelled)
        {
            startTheWorker();
            return;
        }
        //TODO: I AM DONE!
    };
}

BackgroundWorker worker;

private void Button_Click(object sender, RoutedEventArgs e)
{
    if (worker != null && worker.IsBusy)
        worker.CancelAsync();
    else if(worker != null && !worker.CancellationPending)
        startTheWorker();
}

void startTheWorker()
{
    if (worker == null)
        throw new Exception("How come this is null?");
    worker.RunWorkerAsync();
}

As you can see we are having to do all the work of actually cancelling the worker. Hope this helps.

Upvotes: 4

iMax
iMax

Reputation: 31

Your problem is coming from the cancellation. When you are in ct.GetCalllogs your code is blocked and doesn't support cancellation. In ct.GetCalllogs you should verify that the backgroundworker isn't in cancel state and then start again with the new value.

Here is an element of response how to stop backgroundworker correctly

Upvotes: 0

Related Questions