Fully closing application in C# just won't work [closed] - c#

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm writing a simple game and I have a function that basically should just exit the application or rather close everything currently open in C# (I'm using Windows Forms).
private void ExitApp()
{
Application.Exit();
}
However, nothing will work. I have tried using Environment.Exit(0), Application.Exit, tried using a for loop to close every form but it just won't work. What I have noticed is that even if I press the X button, the solution won't close, but something seems to be running in the background and I do not know what. Browsed Stackoverflow forum for similar issues, browsed other forums, googled for days, but nothing seemed to help.
This is the code for opening more forms :
Podešavanja p = new Podešavanja();
private void Settings_FormClosing(object sender, FormClosingEventArgs e)
{
this.Close();
Menu m = new Menu();
m.Show();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
Menu m = new Menu();
m.Show();
}
The SettingsFormClosing event actually just opens up a new Form for me, without closing the previous one, why, I do not know.
Any help would be greatly appreciated.

The problem is that your forms are all running on the same thread. Take a look at your Program.cs file. See how it calls Application.Run(New Form1())? This is where your application form initially runs on the application thread.
So the problem we have here is: you are trying to close your Form, which is hosting your second form. Suppose your a single form with a button control on it. Now suppose you tried to tell your application you wanted the window form to close, but wanted the button to stay active and open -- crazy right? Well what you are trying to do is essentially the same thing -- mind you I am basing this on the assumption you are not multithreading. Your Form1 is hosting your Form2 instance, and thus you cannot run Form2 if Form1 is disposed. The best way I can think of, at least off the top of my head, is you need to create a recursive call in your Program.cs and tell it whether or not it needs to run a new Form before it truly exits. This is questionable at best, but it might suffice.
So let's modify our Program.cs Then:
static class Program
{
//This is where we set the current form running -- or to be run.
static Form CurrentForm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Obviously, Form 1 starts everything so we hardcode that here on startup.
CurrentForm = new Form1();
//Then call our Run method we created, which starts the cycle.
Run();
}
//This runs the current form
static void Run()
{
//Tell our program to run this current form on the application thread
Application.Run(CurrentForm);
//Once the form OFFICIALLY closes it will execute the code below
//Until that point, imagine Application.Run being stuck there
if(CurrentForm != null && CurrentForm.IsDisposed == false)
{
//If our current form is NOT null and it is NOT disposed,
//Then that means the application has a new form to display
//So we will recall this method.
Run();
}
}
//This method is what we will call inside our forms when we want to
//close the window and open a new one.
public static void StartNew(Form form)
{
//Close the current form running
CurrentForm.Close();
//Set the new form to be run
CurrentForm = form;
//Once all this is called, imagine the program now
//Releasing Application.Run and executing the code after
}
}
Okay so if you wrap your head around this, then closing and opening a new form is a piece of cake. We simply can open new forms on button click events.
Inside Form1.cs:
private void OpenForm2_Click(object sender, EventArgs e)
{
Program.StartNew(new Form2());
}
Inside Form2.cs
private void OpenForm1_Click(object sender, EventArgs e)
{
Program.StartNew(new Form1());
}
I will reiterate, this method is super questionable.... But it may suffice for what ever you are doing. It is also super reusable through your application regardless of the class or form.

Related

how to properly start and kill a form thread [duplicate]

I want a splash screen to show while the application is loading. I have a form with a system tray control tied to it. I want the splash screen to display while this form loads, which takes a bit of time since it's accessing a web service API to populate some drop-downs. I also want to do some basic testing for dependencies before loading (that is, the web service is available, the configuration file is readable). As each phase of the startup goes, I want to update the splash screen with progress.
I have been reading a lot on threading, but I am getting lost on where this should be controlled from (the main() method?). I am also missing how Application.Run() works, is this where the threads for this should be created from? Now, if the form with the system tray control is the "living" form, should the splash come from there? Wouldn't it not load until the form is completed anyway?
I'm not looking for a code handout, more of an algorithm/approach so I can figure this out once and for all :)
The trick is to to create separate thread responsible for splash screen showing.
When you run you app .net creates main thread and loads specified (main) form. To conceal hard work you can hide main form until loading is done.
Assuming that Form1 - is your main form and SplashForm is top level, borderles nice splash form:
private void Form1_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new SplashForm())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
Thread.Sleep(3000); // Emulate hardwork
done = true;
Show();
}
Well, for a ClickOnce app that I deployed in the past, we used the Microsoft.VisualBasic namespace to handle the splash screen threading. You can reference and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 and it provides a lot of nice services.
Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
Override the "OnCreateSplashScreen" method like so:
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new SplashForm();
this.SplashScreen.TopMost = true;
}
Very straightforward, it shows your SplashForm (which you need to create) while loading is going on, then closes it automatically once the main form has completed loading.
This really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is of course well tested by Microsoft and has a lot of functionality that can make your life a lot easier in Winforms, even in an application that is 100% C#.
At the end of the day, it's all IL and bytecode anyway, so why not use it?
After looking all over Google and SO for solutions, this is my favorite:
http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen
FormSplash.cs:
public partial class FormSplash : Form
{
private static Thread _splashThread;
private static FormSplash _splashForm;
public FormSplash() {
InitializeComponent();
}
/// <summary>
/// Show the Splash Screen (Loading...)
/// </summary>
public static void ShowSplash()
{
if (_splashThread == null)
{
// show the form in a new thread
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// called by the thread
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new FormSplash();
// create a new message pump on this thread (started from ShowSplash)
Application.Run(_splashForm);
}
/// <summary>
/// Close the splash (Loading...) screen
/// </summary>
public static void CloseSplash()
{
// need to call on the thread that launched this splash
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// splash screen, which is terminated in FormMain
FormSplash.ShowSplash();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// this is probably where your heavy lifting is:
Application.Run(new FormMain());
}
}
FormMain.cs
...
public FormMain()
{
InitializeComponent();
// bunch of database access, form loading, etc
// this is where you could do the heavy lifting of "loading" the app
PullDataFromDatabase();
DoLoadingWork();
// ready to go, now close the splash
FormSplash.CloseSplash();
}
I had issues with the Microsoft.VisualBasic solution -- Worked find on XP, but on Windows 2003 Terminal Server, the main application form would show up (after the splash screen) in the background, and the taskbar would blink. And bringing a window to foreground/focus in code is a whole other can of worms you can Google/SO for.
This is an old question, but I kept coming across it when trying to find a threaded splash screen solution for WPF that could include animation.
Here is what I ultimately pieced together:
App.XAML:
<Application Startup="ApplicationStart" …
App.XAML.cs:
void ApplicationStart(object sender, StartupEventArgs e)
{
var thread = new Thread(() =>
{
Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
// call synchronous configuration process
// and declare/get reference to "main form"
thread.Abort();
mainForm.Show();
mainForm.Activate();
}
I recommend calling Activate(); directly after the last Show(); in the answer provided by aku.
Quoting MSDN:
Activating a form brings it to the
front if this is the active
application, or it flashes the window
caption if this is not the active
application. The form must be visible
for this method to have any effect.
If you don't activate your main form, it may be displayed behind any other open windows, making it look a bit silly.
I think using some method like aku's or Guy's is the way to go, but a couple of things to take away from the specific examples:
The basic premise would be to show your splash on a separate thread as soon as possible. That's the way I would lean, similar to what aku's illustrated, since it's the way I'm most familiar with. I was not aware of the VB function Guy mentioned. And, even thought it's a VB library, he is right -- it's all IL in the end. So, even if it feels dirty it's not all that bad! :) I think you'll want to be sure that either VB provides a separate thread for in that override or that you create one yourself -- definitely research that.
Assuming you create another thread to display this splash, you will want to be careful of cross thread UI updates. I bring this up because you mentioned updating progress. Basically, to be safe, you need to call an update function (that you create) on the splash form using a delegate. You pass that delegate to the Invoke function on your splash screen's form object. In fact if you call the splash form directly to update progress/UI elements on it, you'll get an exception provided you are running on the .Net 2.0 CLR. As a rule of thumb, any UI element on a form must be updated by the thread that created it -- that's what Form.Invoke insures.
Finally, I would likely opt to create the splash (if not using the VB overload) in the main method of your code. To me this is better than having the main form perform creation of the object and to be so tightly bound to it. If you take that approach, I'd suggest creating a simple interface that the splash screen implements -- something like IStartupProgressListener -- which receives start-up progress updates via a member function. This will allow you to easily swap in/out either class as needed, and nicely decouples the code. The splash form can also know when to close itself if you notify when start-up is complete.
One simple way is the use something like this as main():
<STAThread()> Public Shared Sub Main()
splash = New frmSplash
splash.Show()
' Your startup code goes here...
UpdateSplashAndLogMessage("Startup part 1 done...")
' ... and more as needed...
splash.Hide()
Application.Run(myMainForm)
End Sub
When the .NET CLR starts your application, it creates a 'main' thread and starts executing your main() on that thread. The Application.Run(myMainForm) at the end does two things:
Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
Designates your 'main form' as the 'shutdown form' for the application. If the user closes that form, then the Application.Run() terminates and control returns to your main(), where you can do any shutdown you want.
There is no need to spawn a thread to take care of the splash window, and in fact this is a bad idea, because then you would have to use thread-safe techniques to update the splash contents from main().
If you need other threads to do background operations in your application, you can spawn them from main(). Just remember to set Thread.IsBackground to True, so that they will die when the main / GUI thread terminates. Otherwise you will have to arrange to terminate all your other threads yourself, or they will keep your application alive (but with no GUI) when the main thread terminates.
I posted an article on splash screen incorporation in the application at codeproject. It is multithreaded and might be of your interest
Yet Another Splash Screen in C#
private void MainForm_Load(object sender, EventArgs e)
{
FormSplash splash = new FormSplash();
splash.Show();
splash.Update();
System.Threading.Thread.Sleep(3000);
splash.Hide();
}
I got this from the Internet somewhere but cannot seem to find it again. Simple but yet effective.
I like Aku's answer a lot, but the code is for C# 3.0 and up since it uses a lambda function. For people needing to use the code in C# 2.0, here's the code using anonymous delegate instead of the lambda function. You need a topmost winform called formSplash with FormBorderStyle = None. The TopMost = True parameter of the form is important because the splash screen might look like it appears then disappears quickly if it's not topmost. I also choose StartPosition=CenterScreen so it looks like what a professional app would do with a splash screen. If you want an even cooler effect, you can use the TrasparencyKey property to make an irregular shaped splash screen.
private void formMain_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem(delegate
{
using (formSplash splashForm = new formSplash())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
}, null);
Thread.Sleep(2000);
done = true;
Show();
}
I disagree with the other answers recommending WindowsFormsApplicationBase. In my experience, it can slow your app. To be precise, while it runs your form's constructor in parallel with the splash screen, it postpone your form's Shown event.
Consider an app (without splashs screen) with a constructor that takes 1 second and a event handler on Shown that takes 2 seconds. This app is usable after 3 seconds.
But suppose you install a splash screen using WindowsFormsApplicationBase. You might think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't slow your app. But, try it, your app will now take 5 seconds to load.
class App : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
this.SplashScreen = new Splash();
}
protected override void OnCreateMainForm()
{
this.MainForm = new Form1();
}
}
and
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
void Form1_Shown(object sender, EventArgs e)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Program.watch.Stop();
this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}
Conclusion: don't use WindowsFormsApplicationBase if your app has a handler on the Slown event. You can write better code that runs the splash in parallel to both the constructor and the Shown event.
Actually mutlithreading here is not necessary.
Let your business logic generate an event whenever you want to update splash screen.
Then let your form update the splash screen accordingly in the method hooked to eventhandler.
To differentiate updates you can either fire different events or provide data in a class inherited from EventArgs.
This way you can have nice changing splash screen without any multithreading headache.
Actually with this you can even support, for example, gif image on a splash form. In order for it to work, call Application.DoEvents() in your handler:
private void SomethingChanged(object sender, MyEventArgs e)
{
formSplash.Update(e);
Application.DoEvents(); //this will update any animation
}

Methods this.hide() vs this.close()

My c# windows forms application has 5 forms which I am displaying one after the another. When the user clicks on next button, the code I have given is:
new Form1().Show();
this.Hide();
However I do not want my current Form to hide. I want to close it/dispose it so that it does not consume memory. I want to release its resources like the images and variable used as soon as I am done with it.
For that I tried implementing:
new Form1().Show();
this.Close(); //Form 2
but this simply closes both the forms.
I even tried swapping the positions of the above two lines:
this.Close();
new Form1().Show();
but this also does same thing.
How do I release the resources of one form as soon as I am done with it? because my program throws out of memory exception when I try to re-open my Form 2 using:
new Form2().Show();
this.Hide();
You can start your NewForm in a new thread and create a new message loop
When the main message loop is closed, the application exits. In Windows Forms, this loop is closed when the Exit method is called
For more information see here.
var th = new Thread(() => Application.Run(new NewForm()));
th.SetApartmentState(ApartmentState.STA); // Deprecation Fix
th.Start();
this.Close();
Another way to do it, is to manage the application context yourself. Here is a small demo:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (var myApplicationContext = new MyApplicationContext(new Form1()))
{
Application.Run(myApplicationContext);
}
}
You can define your tailored made ApplicationContext in the following way:
public class MyApplicationContext : ApplicationContext
{
public MyApplicationContext(Form mainForm)
:base(mainForm)
{
}
protected override void OnMainFormClosed(object sender, EventArgs e)
{
if (Form.ActiveForm != null)
{
this.MainForm = Form.ActiveForm;
}
else
{
base.OnMainFormClosed(sender, e);
}
}
}
And now, you could do the following on the Button.Click event handler:
var f = new Form();
f.Show();
this.Close();
And the application will keep on running. Basically this way you keep the app alive while there is at least one active form.
NOTE Haven't tested it but it should work.
Closing the form which the Program start in its main function will close the application, an idea is to have a parent Form and make it the main form, and never close it, this can be a hidden form if you want.
I am not working with Windows forms since long time ago but found on this page the reason behind behavior you are getting:
http://msdn.microsoft.com/en-us/library/ms157902(v=vs.110).aspx
Typically, the main function of an application calls this method and
passes to it the main window of the application. This method adds an
event handler to the mainForm parameter for the Closed event. The
event handler calls ExitThread to clean up the application.
Also on this question How do I prevent the app from terminating when I close the startup form? there was a discussion about something the same
I am not sure if it would work for you. But when I had the same problem while dealing with a login form... i just used ShowDialog() instead of Show() , (and for me it solved the problem ) Just Like:
this.Hide();
MainForm MForm = new MainForm();
MForm.ShowDialog();
this.Close();
By default, a C# Forms application creates a "root" form in the Program.Main() method and passes that to the Application.Run() method. When this Form is closed, your program will exit.
However, you can change this behavior by using a different Application.Run() overload. Just don't pass the Form instance to Run(). Instead, show the form before calling Application.Run(), and then later on (when you finally do want the program to quit) use the Application.ExitThread() method to tell the Application class you're ready to close the application.

Form closing itself

I'm new to C# (switching from Java) and i'm having a bit of trouble understanding GUI in C#
Ill just paste the Code i have and let that explain the most part.
Main Class:
Frame frame;
keepRunning = true;
public GraphicsComponent()
{
frame = new Frame();
frame.Show();
}
public void run()
{
while (keepRunning)
{
Console.WriteLine("Running");
}
}
public static void Main()
{
GraphicsComponent gameComponent = new GraphicsComponent();
gameComponent.run();
}
using the frame.Show() method, it appears and disappears for a quick moment. but the "running" loop is run.
The other way i have seen is another method called ShowDialoge(), that one keeps the window open but blocks the rest of the code from running until it is closed.
The way i want to use it is basically how GUIs work in Java. Where i can reference an object (and it's components) as needed in the rest of my code.
Thanks heaps!
Edit: There is alot of confusion, so here is my Frame class:
public partial class Frame : Form
{
public Frame()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
A normal way to kick off a winforms application is by file->new winforms project. WHen you do this you get "Program.cs", which contains your Main method - the entry point into your application. From there you have code like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ConfigForm());
}
In this example I kick off a new "ConfigForm"" that is a Form, and contains in it's ctor some code that looks like this:
public ConfigForm()
{
InitializeComponent();
// Other init work here
}
I create the form by right-clicking in the project and adding a new form.
You might want to look into the Application.Run method, see here. You can specify a "Main Form" which is going to be the main form that runs for the duration of the application, and when closed ends the application, or you can specify other options if you want. Perhaps the simplest solution is to start a main form, and since the form is live for the duration of the application, you can kick off other long-running code inside that form. If it's code that you want to be executing all the time performing some sort of background operation then you might want to fire it off in it's own thread. However, threading is hard so if you're new to c# / .net I'd try to stay away from threading unless you need it.

Run multiple UI Threads

Skip to the bottom for the question; this is just some extra info
I am using a component (GeckoFX) to render some websites, well fine, yet it can only be used in a Windows Form; as it has to bind to a WinForms object that can be drawn. Because all the WinForms are running in the same thread, I can only use one GeckoFX instance at a time; so I decided to create a 'worker class' in the form of a WinForm, and add all the logic in there. The form doesn't require to communicate with the main form.
Now I can fire up 10 windows, and they will eventually work, but every new form will wait before all the other forms have handled all their GeckoFX events, as you cannot use multiple instances on one thread. Furthermore, the browser has to be on a UIThread. So:
Is it possible to create multiple UI Threads (one for each form)?
I have seen someone doing it ([edit: removed 'bad' link]), yet no one ever got his code samples working. The guy who got it working originally used some form of custom message pumping to do this kind of things, but I have no clue how to achieve something like that.
I don't think that what you ask is really what you want but creating a message pump per thread is easy, you just have to call Application.Run once per thread.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Thread t1 = new Thread(Main_);
Thread t2 = new Thread(Main_);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
static void Main_()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Use Application.DoEvent().
or
Create multiply threading forms:
Thread form2Thread;
Form2 form2;
private void Form1_Load(object sender, EventArgs e)
{
form2Thread = new Thread(RunForm2);
form2Thread.SetApartmentState(ApartmentState.STA);
form2Thread.Name = "Form2 Thread"; // looks nice in Output window
form2Thread.Start();
}
public void RunForm2()
{
form2 = new Form2();
Application.Run(form2);
}
GeckoFx doesn't require a form.
GeckoWebBrowser wb = new GeckoWebBrowser();
wb.CreateControl(); //<-- the magic lays here!
wb.DocumentCompleted += delegate{ MessageBox.Show(wb.DocumentTitle); };
wb.Navigate("http://mysite.com");
Seems like it is possible.
I took backgrounder, opened TestApp, and created a new Form1 on thread/message pump #2:
private void button2_Click(object sender, EventArgs e) {
helper.Background(() => {
Form1 form2 = new Form1();
form2.Show();
});
}
The second window responds to mouse clicks etc.
Haven't actually verified if everything looks right, the freebie Visual Studio Express Edition I'm using is missing the "Threads" debug window, ahem. So I'm a bit in the dark. It seems to work, though. Let me know :-).

Multi-threaded splash screen in C#?

I want a splash screen to show while the application is loading. I have a form with a system tray control tied to it. I want the splash screen to display while this form loads, which takes a bit of time since it's accessing a web service API to populate some drop-downs. I also want to do some basic testing for dependencies before loading (that is, the web service is available, the configuration file is readable). As each phase of the startup goes, I want to update the splash screen with progress.
I have been reading a lot on threading, but I am getting lost on where this should be controlled from (the main() method?). I am also missing how Application.Run() works, is this where the threads for this should be created from? Now, if the form with the system tray control is the "living" form, should the splash come from there? Wouldn't it not load until the form is completed anyway?
I'm not looking for a code handout, more of an algorithm/approach so I can figure this out once and for all :)
The trick is to to create separate thread responsible for splash screen showing.
When you run you app .net creates main thread and loads specified (main) form. To conceal hard work you can hide main form until loading is done.
Assuming that Form1 - is your main form and SplashForm is top level, borderles nice splash form:
private void Form1_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new SplashForm())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
Thread.Sleep(3000); // Emulate hardwork
done = true;
Show();
}
Well, for a ClickOnce app that I deployed in the past, we used the Microsoft.VisualBasic namespace to handle the splash screen threading. You can reference and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 and it provides a lot of nice services.
Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
Override the "OnCreateSplashScreen" method like so:
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new SplashForm();
this.SplashScreen.TopMost = true;
}
Very straightforward, it shows your SplashForm (which you need to create) while loading is going on, then closes it automatically once the main form has completed loading.
This really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is of course well tested by Microsoft and has a lot of functionality that can make your life a lot easier in Winforms, even in an application that is 100% C#.
At the end of the day, it's all IL and bytecode anyway, so why not use it?
After looking all over Google and SO for solutions, this is my favorite:
http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen
FormSplash.cs:
public partial class FormSplash : Form
{
private static Thread _splashThread;
private static FormSplash _splashForm;
public FormSplash() {
InitializeComponent();
}
/// <summary>
/// Show the Splash Screen (Loading...)
/// </summary>
public static void ShowSplash()
{
if (_splashThread == null)
{
// show the form in a new thread
_splashThread = new Thread(new ThreadStart(DoShowSplash));
_splashThread.IsBackground = true;
_splashThread.Start();
}
}
// called by the thread
private static void DoShowSplash()
{
if (_splashForm == null)
_splashForm = new FormSplash();
// create a new message pump on this thread (started from ShowSplash)
Application.Run(_splashForm);
}
/// <summary>
/// Close the splash (Loading...) screen
/// </summary>
public static void CloseSplash()
{
// need to call on the thread that launched this splash
if (_splashForm.InvokeRequired)
_splashForm.Invoke(new MethodInvoker(CloseSplash));
else
Application.ExitThread();
}
}
Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// splash screen, which is terminated in FormMain
FormSplash.ShowSplash();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// this is probably where your heavy lifting is:
Application.Run(new FormMain());
}
}
FormMain.cs
...
public FormMain()
{
InitializeComponent();
// bunch of database access, form loading, etc
// this is where you could do the heavy lifting of "loading" the app
PullDataFromDatabase();
DoLoadingWork();
// ready to go, now close the splash
FormSplash.CloseSplash();
}
I had issues with the Microsoft.VisualBasic solution -- Worked find on XP, but on Windows 2003 Terminal Server, the main application form would show up (after the splash screen) in the background, and the taskbar would blink. And bringing a window to foreground/focus in code is a whole other can of worms you can Google/SO for.
This is an old question, but I kept coming across it when trying to find a threaded splash screen solution for WPF that could include animation.
Here is what I ultimately pieced together:
App.XAML:
<Application Startup="ApplicationStart" …
App.XAML.cs:
void ApplicationStart(object sender, StartupEventArgs e)
{
var thread = new Thread(() =>
{
Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
// call synchronous configuration process
// and declare/get reference to "main form"
thread.Abort();
mainForm.Show();
mainForm.Activate();
}
I recommend calling Activate(); directly after the last Show(); in the answer provided by aku.
Quoting MSDN:
Activating a form brings it to the
front if this is the active
application, or it flashes the window
caption if this is not the active
application. The form must be visible
for this method to have any effect.
If you don't activate your main form, it may be displayed behind any other open windows, making it look a bit silly.
I think using some method like aku's or Guy's is the way to go, but a couple of things to take away from the specific examples:
The basic premise would be to show your splash on a separate thread as soon as possible. That's the way I would lean, similar to what aku's illustrated, since it's the way I'm most familiar with. I was not aware of the VB function Guy mentioned. And, even thought it's a VB library, he is right -- it's all IL in the end. So, even if it feels dirty it's not all that bad! :) I think you'll want to be sure that either VB provides a separate thread for in that override or that you create one yourself -- definitely research that.
Assuming you create another thread to display this splash, you will want to be careful of cross thread UI updates. I bring this up because you mentioned updating progress. Basically, to be safe, you need to call an update function (that you create) on the splash form using a delegate. You pass that delegate to the Invoke function on your splash screen's form object. In fact if you call the splash form directly to update progress/UI elements on it, you'll get an exception provided you are running on the .Net 2.0 CLR. As a rule of thumb, any UI element on a form must be updated by the thread that created it -- that's what Form.Invoke insures.
Finally, I would likely opt to create the splash (if not using the VB overload) in the main method of your code. To me this is better than having the main form perform creation of the object and to be so tightly bound to it. If you take that approach, I'd suggest creating a simple interface that the splash screen implements -- something like IStartupProgressListener -- which receives start-up progress updates via a member function. This will allow you to easily swap in/out either class as needed, and nicely decouples the code. The splash form can also know when to close itself if you notify when start-up is complete.
One simple way is the use something like this as main():
<STAThread()> Public Shared Sub Main()
splash = New frmSplash
splash.Show()
' Your startup code goes here...
UpdateSplashAndLogMessage("Startup part 1 done...")
' ... and more as needed...
splash.Hide()
Application.Run(myMainForm)
End Sub
When the .NET CLR starts your application, it creates a 'main' thread and starts executing your main() on that thread. The Application.Run(myMainForm) at the end does two things:
Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
Designates your 'main form' as the 'shutdown form' for the application. If the user closes that form, then the Application.Run() terminates and control returns to your main(), where you can do any shutdown you want.
There is no need to spawn a thread to take care of the splash window, and in fact this is a bad idea, because then you would have to use thread-safe techniques to update the splash contents from main().
If you need other threads to do background operations in your application, you can spawn them from main(). Just remember to set Thread.IsBackground to True, so that they will die when the main / GUI thread terminates. Otherwise you will have to arrange to terminate all your other threads yourself, or they will keep your application alive (but with no GUI) when the main thread terminates.
I posted an article on splash screen incorporation in the application at codeproject. It is multithreaded and might be of your interest
Yet Another Splash Screen in C#
private void MainForm_Load(object sender, EventArgs e)
{
FormSplash splash = new FormSplash();
splash.Show();
splash.Update();
System.Threading.Thread.Sleep(3000);
splash.Hide();
}
I got this from the Internet somewhere but cannot seem to find it again. Simple but yet effective.
I like Aku's answer a lot, but the code is for C# 3.0 and up since it uses a lambda function. For people needing to use the code in C# 2.0, here's the code using anonymous delegate instead of the lambda function. You need a topmost winform called formSplash with FormBorderStyle = None. The TopMost = True parameter of the form is important because the splash screen might look like it appears then disappears quickly if it's not topmost. I also choose StartPosition=CenterScreen so it looks like what a professional app would do with a splash screen. If you want an even cooler effect, you can use the TrasparencyKey property to make an irregular shaped splash screen.
private void formMain_Load(object sender, EventArgs e)
{
Hide();
bool done = false;
ThreadPool.QueueUserWorkItem(delegate
{
using (formSplash splashForm = new formSplash())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
}, null);
Thread.Sleep(2000);
done = true;
Show();
}
I disagree with the other answers recommending WindowsFormsApplicationBase. In my experience, it can slow your app. To be precise, while it runs your form's constructor in parallel with the splash screen, it postpone your form's Shown event.
Consider an app (without splashs screen) with a constructor that takes 1 second and a event handler on Shown that takes 2 seconds. This app is usable after 3 seconds.
But suppose you install a splash screen using WindowsFormsApplicationBase. You might think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't slow your app. But, try it, your app will now take 5 seconds to load.
class App : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
this.SplashScreen = new Splash();
}
protected override void OnCreateMainForm()
{
this.MainForm = new Form1();
}
}
and
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
void Form1_Shown(object sender, EventArgs e)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Program.watch.Stop();
this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}
Conclusion: don't use WindowsFormsApplicationBase if your app has a handler on the Slown event. You can write better code that runs the splash in parallel to both the constructor and the Shown event.
Actually mutlithreading here is not necessary.
Let your business logic generate an event whenever you want to update splash screen.
Then let your form update the splash screen accordingly in the method hooked to eventhandler.
To differentiate updates you can either fire different events or provide data in a class inherited from EventArgs.
This way you can have nice changing splash screen without any multithreading headache.
Actually with this you can even support, for example, gif image on a splash form. In order for it to work, call Application.DoEvents() in your handler:
private void SomethingChanged(object sender, MyEventArgs e)
{
formSplash.Update(e);
Application.DoEvents(); //this will update any animation
}

Categories

Resources