Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 901
Robby Smet Avatar asked Nov 20 '25 05:11

Robby Smet


2 Answers

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.

like image 58
Nico Avatar answered Nov 21 '25 18:11

Nico


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.

like image 43
Markus Avatar answered Nov 21 '25 19:11

Markus