I have a scenario. (Windows Forms, C#, .NET)
- There is a main form which hosts some user control.
- The user control does some heavy data operation, such that if I directly call the
UserControl_Load
method the UI become nonresponsive for the duration for load method execution. - To overcome this I load data on different thread (trying to change existing code as little as I can)
- I used a background worker thread which will be loading the data and when done will notify the application that it has done its work.
- Now came a real problem. All the UI (main form and its child usercontrols) was created on the primary main thread. In the LOAD method of the usercontrol I'm fetching data based on the values of some control (like textbox) on userControl.
The pseudocode would look like this:
CODE 1
UserContrl1_LoadDataMethod()
{
if (textbox1.text == "MyName") // This gives exception
{
//Load data corresponding to "MyName".
//Populate a globale variable List<string> which will be binded to grid at some later stage.
}
}
The Exception it gave was
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.
To know more about this I did some googling and a suggestion came up like using the following code
CODE 2
UserContrl1_LoadDataMethod()
{
if (InvokeRequired) // Line #1
{
this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
return;
}
if (textbox1.text == "MyName") // Now it wont give an exception
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be binded to grid at some later stage
}
}
BUT BUT BUT... it seems I'm back to square one. The Application again become nonresponsive. It seems to be due to the execution of line #1 if condition. The loading task is again done by the parent thread and not the third that I spawned.
I don't know whether I perceived this right or wrong. I'm new to threading.
How do I resolve this and also what is the effect of execution of Line#1 if block?
The situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.
So only accessing the value so that the corresponding data can be fetched from the database.
You only want to use Invoke or BeginInvoke for the bare minimum piece of work required to change the UI. Your "heavy" method should execute on another thread (e.g. via BackgroundWorker) but then using Control.Invoke/Control.BeginInvoke just to update the UI. That way your UI thread will be free to handle UI events etc.
See my threading article for a WinForms example - although the article was written before BackgroundWorker arrived on the scene, and I'm afraid I haven't updated it in that respect. BackgroundWorker merely simplifies the callback a bit.
You need to look at the Backgroundworker example:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Especially how it interacts with the UI layer. Based on your posting, this seems to answer your issues.
Same question : how-to-update-the-gui-from-another-thread-in-c
Two Ways:
Return value in e.result and use it to set yout textbox value in backgroundWorker_RunWorkerCompleted event
Declare some variable to hold these kind of values in a separate class (which will work as data holder) . Create static instance of this class adn you can access it over any thread.
Example:
There are two options for cross thread operations.
and second one is to use
Control.InvokeRequired is only useful when working controls inherited from Control class while SynchronizationContext can be used anywhere. Some useful information is as following links
Cross Thread Update UI | .Net
Cross Thread Update UI using SynchronizationContext | .Net
A new look using Async/Await and callbacks. You only need one line of code if you keep the extension method in your project.
You can add other things to the Extension method such as wrapping it in a Try/Catch statement, allowing caller to tell it what type to return after completion, an exception callback to caller:
Adding Try Catch, Auto Exception Logging and CallBack
The cleanest (and proper) solution for UI cross-threading issues is to use SynchronizationContext, see Synchronizing calls to the UI in a multi-threaded application article, it explains it very nicely.