467,223 Members | 1,377 Online
Bytes | Developer Community
Ask Question

Home New Posts Topics Members FAQ

Post your question to a community of 467,223 developers. It's quick & easy.

Multithreading a databound app?

I am looking for a resource to learn how to do multithreading in a data
bound app. So far, I have figured out that I can't update a databound object
on a worker thread, because that triggers a cross-thread call to the UI
control that it is bound to.

I have created a simple demo to explore the problem. The demo has a
dataGridView control, a bindingSource control, a backgroundWorker control
and a Go button. The grid is bound via the BindingSource to WidgetList,
which is a llist of WidgetItem objects derived from BindingList<T>.
WidgetList has one method, PopulateList(), which simply adds 100 WidgetItems
to itself, very slowly.

At run time, the WidgetList is instantiated and bound to the bindingSource
control in the FormLoad method. The backgroundWorker's DoWork event handler
calls the WidgetList's PopulateList() method.

I had hoped I could populate the list on a worker thread without triggering
a UI control call. I'm getting a cross-thread UI call exception, even when I
suspend binding on the bindingSource and the dataGridView.

The other demos I have downloaded all seem to fudge the issue by creating an
object in the worker thread, then binding it after the thread completes.
That won't work for me, since my production app needs to transform the same
collection in a series of operations.

Can a databound object be updated on a worker thread? If so, how is it done?
Has anyone seen any good articles that discuss the issue? Thanks.

--
David Veeneman
Foresight Systems
Aug 1 '06 #1
  • viewed: 5411
Share:
6 Replies
David,

No, it can't be done. You should probably unbind the object itself,
perform your operation, and then bind the object back.

Hop ethis helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"David Veeneman" <da****@nospam.com (domain is my last name)wrote in
message news:%2****************@TK2MSFTNGP03.phx.gbl...
>I am looking for a resource to learn how to do multithreading in a data
bound app. So far, I have figured out that I can't update a databound
object on a worker thread, because that triggers a cross-thread call to the
UI control that it is bound to.

I have created a simple demo to explore the problem. The demo has a
dataGridView control, a bindingSource control, a backgroundWorker control
and a Go button. The grid is bound via the BindingSource to WidgetList,
which is a llist of WidgetItem objects derived from BindingList<T>.
WidgetList has one method, PopulateList(), which simply adds 100
WidgetItems to itself, very slowly.

At run time, the WidgetList is instantiated and bound to the bindingSource
control in the FormLoad method. The backgroundWorker's DoWork event
handler calls the WidgetList's PopulateList() method.

I had hoped I could populate the list on a worker thread without
triggering a UI control call. I'm getting a cross-thread UI call
exception, even when I suspend binding on the bindingSource and the
dataGridView.

The other demos I have downloaded all seem to fudge the issue by creating
an object in the worker thread, then binding it after the thread
completes. That won't work for me, since my production app needs to
transform the same collection in a series of operations.

Can a databound object be updated on a worker thread? If so, how is it
done? Has anyone seen any good articles that discuss the issue? Thanks.

--
David Veeneman
Foresight Systems

Aug 1 '06 #2
See my post a few days ago; in the end I wrote a ThreadedBinding
(equiv. to Binding) to help with this issue; following link may work:

http://groups.google.com/group/micro...078656d6f1ee1f

Or: Tues, Jul 25 2006 9:31 am, by myself, titled "Threaded Bindings
don't update"

Hope it helps,

Marc

Aug 1 '06 #3
Thanks--but how do I unbind the object? I've tried setting the DataSource of
the bindingSource and the dataGridView to null, but I'm still getting the
same illegal cross-thread call exception on the DGV.

Thanks agsin for your help.

--
David Veeneman
Foresight Systems
Aug 1 '06 #4
I found my answer--see below. Thanks again for your help.
Aug 1 '06 #5
I found my answer. Thanks to those who responded to my original question!

As Nicholas Paladino pointed out, you have to disconnect a control from a
bound object before you update the object on a worker thread. Then update
it, and when the worker thread completes, rebind the resulting object.

In my demo project, I am using a dataGridView control bound to a
bindingSource control. The binding source control is bound to a WidgetList.
Here's how I fixed my code:

(1) I threw out the WidgetList member variable. Instead, I created it as a
local variable in the FormLoad event handler and set it as the
bindingSource.DataSource.

private void this_Load(object sender, EventArgs e)
{
WidgetList widgetList = new WidgetList();
widgetListBindingSource.DataSource = widgetList;
}

(2) In the button handler that triggers the background worker, I get the
WidgetList from the bindinmgSource, then clear bindingSource.DataSource. I
also show a label and progress bar to show the progress of the operation. I
pass the WidgetList to the worker thread as the argument to
RunWorkerAsync():

private void toolBarButtonGo_Click(object sender, EventArgs e)
{
statusStripLabel1.Visible = true;
statusStripProgressBar1.Visible = true;
WidgetList widgetList = (WidgetList)widgetListBindingSource.DataSource;
widgetListBindingSource.DataSource = null;
backgroundWorker1.RunWorkerAsync(widgetList);
}

(3) The backgroundWorker.DoWork event fires. I get the WidgetList from the
event args and pass it to my worker method. The worker method returns the
loaded WidgetList, which I put into the event args:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Call method to be run on worker thread
WidgetList argumentList = (WidgetList)e.Argument;
WidgetList resultList = this.PopulateList(argumentList);
e.Result = resultList;
}

Here is the worker method from my demo:

private WidgetList PopulateList(WidgetList widgetList)
{
for (int i = 0; i < 100; i++)
{
// Add new widget to the list
WidgetItem widget = new WidgetItem(i);
widgetList.Add(widget);

// Pause to simulate slow process
Thread.Sleep(100);

// Report progress
backgroundWorker1.ReportProgress(i);
}
return widgetList;
}

(4) When the worker thread completes, I get the WidgetList from the
backgroundWorker.RunWorkerCompleted event args and re-bind the binding
source to it. I also hide the progress bar and label:

private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
WidgetList widgetList = (WidgetList)e.Result;
widgetListBindingSource.DataSource = widgetList;
statusStripLabel1.Visible = false;
statusStripProgressBar1.Visible = false;
}

(5) While the operation is in progress, my worker method periodically
instructs the background worker to report its progress (see worker method
above).

(6) The backgroundWorker.ProgressChanged event fires each time
backgroundWorkerReportProgress() is called. It simply updates the progress
bar:

private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
statusStripProgressBar1.Value = e.ProgressPercentage;
}

The overall result is that I have preserved the identity of my WidgetList
and updated it on a worker thread, without making any significant changes to
my data binding scheme.

--
David Veeneman
Foresight Systems


Aug 1 '06 #6
An update--I subsequently discovered that SuspendBinding() and
ResumeBinding() do eliminate the cross-thread control call problem in my
original message. You don't need to set the DataSource to null.

The trick is to call SuspendBinding() before calling
BackgroundWorker.RunWorkerAsync(), and to call ResumeBinding() after the
asynchronous process completes-- in the RunWorkerCompleted() event handler
or later. Here is how I did the second step:

// Re-bind binding source control to updated widget list
widgetListBindingSource.DataSource = newWidgetList;
widgetListBindingSource.ResumeBinding();

The newWidgetList object was returned to me in the
RunWorkerCompletedEventArgs.Result property:

WidgetList widgetList = (WidgetList)e.Result;
--
David Veeneman
Foresight Systems

Aug 3 '06 #7

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

47 posts views Thread by mihai | last post: by
16 posts views Thread by Robert Zurer | last post: by
5 posts views Thread by sarge | last post: by
9 posts views Thread by tommy | last post: by
2 posts views Thread by Rich | last post: by
55 posts views Thread by Sam | last post: by
5 posts views Thread by sandy82 | last post: by
7 posts views Thread by Ray | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.