Can I close the program from the Main constructor? - c#

Can you exit an application before the constructor is finished and the main form is loaded?
At startup, I have a loading screen that displays before the main form is loaded. The loading screen is displayed from the constructor before the constructor has finished.
I do something similar with an exit screen by using a variable between the main form and the exit screen. I have an application exit in the main form if the exit screen returns true.
Finally, should all the thread/class/loading/program setup be done in the main constructor or am I doing it wrong?
Update:
I mean after the program.cs and in the static main
namespace app
{
public partial class app1 : Form
{
public app1()
{
InitializeComponent();
// open loading screen
// initialize vars
// create objects
}
// form opens when app1() finishes
Is app1() the right place to initialize everything?
If I try to send a "close" message back from the loading screen before app1() is finished, it doesn't work - the process still runs even though nothing is open.

I've found that if I try to kill my application from the main form constructor when I still have the splash screen showing on a different thread (which looks similar to what you are doing), that Application.Exit() does not work, but Environment.Exit(-1) does.

try this,
public partial class MyForm : Form
{
public MyForm()
{
if (MyFunc())
{
this.Shown += new EventHandler(MyForm_CloseOnStart);
}
}
private void MyForm_CloseOnStart(object sender, EventArgs e)
{
this.Close();
}
}
it will work well...

Which main constructor of which class?
Are you talking about the static method Main that has a default location in the Program class?
You use that method to do initialization that needs to occur before you open any windows on screen.
Obviously, if you need to use a loading screen, you will probably want to move some code somewhere else, as you need a message loop around forms, and the message loop will block until your form closes.
If you return from the Main method before you open any form, then no form will be shown obviously.
Having said all that, I feel your question is a bit vague and I'm pretty sure I didn't understand exactly what it is that you're asking.
First and foremost, Main is not a constructor, it's just a static method.

When main thread ends:
background threads are "killed/abandoned"
foreground threads (the default when creating threads) are waited till they finish.

you can break constructor only via throwing an exception. To do that surreptitiously, throw you own specific exception.
class ConstructorAbortedException : Exception { }
class Foo
{
public Foo()
{
if(goesWrong)
{
throw new ConstructorAbortedException();
}
}
}
void Bar()
{
try
{
Foo f = new Foo();
}
catch(ConstructorAbortedException)
{
//..
}
}

As jontsnz answered, the code with
Environment.Exit(-1)
works fine in the constructor, but this causes the application to throw an "Application Hang" event, which can be seen as an error in the Windows Event Viewer. Using
Environment.Exit(0)
exits without registering an error though, so I prefer that one.

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.

Code starts only when Application.Run() is invoked WPF application

I have a method which is invoked in class A and it is defined in class B:
class B{
[STAThread]
public static void ScanForAxisCameras() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
Application.Run();//if not invoked everything above does not start
}
}
class A{ ...before invoking..... B.ScanForAxisCameras(); ....after invoking....}
The code in class B "starts"/works only if I invoke Application.Run(). But it causes that all the code in class A ....after invoking.... method does not work. How to handle it so it will not freeze the application?
Edit: the class A is class MainWindow.xaml.cs. It is WPF application.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
createGUI();
}
private void createGUI() {
LocalNetworkScanner.ScanForAxisCameras();//when there is no Application.Run() ScanForAxisCameras() does not work.
}
}
The WPF UI thread on which you call ScanForAxisCameras() already has a message loop. I believe the problem with your code is that all objects you create inside ScanForAxisCameras have the local scope:
public static void ScanForAxisCameras() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
Application.Run();//if not invoked everything above does not start
}
Without Application.Run(), your objects (service, eventManager, browse) may be getting destroyed and finalized as soon as ScanForAxisCameras finishes. So, the events you're looking for (like ServiceFound) may not even have a chance to get fired.
If you call Application.Run(), then ScanForAxisCameras doesn't exit (at least not until Application.Run() itself exits). That keeps your objects alive and functional.
Try refactoring your code to keep the references to these objects in member fields of your class (or in static variables, FWIW). I believe that should fix the problem.
[EDITED] On a side note, the [STAThread] attribute doesn't make sense in that context (unless you use ScanForAxisCameras as an entry point for a new thread - apparently, that's not the case here).
You can introduce a new Run()-method in your App in the App.xaml.cs file.
Here you can perform custom actions before the application itself gets initialized.
Further information here.
public partial class App : Application
{
public new void Run()
{
// Do your stuff here
B.DoStuff();
// Call the base method
base.Run();
}
}
Application.Run starts the message loop for that particular thread, if there is no message loop then there is no notification for your objects to know they have to do something.
The code in class B "starts"/works only if I invoke Application.Run(). But it causes that all the code in class A ....after invoking.... method does not work.
Run is a blocking call therefore any code after that call is not reachable until the application is closing down i.e. when you exit the message loop.
How to handle it so it will not freeze the application?
In short, you can't. Run will always block so any code you need to run as part of your application startup will have to happen before the call.
After your edit to mention that this is a WPF application then Application.Run as a static method is not the right way to go here. If you need to run initialization when your application starts then you can do what has already been suggested and override the Run method of the Application class, or alternatively (maybe more appropriately) you can hook into the OnStartup event e.g.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// code before startup
base.OnStartup(e);
// code after startup
}
}
It seems as though in this situation you would need to add Application.Run() in a different class. Run() accepts nothing, ApplicationContext, Form. This controls the lifetime of the application and should be called before class A, unless class A is the entry point.
See http://msdn.microsoft.com/en-us/library/ms157900.aspx for details.
A Windows Forms application starts when the Main method is called. You can implement initialization procedures on the Main function. However, to initialize a Windows Forms application fully and start it routing Windows Forms events, you need to invoke Application.Run.
you can read about Application here

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.

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