views:

8788

answers:

7

I know this question is all over the net, but I can't quite find what I am looking for and quite frankly this community seems more sophisticated than most I have seen. 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 (i.e. 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 think it's clear I am confused. I have seen lots of examples online, but none seem to really give a concise approach. I'm not looking for a code handout, more of an algorithm/approach so I can figure this out once and for all :) Thanks!

+1  A: 

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:

  1. Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
  2. 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.

McKenzieG1
+14  A: 

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();
}
aku
I love this method, thanks bud, I use this often now.
Echostorm
This is probably the best method I've seen for doing splash screens. Works BEAUTIFULLY on .NET compact with my pocket PC app.
Damien
@aku: Is it a good idea to use Application.DoEvents() in the application? I read lot about not using this function on internet like http://blogs.msdn.com/jfoscoding/archive/2005/08/06/448560.aspx
Kavitesh Singh
It might not be a good idea to create the form in a non-UI thread...could end up in a world of pain :)
Cocowalla
+25  A: 

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.

  1. Have the main for inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. 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?

Guy Starbuck
This was the most elegant solution since it uses built-in functionality that is abstracted very well by the Visual Basic library. I didn't know how to create a VB ApplicationBase program, so more research was needed...I'll add more info for completeness. Thanks for all the great feedback!!
John Virgolino
I've had to fix some badly-written manual splash screens-- using the supported functionality like Guy suggests is definitely the awesome way to go. :)
Greg D
Nice idea but doesn't work on the compact framework.
Damien
+3  A: 

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:

  1. 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.

  2. 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.

Peter Meyer
+4  A: 

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.

DelftRed
A: 

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#

Kavitesh Singh
+2  A: 

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();
  }
Jay