0

My WinForms app's main window is slow to load (up to 20 seconds, depending on arguments), so it needs a splash screen.

The main window constructor is slow because it exercises thousands of lines of code (some of it beyond my influence). Sometimes this code pops up message boxes.

I've tried two splash screen designs, they each have problems. Any better ideas?

Splash screen with BackgroundWorker

static void Main(string[] args)
{
    var splash = !args.Contains("--no-splash");
    if (splash)
    {
        var bw = new BackgroundWorker();
        bw.DoWork += (sender, eventArgs) => ShowSplash();
        bw.RunWorkerAsync();
    }

    var app = new FormMain(args); // slow. sometimes opens blocking message boxes.
    Application.Run(app);
}

private static void ShowSplash()
{
    using (var splash = new FormSplash())
    {
        splash.Show();
        splash.Refresh();
        Thread.Sleep(TimeSpan.FromSeconds(2));
    }
}

Problems:

  1. Splash screen sometimes expires before main window open (user thinks app has crashed)
  2. Main window sometimes minimises when splash closes.

Splash screen with WindowsFormsApplicationBase

sealed class App : WindowsFormsApplicationBase
{
    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new FormSplash();
    }

    protected override void OnCreateMainForm()
    {
        // slow. sometimes opens blocking message boxes.
        this.MainForm = new FormMain(this.CommandLineArgs);
    }
}

Problems:

  1. Any MessageBoxes opened appear behind splash screen, silently. User won't notice it and thinks app is stuck.
  2. If splash screen is 'always on top', the message box is inaccessible and unclickable.
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 1
    Nothing in your posted code would duplicate the minimizing issue. Perhaps your FormApp is saving and using the last state? There is built-in support for [splash forms](http://stackoverflow.com/a/393870/719186). – LarsTech Jul 31 '13 at 16:31
  • 2
    Do not create the splash screen on a background thread and try to guess how long it will take to instantiate `FormApp`. `Close()` it when the instantiation is actually completed (just before `Application.Run(app)`). – JosephHirn Jul 31 '13 at 19:21
  • 13
    There are *very* serious problems with this code. Too many to mention but particularly nasty for its ability to get the SystemEvents class to start generating events on the wrong thread. Don't do this yourself, use the buitin [.NET framework support](http://stackoverflow.com/a/393870/17034) for splash screens. – Hans Passant Jul 31 '13 at 19:28
  • Is there code in the splash's closing event handler? – ps2goat Jul 31 '13 at 22:09
  • 1
    @HansPassant the standard library class has problems too—any message boxes appear behind the splash screen. Any ideas? – Colonel Panic Aug 01 '13 at 13:14
  • Could it possibly help to use an overload of `MessageBox.Show` that accepts an `IWin32Window`, e.g. [`MessageBox.Show(IWin32Window, string)`](http://msdn.microsoft.com/en-us/library/cked7698.aspx)? Would the message box appear in front of the splash screen if (a) a null handle, (b) the splash screen's window handle, or (c) the main form's handle were passed as `IWin32Window`? – stakx - no longer contributing Aug 05 '13 at 09:26
  • 2
    Possible duplicate of ["Dialog MessageBox sometimes hidden behind the main form"](http://stackoverflow.com/questions/3467403/dialog-messagebox-sometimes-hidden-behind-the-main-form). – stakx - no longer contributing Aug 05 '13 at 09:31

3 Answers3

3

I agree with Hans Passant in that the code needs to be re-evaluated as the design seems incorrect.

As for the problem at hand, you should be able to resolve this by creating your own instance of a messageBox.

I tested using this code;

public DialogResult TopMostMessageBox(string message, string title, MessageBoxButtons button, MessageBoxIcon icon)
    {
        return DisplayMessageBox(message, title, button, icon);
    }

public DialogResult DisplayMessageBox(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
    {
        DialogResult result;
        using (var topmostForm = new Form {Size = new System.Drawing.Size(1, 1), StartPosition = FormStartPosition.Manual})
        {
            var rect = SystemInformation.VirtualScreen;
            topmostForm.Location = new System.Drawing.Point(rect.Bottom + 10, rect.Right + 10);
            topmostForm.Show();
            topmostForm.Focus();
            topmostForm.BringToFront();
            topmostForm.TopMost = true;
            result = MessageBox.Show(topmostForm, message, title, buttons, icon);
            topmostForm.Dispose();
        }
        //You might not need all these properties...
        return result;
    }
//Usage
TopMostMessageBox("Message","Title" MessageBoxButtons.YesNo, MessageBoxIcon.Question)

Again, I need to stress that I agree that the original code needs to be re-factored and am only providing a possible solution to the question.

Hope this helps?

Hexie
  • 3,955
  • 6
  • 32
  • 55
0

You can implement our own message box and use TopMost property, with TopMost you will get message in-front of splash screen loader.

More about topmost: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.topmost.aspx

Dejan Dakić
  • 2,418
  • 2
  • 25
  • 39
-1

In the end, moved the slow code from the constructor to a handler for the OnShown event.

Used WindowsFormsApplicationBase for splash screen as Hans Passant suggested, carefully checked the remaining constructor code to make sure it'll never open an message boxes.

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465