views:

418

answers:

5

I'm trying to make some sense out of an application Ive been handed in order to track down the source of an error. Theres a bit of code (simplified here) which creates four threads which in turn populate list views on the main form. Each method gets data from the database and retrieves graphics from a resource dll in order to directly populate an imagelist and listview.

From what Ive read on here (link) updating UI elements from any thread other than the UI thread should not be done, and yet this appears to work?

Thread t0 = new Thread(new ThreadStart(PopulateListView1));
t0.IsBackground = true;
t0.Start();

Thread t1 = new Thread(new ThreadStart(PopulateListView2));
t1.Start();

Thread t2 = new Thread(new ThreadStart(PopulateListView3));
t2.Start();

Thread t3 = new Thread(new ThreadStart(PopulateListView4));
t3.Start();

The error itself is a System.InvalidOperationException "Image cannot be added to the ImageList." which has me wondering if the above code is linked in some way.

Iis this method of populating the UI recommended and if not what are the possible complications resulting from it?

Update:

I may have given some misinformation by referring to a 'form'. The application is a windows forms application but the code is from a plugin application based on a usercontrol. The threads are created inside an initialise method publically exposed by this control. The listviews etc are also part of this plugin usercontrol.

+4  A: 
  • DO NOT USE threads for that - if you have to do that async, use WOrkItems on a THreadPool. Thread usage in general should be reseved for long running items - a THreadPool or the new .NET 4.0 tasks API are way better suited for that.

  • UI elements should only be namipulated from the element creation thread. It "works" or not depending on what version of the .net framework you use or what the control really is if you break this.

TomTom
As I was writing roughly the same thing, I gave up. Here is a link about this: http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
Timores
+2  A: 

Never update UI from a worker thread. Program may work sometimes, but this is undefined behavior. Add this line to the program initialization code:

Control.CheckForIllegalCrossThreadCalls = true;

After this every incorrect UI update attempt fails, allowing to fix all errors in the code.

Alex Farber
+2  A: 

As others have stated, you cannot update your UI from any thread other than the one it was created by.

If a thread wants to update the UI, it needs to invoke a method on the UI control on the same thread that created it using BeginInvoke.

PS: I am assuming when you say UI, you mean a WindowsForms and not WPF. I have used the above solution successfully in WinForms.

Update: Here are a couple of articles that explain the concept in-depth: * Threading in Windows Forms * WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred

Also, related question from SO: http://stackoverflow.com/questions/10349/in-winforms-why-cant-you-update-ui-controls-from-other-threads

hitec
I do indeed mean windows forms, specifically a usercontrol on a windows form. I should have included this information originally and have now updated the question.
Andrew
A: 

If this is done because getting the values fr the listviews takes time, then get the values in a background worker and then use the main thread to bind the data to the listview.

Roopesh Shenoy
+2  A: 

Within your threads methods, such as DoWork() for the BackgroundWorker class, for example, you will need to instiate a delegate method to populate your UI control. Then, verifying whether your UI control requires to be invoked (InvokeRequired property), then invoking it when it'srequired to.

private delegate IList<MyObject> PopulateUiControl();

private void myThread_DoWork(object sender, DoWorkEventArgs e) {
    PopulateUiControl myDelegate = FillUiControl;

    while(uiControl.InvokeRequired)
        uiControl.Invoke(myDelegate);
}

private IList<MyObject> FillUiControl() {
    uiControl.Items = myThreadResultsITems;
}

It is not a precise working code, as I can't take the time to do the research, etc. but this shall put you in the path to succeed.

In the end, I agree with the others, try to avoid such things in the future, as it can get tricky to debug or reveal some strange behaviour. Perhaps .NET 4 has some improvements on the topic as Microsoft has worked hard to make parallelism easy for the use of multicore processors for developers.

Will Marcouiller