Skip to content

IVSoftware/app-with-splash

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Here's my 2023 take on a 2011 question.


Over time, I've done this many times in many ways. The approach that currently use:

  • Force the main form Handle creation so that the message that creates the splash can be posted into the main form's message queue using BeginInvoke. This allows the main form ctor to return. Ordinarily the handle (the native hWnd) doesn't come into existence until it's shown. Therefore, it needs to be coerced while it's still hidden.

  • Override the SetVisibleCore() preventing the main window from becoming visible until the Splash has finished processing.

startup flow


public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        Debug.Assert(!IsHandleCreated, "Expecting handle is not yet created.");
        // Ordinarily we don't get the handle until
        // window is shown. But we want it now.
        _ = Handle;
        Debug.Assert(IsHandleCreated, "Expecting handle exists.");

        // Call BeginInvoke on the new handle so as not to block the CTor.
        BeginInvoke(new Action(()=> execSplashFlow()));
    }
    protected override void SetVisibleCore(bool value) =>
        base.SetVisibleCore(value && _initialized);

    bool _initialized = false;

    private void execSplashFlow()
    {
        using (var splash = new SplashForm())
        {
            splash.ShowDialog();
        }
        _initialized= true;
        WindowState = FormWindowState.Maximized;
        Show();
    }
}

Splash Example

The async initialization can be performed in the Splash class itself or it can fire events causing the main app to do things. Either way, when it closes itself the main form will set the _initialized bool to true and it is now capable of becoming visible.

public partial class SplashForm : Form
{
    public SplashForm()
    {
        InitializeComponent();
        StartPosition = FormStartPosition.CenterScreen;
        FormBorderStyle = FormBorderStyle.None;
    }
    protected async override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);
        if (Visible)
        {
            labelProgress.Text = "Updating installation...";
            progressBar.Value = 5;progressBar.Value = 25;

            // SIMULATED background task like making an API call or loading a
            // database (long-running task that doesn't require the UI thread).
            labelProgress.Text = "Loading avatars...";
            await Task.Delay(1000);

            labelProgress.Text = "Fetching game history...";
            progressBar.Value = 50;
            await Task.Delay(1000);
            labelProgress.Text = "Initializing scenario...";
            progressBar.Value = 75;
            await Task.Delay(1000);
            labelProgress.Text = "Success!";
            progressBar.Value = 100;
            await Task.Delay(1000);
            DialogResult= DialogResult.OK;
        }
    }
}

Releases

No releases published

Packages

No packages published

Languages