at the moment I got this
UPDATED: Thanks for all the answers.
private void Form1_Load(object sender, EventArgs e)
{
//hide() doesnt help
Thread init = new Thread(InitApplication);
init.IsBackground = true;
init.Start();
}
InitApplication takes at least 5+ seconds to complete and write in all the settings. I want my splashScreen to stay up until then.
ADDED:
private readonly SplashScreen _splash;
public Form1(SplashScreen splashy)
{
_splash = splashy;
InitializeComponent();
}
and I got
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen splashy = new SplashScreen();
splashy.Show();
Application.DoEvents();
Application.Run(new Form1(splashy));
}
It is doing what its suppose to do, However, Now I see form1 on top of the splashform. Where can I add the hide and show method so it only the splash screen is shown and form1 popups when its fully loaded.
Try loading the splash screen from the UI thread, then use a background worker to execute InitApplication(). The background worker can communicate progress to the UI thread.
BackgroundWorker Class on MSDN
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.
You can use Async Await, Background worker or Thread for initializing your app etc. These are written in the sequence of easy to use and the general pattern being followed.
Use a normal windows form and there use progress bar, gif image etc. what you like most there. On the form load or form shown event start the background task and when it finishes, close the normal form and load your main application form.
If your InitApplication has any call that deals with GUI, you will have to change that call and Invoke the desired action on GUI thread.
Related
I have a main UI that doing some time-consuming work. When it is executing, I would like to open a second form with a progress bar (marquee style) to indicate "working on it".
I have seen people putting the time-consuming task in the BackgroundWorker, however, I would like to run in the main UI thread.
The time-consuming task will be executed in MainForm. I would like to reuse the progress bar for various process, so I am writing a second form ProgressBarForm with BackgroundWorker in it, that would start the _mainWork at the same time as showing progress bar, and will stop and close the ProgressBarForm when _mainWork is done.
Because forms are modals, I am thinking of showing ProgressBarForm in the BackgroundWorker in order not to block MainForm.
Please note that I am not running mainForm in BackgroundWorker. My backgroundWorker just show the form and perhaps report a timer.
public partial class ProgressBarFom : UControl
{
public delegate void MainWork();
private MainWork _mainWork;
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//progressBar.Hide();
this.OnClose(sender, e);
//
backgroundWorker.Dispose();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//show this ProgressBarForm
this.ShowDialog();
//stop backgroundWorker
//calling this.Close() in RunWorkerComplete
if (backgroundWorker.CancellationPending == true)
{
e.Cancel = true;
return;
}
}
public void CallProgressBar(object sender, EventArgs e)
{
//progressBar.Show();
backgroundWorker.RunWorkerAsync();
_mainWork();
if (backgroundWorker.IsBusy)
backgroundWorker.CancelAsync();
}
}
In MainForm, I am passing mainwork and call ExecWithProgressBar
private void ExecWithProgressBar()
{
ProgressBarFom .MainWork mainWork = new ProgressBarFom .MainWork(ProgressBarMainWork);
ProgressBarFom prBar = new ProgressBarFom (mainWork);
prBar.CallProgressBar(null, null);
}
Some problems I encoutered
Inside DoWork, the same modal issue occurs. ShowDialog() will block the thread and therefore I never get to check CancellationPending to close ProgressBarForm.
ProgressBarForm starts later then the mainWork. I thought when I called CallProgressBar, the backgroundWorker should start well before my mainWork.
Is worker.Dispose() necessary in RunWorkerComplete?
Would it be a better choice to run mainWork in Worker? And why? I decided to let the main thread run this to not disturb the normal flow, what in Main thread will remain in Main thread, Progress bar is like an addon. If we bring it to the worker, would we need another thread to for progress bar itself?
Unless you do some very ugly hacks (like running more than one message loop inside your application) you cannot display a dialog if the thread running the main window is busy. All dialogs use the same thread to do the display update stuff in WinForms. In fact, they even must be running on the same thread.
There's one (sometimes acceptable) hack using Application.DoEvents(), but I wouldn't use it either, because it gets you into a lot of problems as well.
So the simple answer is: This doesn't work. Use a background worker to do lengthy processing.
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
When my main form loads I want to do some processing like loading the last client viewed. This extra processing causing the drawing of the form to be pretty ugly. I am doing it on form shown so I would expect that the form would be done painting.
I have used PostMessage... Is there a modern day version of PostMessage? During this processing I would like to set the hourglass cursor.
I think what you are trying to do is background processing. So you can use a BackgroundWorker class. When your form loads, you would start the background worker and when the worker is finished, it will notify your form through the RunWorkerCompleted event. You can even get progress updates from the worker by implementing ProgressChanged event.
How about using Task + ContinueWith. (If you upgrade to .Net 4.5 async/await would be a better choice).
private void Form_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(5000); //Some long Running Jobs
return "Finished";
})
.ContinueWith(t =>
{
this.Text = t.Result; //Update GUI
},
TaskScheduler.FromCurrentSynchronizationContext());
}
I am not sure why would you want to use PostMessage. If you post a message using PostMessage it will eventually be obtained by GetMessage and unless the message is posted from a different thread whole exercise seems a little futile. Am I missing something here? As for BackgroundWorker please refer to the solution for Label is not set until method is done.
I am new to multi-threading. I am doing a project using wpf.
Brief introduction to my project:
One mainwindow
One method in mainwindow creates another thread that creates another window, which is called window 2.
Whenever window 2 is visible, mainwindow is suspended(so i used join() method to suspend the main thread)
The problem is whenever I closed the window 2, the new thread doesn't terminate. so the main thread is forever suspended. So how to solve this problem.
The following code is where i create a new thread for window 2 in MainWindow Class:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Thread addThread = new Thread(CreateCourseWindow);
addThread.SetApartmentState(ApartmentState.STA);
addThread.Start();
Thread.Sleep(TimeSpan.FromSeconds(1));
addThread.Join();
if (addcourse.Saved) CreateCourseButton(myCourses.Count(), myCourses.Last());
}
The following code is the entry function for the new thread
private void CreateCourseWindow()
{
addcourse = new AddCourse();
addcourse.Activate();
addcourse.Show();
addcourse.Topmost = true;
System.Windows.Threading.Dispatcher.Run();
}
The following code is where i want to terminate the thread
private void Button_Click_2(object sender, RoutedEventArgs e)
{
this.Close();
}
AddCourse is the window 2 class.
First off, don't create multiple UI threads. It creates a real mess that's very hard to deal with. Only ever have on UI thread in your application unless you're sure that you need more, and that you really know what you're doing.
As for this specific case, you don't need to worry about asynchrony, multiple threads, or any of it:
ChildWindow child = new ChildWindow();
this.Hide();
child.ShowDialog();
this.Show();
By using ShowDialog instead of Show you remove all the pesky asynchrony issues.
If you want the parent window visible but not enabled you can remove the show/hide but leave the ShowDialog, which will ensure the parent is disabled.
It is not advised to suspend a window by hanging the thread. What you get is a complete non-responsive window, that doesn't handle any Windows event at all. For example, it will not react to move events, or set cursor events, or other things that you would expect even a disabled window to have. Furthermore, if there are any COM STA objects on this thread they will hang (and sometimes there can be such object even if you didn't explicitly create them).
How are you opening the window from the second thread? It sounds like this thread is running a loop which doesn't terminate when the window is closed. If you post some code it can help.
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.