Controls not loading when multithreading a form - c#

I have a C# WinForms application. When the main form loads, two other forms are loaded and hidden as I need them to run in the background. I keep 2 global variables pointing to them in order to be able to access them. I want to load them on a separate thread to reduce the loading time. For this, I have the following code in the main form's constructor:
if (!IsHandleCreated)
{
CreateHandle();
}
//Start the license manager using a separate thread.
new Thread(delegate()
{
BeginInvoke(new Action(() =>
{
GlobalVariables.licenseManagerWindow = new LicenseManager();
GlobalVariables.licenseManagerWindow.Show();
}));
bt_Licenses.BeginInvoke(new Action(() =>
{
bt_Licenses.Enabled = true;
}));
}).Start();
//Start the employee app manager using a separate thread.
new Thread(delegate()
{
BeginInvoke(new Action(() =>
{
GlobalVariables.employeeAppManagerWindow = new ManageEmployeeApp();
GlobalVariables.employeeAppManagerWindow.Show();
}));
bt_Employees.BeginInvoke(new Action(() =>
{
bt_EmployeeApp.Enabled = true;
}));
}).Start();
The problem is that instead of showing the main form and updating the buttons after each form is loaded, the main form looks like this until both of the other forms are loaded:
As you can see, the controls don't load and they show transparently. Eventually they all load fine when the 2 forms load. If I comment out the code above, the main form loads perfectly fine. I obviously don't want the user to see this skeleton while the forms load, but to see the controls. What am I doing wrong?

Try using background workers instead of threads. use the report progress / runworkercomplete events to write the results to your form. This prevents your UI waiting with the draw until the threads are completed and makes it that you don't have to manually handle the invokes of controls.
documentation and examples can be found here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx

Instead of loading the two hidden forms in the main form's constructor, do it in the main form's "Load" event handler.
Windows forces you to load the hidden forms through the main thread (that's what BeginInvoke does), but you can minimize the impact to the main thread by having the hidden forms kick off the long-running network communication on a background thread from the hidden forms' "Load" event.

Related

Wait screen during rendering UIElement in WPF

I have a WPF application using PRISM. I have a login screen and on successfuly logging in a new view containing a TileListView with many items appears. This needs over 10 seconds to render because control has to calculate a lot etc. All this uses the UI thread by standard behaviour, because rendering in WPF is done in UI thread. Is it possible to display a WaitControl like a spinner or just a simple animation in seperate window or something like this? Now to animation stops at the time, the control is rendered in UI Thread.
You could create a new window that launches in a separate thread. Please refer to the following blog post for an example of how to do this.
Launching a WPF Window in a Separate Thread: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
Then you just start this thread that displays the window just after you have validated the credentials and just before your heavy rendering is about to begin.
This is probably the best thing you can do and it should also be a pretty easy thing to implement.
Edit - Including code from above link to be recorded for posterity:
using System.Threading;
using System.Windows.Threading;
void LoadWindowInThread()
{
Thread newWindowThread = new Thread(new ThreadStart(() =>
{
// Create our context, and install it:
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
// Create and configure the window
Window1 tempWindow = new Window1();
// When the window closes, shut down the dispatcher
tempWindow.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
tempWindow.Show();
// Start the Dispatcher Processing
Dispatcher.Run();
}));
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
// Start the thread
newWindowThread.Start();
}
you can use SplashScreen to display untill the background process is completed. Refer this Splash Screen in WPF

Closing Form from inside an Invoke

Closing a form from inside an Invoke like this:
Invoke(new Action(() => {
Close();
MessageBox.Show("closed in invoke from another thread");
new Form1();
}));
throws an exception as soon as the form is closed:
Invoke or BeginInvoke cannot be called on a control until the window
handle has been created.
But only on NET 4.0. On NET 4.5 no exceptions are thrown.
Is this expected behavior? How should I go about it?
That's because Close method closes the form and destroys it's handle and then the MessageBox is invoked in the Closed form with no handle, so the error message shows up.
I don't understand your purpose, but you should either move the code after Close out of invoke, or move the Close after them. For example:
Invoke(new Action(() => {
Hide();
MessageBox.Show("closed in invoke from another thread");
new Form1();
Close();
}));
Edit:
MSDN note about Control.Invoke:
The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet. If no appropriate handle can be found, the Invoke method will throw an exception. Exceptions that are raised during the call will be propagated back to the caller.
If you start a thread during initialisation, you do not know how far the initialisation has gone in another thread.
You notice differences in behavior on different .Net versions, but you cannot be sure about the order of things on different machines.
I have solved a lot of threading issues in Windows forms using my own messagepump, using a Queue and a normal Timer control:
Add a timer control to your form, with a small interval (250 ms)
Add a Queue to your form.
Let the timer event dequeue the actions, and execute it.
Add Actions to the queue during initialisation or even other background jobs.
Using this approach will issues with background jobs during initialisation, but also during closing/disposing of the form, since the timer will only trigger if the form is fully functional.

How to run an animated SplashScreen at the same time as a Bootstrapper on STA threads

Background:
I am working on a WPF application that uses the MEF framework to automatically link the necessary libraries and components for the app. With people adding their own components, the loading takes anywhere from 0-2 seconds to 6-7 seconds on slower machines. For this, I think a nice splash screen to let the user know the application is loading would be necessary. Ideally the splash screen would display an animated progress bar as well as a status text field describing which components are being loaded.
Problem:
I designed a splash screen window that is called right at the start of the application (OnStartup), followed by the loading of the MEF bootstrapper. Problem is of course, the window does not animate because it is on the same thread as the MEF bootstrapper loading. I tried putting the bootstrapper on a separate thread but it complains that it is not an STA thread. Even on an STA thread it still didn't like it and it threw errors when trying to load the main app window. I can't put the splash screen window itself on a separate thread because then I don't have access to it, and it has to be an STA thread also because its a UI component. (Realized this is untrue, I can talk to the thread)
Update
I found a solution where I kept the splash screen window in a separate STA thread. Thank you everyone who replied for pointing me in the right direction:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Dispatcher threadDispacher = null;
Thread thread = new Thread((ThreadStart)delegate
{
threadDispacher = Dispatcher.CurrentDispatcher;
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(threadDispacher));
loadingWindow = new LoadingWindow();
loadingWindow.Closed += (s, ev) => threadDispacher.BeginInvokeShutdown(DispatcherPriority.Background);
loadingWindow.Show();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
var bootstrapper = new Bootstrapper();
bootstrapper.Run();
if (threadDispacher != null)
{
threadDispacher.BeginInvoke(new Action(delegate { loadingWindow.Close(); }));
}
}
You were on the right way moving the bootstrapper to a thread of its own.
What you should do, however is make sure that the portions that require being executed on the UI thread are also invoked on the UI thread.
So when the MEF bootstrapper is done, you can invoke the hiding of the splash window and opening of the main window on the main thread, even from the MEF's thread.
A way to get your message across to the main thread is by dispatcher

how to solve this problme == Cross-thread operation not valid: Control 'frm_proc' accessed from a thread other than the thread it was created on

i have a from On this form i have a button...
on the click event write this code
_BackgroundWorker.RunWorkerAsync(new MethodInvoker(() =>
{
progressBar1.BeginInvoke(new MethodInvoker(() => progressBar1.Visible = true));
Print formp = new Print();
formp.ShowDialog();
this.Hide();
}));
but i recived error on this.Hide() ...
how can i solve
You can´t access controls from a thread other than the GUI thread, you´ll need to use InvokeRequired to check if such is the case. If so, you'll need to invoke your call on the GUI thread.
It's hard to tell without the context, but maybe you should ask yourself if you need to run this code asynchronously.
Try this:
this.Invoke(new System.Action(()=>this.Hide()));
You are not allowed to access UI controls (or their parent form) from a thread other than the one running that form's message pump (commonly referred to as it's UI thread). In this case, this refers to the form itself.

New form on a different thread

So I have a thread in my application, which purpose is to listen to messages from the server and act according to what it recieves.
I ran into a problem when I wanted to fire off a message from the server, that when the client app recieves it, the client app would open up a new form. However this new form just freezes instantly.
I think what's happening is that the new form is loaded up on the same thread as the thread listening to the server, which of course is busy listening on the stream, in turn blocking the thread.
Normally, for my other functions in the clients listening thread, I'd use invokes to update the UI of the main form, so I guess what I'm asking for is if here's a way to invoke a new form on the main form.
I assume this is Windows Forms and not WPF? From your background thread, you should not attempt to create any form, control, etc or manipulate them. This will only work from the main thread which has a message loop running and can process Windows messages.
So to get your code to execute on the main thread instead of the background thread, you can use the Control.BeginInvoke method like so:
private static Form MainForm; // set this to your main form
private void SomethingOnBackgroundThread() {
string someData = "some data";
MainForm.BeginInvoke((Action)delegate {
var form = new MyForm();
form.Text = someData;
form.Show();
});
}
The main thing to keep in mind is that if the background thread doesn't need any response from the main thread, you should use BeginInvoke, not Invoke. Otherwise you could get into a deadlock if the main thread is busy waiting on the background thread.
You basically gave the answer yourself - just execute the code to create the form on the GUI thread, using Invoke.

Categories