[ACCEPTED]-Multi-threaded splash screen in C#?-splash-screen

Accepted answer
Score: 49

The trick is to to create separate thread 7 responsible for splash screen showing.
When 6 you run you app .net creates main thread 5 and loads specified (main) form. To conceal 4 hard work you can hide main form until loading 3 is done.

Assuming that Form1 - is your main 2 form and SplashForm is top level, borderles 1 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();
}
Score: 46

Well, for a ClickOnce app that I deployed 16 in the past, we used the Microsoft.VisualBasic namespace to handle 15 the splash screen threading. You can reference 14 and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 13 and it provides a lot of nice services.

  1. Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. Override 12 the "OnCreateSplashScreen" method like so:

    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

Very 11 straightforward, it shows your SplashForm 10 (which you need to create) while loading 9 is going on, then closes it automatically 8 once the main form has completed loading.

This 7 really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is 6 of course well tested by Microsoft and has 5 a lot of functionality that can make your 4 life a lot easier in Winforms, even in an 3 application that is 100% C#.

At the end of 2 the day, it's all IL and bytecode anyway, so why 1 not use it?

Score: 14

After looking all over Google and SO for 8 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 7 with the Microsoft.VisualBasic solution -- Worked find on XP, but 6 on Windows 2003 Terminal Server, the main 5 application form would show up (after the 4 splash screen) in the background, and the 3 taskbar would blink. And bringing a window 2 to foreground/focus in code is a whole other 1 can of worms you can Google/SO for.

Score: 10

This is an old question, but I kept coming 4 across it when trying to find a threaded 3 splash screen solution for WPF that could 2 include animation.

Here is what I ultimately 1 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();
  }
Score: 9

I recommend calling Activate(); directly after the 9 last Show(); in the answer provided by aku.

Quoting 8 MSDN:

Activating a form brings it to the front 7 if this is the active application, or 6 it flashes the window caption if this 5 is not the active application. The form 4 must be visible for this method to have 3 any effect.

If you don't activate your main 2 form, it may be displayed behind any other open 1 windows, making it look a bit silly.

Score: 6

I think using some method like aku's or Guy's is the 44 way to go, but a couple of things to take 43 away from the specific examples:

  1. The basic 42 premise would be to show your splash on 41 a separate thread as soon as possible. That's 40 the way I would lean, similar to what aku's 39 illustrated, since it's the way I'm most 38 familiar with. I was not aware of the VB 37 function Guy mentioned. And, even thought 36 it's a VB library, he is right -- it's all 35 IL in the end. So, even if it feels dirty it's 34 not all that bad! :) I think you'll want 33 to be sure that either VB provides a separate 32 thread for in that override or that you 31 create one yourself -- definitely research 30 that.

  2. Assuming you create another thread 29 to display this splash, you will want to 28 be careful of cross thread UI updates. I 27 bring this up because you mentioned updating 26 progress. Basically, to be safe, you need 25 to call an update function (that you create) on 24 the splash form using a delegate. You pass 23 that delegate to the Invoke function on your splash 22 screen's form object. In fact if you call 21 the splash form directly to update progress/UI 20 elements on it, you'll get an exception 19 provided you are running on the .Net 2.0 18 CLR. As a rule of thumb, any UI element 17 on a form must be updated by the thread 16 that created it -- that's what Form.Invoke 15 insures.

Finally, I would likely opt to create 14 the splash (if not using the VB overload) in 13 the main method of your code. To me this 12 is better than having the main form perform 11 creation of the object and to be so tightly 10 bound to it. If you take that approach, I'd 9 suggest creating a simple interface that 8 the splash screen implements -- something 7 like IStartupProgressListener -- which receives 6 start-up progress updates via a member function. This 5 will allow you to easily swap in/out either 4 class as needed, and nicely decouples the 3 code. The splash form can also know when 2 to close itself if you notify when start-up 1 is complete.

Score: 5

One simple way is the use something like 18 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 17 your application, it creates a 'main' thread 16 and starts executing your main() on that 15 thread. The Application.Run(myMainForm) at 14 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 13 to spawn a thread to take care of the splash 12 window, and in fact this is a bad idea, because 11 then you would have to use thread-safe techniques 10 to update the splash contents from main().

If 9 you need other threads to do background 8 operations in your application, you can 7 spawn them from main(). Just remember to 6 set Thread.IsBackground to True, so that 5 they will die when the main / GUI thread 4 terminates. Otherwise you will have to arrange 3 to terminate all your other threads yourself, or 2 they will keep your application alive (but 1 with no GUI) when the main thread terminates.

Score: 4

I posted an article on splash screen incorporation 2 in the application at codeproject. It is 1 multithreaded and might be of your interest

Yet Another Splash Screen in C#

Score: 4
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 2 cannot seem to find it again. Simple but 1 yet effective.

Score: 3

I like Aku's answer a lot, but the code 13 is for C# 3.0 and up since it uses a lambda 12 function. For people needing to use the 11 code in C# 2.0, here's the code using anonymous 10 delegate instead of the lambda function. You 9 need a topmost winform called formSplash with FormBorderStyle = None. The 8 TopMost = True parameter of the form is important because 7 the splash screen might look like it appears 6 then disappears quickly if it's not topmost. I 5 also choose StartPosition=CenterScreen so it looks like what a professional 4 app would do with a splash screen. If you 3 want an even cooler effect, you can use 2 the TrasparencyKey property to make an irregular shaped 1 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();
  }
Score: 2

I disagree with the other answers recommending 16 WindowsFormsApplicationBase. In my experience, it can slow your app. To 15 be precise, while it runs your form's constructor 14 in parallel with the splash screen, it postpone 13 your form's Shown event.

Consider an app 12 (without splashs screen) with a constructor 11 that takes 1 second and a event handler 10 on Shown that takes 2 seconds. This app 9 is usable after 3 seconds.

But suppose you 8 install a splash screen using WindowsFormsApplicationBase. You might 7 think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't 6 slow your app. But, try it, your app will 5 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 4 use WindowsFormsApplicationBase if your app has a handler on the Slown 3 event. You can write better code that runs 2 the splash in parallel to both the constructor 1 and the Shown event.

Score: 0

Actually mutlithreading here is not necessary.

Let 13 your business logic generate an event whenever 12 you want to update splash screen.

Then let 11 your form update the splash screen accordingly 10 in the method hooked to eventhandler.

To 9 differentiate updates you can either fire 8 different events or provide data in a class 7 inherited from EventArgs.

This way you can 6 have nice changing splash screen without 5 any multithreading headache.

Actually with 4 this you can even support, for example, gif 3 image on a splash form. In order for it 2 to work, call Application.DoEvents() in 1 your handler:

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}

More Related questions