views:

286

answers:

3

I've got two list boxes, one a master, the other a child. When the index changes on the master, the child listbox gets filled appropriately with records relating to the master. My problem is coming up when one master takes a long time to get all the records and before it has completed getting the records, a user clicks a different master that takes less time to fill. What happens is that eventually, the master that was taking longer fills in the child list box even though the user isn't on that master anymore.

I've been using BackgroundWorker threads to do the filling.

bw_LoadAll.DoWork += new DoWorkEventHandler(bg_LoadAllWork);
bw_LoadAll.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_LoadAllWorkCompleted);
bw_LoadAll.WorkerSupportsCancellation = true;

I subscribed to the SelectedIndexChanged event for the master and I set the canceled equal to true:

bw_LoadAll.CancelAsync();

Here is the code in the DoWork method:

List<object> s = Repository.Instance().LoadAll();
if (!bw_LoadAll.CancellationPending) {
    e.Result = s;
} else {
    e.Cancel = true;
}

But for some reason, the code for the worker completed keeps getting called. Here is the worker completed code:

if (!e.Cancelled) {
    ddl.DataSource = e.Result;
    ddl.DisplayMember = "QuickName";
    ddl.ValueMember = "ID";
}

Is there something else I have to do to cancel this thread from returning?

A: 

From CodeProject in your do_work function you need to check for CancellationPending on the worker thread then set the DoWorkEventArgs.Cancel variable to true.

AvatarOfChronos
That works if I take my time between clicking the master items, but if I do a click and then followed up by a click on another, it doesn't seem to get set in time. I'll update the code to show what I'm doing in the work method.
Josh
A: 

It's been a while since I've used a BackgroundWorker, but if I memory serves, when you call bw_LoadAll.CancelAsync, there's no actual abortion of your LoadAllWork method unless LoadAllWork checks bw_LoadAll.CancelationPending.

Confirmed by http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx : "CancelAsync submits a request to terminate the pending background operation and sets the CancellationPending property to true.

When you call CancelAsync, your worker method has an opportunity to stop its execution and exit. The worker code should periodically check the CancellationPending property to see if it has been set to true."

So, in your SelectedIndexChanged event handler, when you call bw_LoadAll.CancelAsync(), it's setting bw_LoadAll.CancelationPending to true, but it's not actually aborting the LoadAllWork method. The slow-loader is going to still finish.

Yoopergeek
+4  A: 

Your method, bg_LoadAllWork, should be defined as:

void bg_LoadAllWork(object sender, DoWorkEventArgs e)
{
    // Do your work here...
}

Inside of bg_LoadAllWork, you need to check for e.CancellationPending, and if it's true, set e.Cancel = true;

This last part is important - if you never set e.Cancel, then your "e.Cancelled" will never equal true. The call to CancelAsync doesn't actually cancel anything - it's more like "Request that the background work cancel itself" - you have to put the logic in place to cause the cancellation.

Reed Copsey
CancellationPending isn't exposed on DoWorkEventArgs, rather on BackgroundWorker, which would be <sender> parameter in this example.
Sherlock