Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating controls in a non-UI thread

I have a sort of plug-in model in which various complex user controls are stored in DLLs and loaded and instantiated at run time using

Activator.CreateInstanceFrom(dllpath, classname).

Since I'm loading quite a few of these I wanted to do it in the background, keeping my UI responsive, by creating a new thread to do the loading. The controls are then parented to the main form and displayed when needed.

This seems to work fine - until I try to set any property on any nested control on one of these user controls, e.g. in the event handler of a button, which throws a cross threading exception. I do realize I could avoid this by checking InvokeRequired every time I access a property, but I'd rather not have to worry about that when writing the code for the user controls (especially since there are others writing these bits of code too who might not always remember).

So my question is, is there any safe way to do what I'm attempting, or how should I best go about loading these controls in the background? Or is it basically impossible and do I have to stick to the main thread for creating controls?

I hope that the information I've provided is enough to make my situation clear; if not I'd be glad to elaborate and provide code samples.

like image 931
Tom Juergens Avatar asked Jun 24 '09 20:06

Tom Juergens


3 Answers

The code sample in this answer provides an elegant way around this problem.

Code quoted from that answer:

public void UpdateTestBox(string newText)
{
    BeginInvoke((MethodInvoker) delegate {
        tb_output.Text = newText;
    });        
}

...although in your case you would want to call BeginInvoke on the controls themselves:

public void UpdateTestBox(string newText)
{
    tb_output.BeginInvoke((MethodInvoker) delegate {
        tb_output.Text = newText;
    });        
}
like image 32
Fredrik Mörk Avatar answered Sep 21 '22 22:09

Fredrik Mörk


Is fine to load the DLLs and create the control objects in the background, but the control has to be added to the form in the main thread and all user interaction as well as any programmatic change of control properties (after it was created) has to occur in the main thread. There is simply no way around this, as you probably already know. Now unless the load of those DLLs and control creation takes ages, I see no reason to do it in separate background threads.

If certain actions performed by the controls are blocking the UI and you want them to occur in the background, this is a different story, and you better consider each action on an individual basis and create explicit methods to communicate between UI thread and background threads.

Attempting to do a generic one-fits-all framework that works the same in UI thread mode and in background mode and simply checks the InvokeRequired results sometimes in worse performance (as all threads are blocked in the Invoke) or even in (undetected) deadlocks as soon as the application reaches a reasonable complexity. Also doing all updates async in BeginInvoke, w/o considering each method individually, can result in data consistency problems (ie. controls may update themselves to states back in time due to reversing of the invocation order between threads).

like image 192
Remus Rusanu Avatar answered Sep 23 '22 22:09

Remus Rusanu


Not knowing the details of the InvokeRequired mechanism, I have been experimenting a little and afaik you can set most properties in a Thread as long as it has not been Parented (ie added to some Control.Controls property).

So you should be able to prepare your controls, store them in a list and attach them to the main UI in an Invoked method.


Edit: I don't think it matters which Thread created a Control. So the normal rules should apply, ie you can only work with controls form the main Windows thread. And I think the criterion is HandleCreated rather than Parented. As long as that hasn't happened you get a little respite.

like image 25
Henk Holterman Avatar answered Sep 20 '22 22:09

Henk Holterman