Saurabh R S
Saurabh R S

Reputation: 3177

Splash Screen not showing up until all the steps of updating label in it is complete

I have created a splash screen for a WinForm app. Everything works well until the splash screen is just a form with a background image and a label which has a static text - "Loading..."
I want to update the label continuously (with small delay in between) with texts - "Loading","Loading.","Loading.." and "Loading...". For this I have put the following code in my SplashScreen form:

private void SplashScreen_Load(object sender, EventArgs e)
{          
    lblLoading.Refresh();
    lblLoading.Text = "Loading.";
    Thread.Sleep(500);
    lblLoading.Refresh();
    lblLoading.Text = "Loading..";
    Thread.Sleep(500);
    lblLoading.Refresh();
    lblLoading.Text = "Loading...";
    Thread.Sleep(500);
}

Now the Splash Screen doesn't appear until the label in it gets updated to "Loading..."
Please help to let me know what I am doing wrong. Code in my HomeScreen:

public HomeScreen()
{  
    //....
    this.Load += new EventHandler(HandleFormLoad);
    this.splashScreen = new SplashScreen();
}

private void HandleFormLoad(object sender, EventArgs e)
{
    this.Hide();
    Thread thread = new Thread(new ThreadStart(this.ShowSplashScreen));
    thread.Start();

    //...Performing tasks to be done while splash screen is displayed

    done = true;
    this.Show();
}

private void ShowSplashScreen()
{
    splashScreen.Show();
    while (!done)
    {                
        Application.DoEvents();
    }
    splashScreen.Close();
    this.splashScreen.Dispose();
}

EDIT:
As suggested by some users here I have put the startup tasks in background thread and now I am displaying the Splash Screen from the main thread. But still the same issue persist. Here is the updated code of HomeScreen form:

    public HomeScreen()
{            
    //...
    this.Load += new EventHandler(HandleFormLoad);
}
private void HandleFormLoad(object sender, EventArgs e)
{
    this.Hide();
    SplashScreen sc = new SplashScreen();
    sc.Show();

    Thread thread = new Thread(new ThreadStart(PerformStartupTasks));
    thread.Start();            

    while (!done)
    {
        Application.DoEvents();
    }

    sc.Close();
    sc.Dispose();
    this.Show();
}

private void PerformStartupTasks()
{
    //..perform tasks
    done = true;
}  

and here's the Splash Screen :

    private void SplashScreen_Load(object sender, EventArgs e)
    {
        lblLoading.Update();
        lblLoading.Text = "Loading.";
        Thread.Sleep(500);

        lblLoading.Update();
        lblLoading.Text = "Loading..";
        Thread.Sleep(500);

        lblLoading.Update();
        lblLoading.Text = "Loading...";
        Thread.Sleep(500);
}

Upvotes: 0

Views: 1929

Answers (4)

Saurabh R S
Saurabh R S

Reputation: 3177

Thanks to all for answering my questions. Finally my issue is solved ! I changed my code such that start-up tasks are now being performed on a separate thread and splash screen is displayed from the Home Screen (main) thread. In the home screen I made use of Backgroundworker to update the 'Loading...' label.
I am posting my code here hoping it may also help someone in future..
For the Home Screen code plz see the EDIT part in my question. Here's the code of Splash Screen :

public SplashScreen()
    {
        InitializeComponent();
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.WorkerSupportsCancellation = false;
        lblLoading.Text = string.Empty;
    }

    private void SplashScreen_Load(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();            
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        while (true)
        {
            for (int i = 1; i <= 20; i++)
            {
                System.Threading.Thread.Sleep(200);
                worker.ReportProgress(i);
            }
        }
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        string dots = string.Empty;
        for (int k = 1; k <= e.ProgressPercentage; k++)
        {
            dots = string.Format("{0}..",dots);
        }
        lblLoading.Text = ("Loading" + dots);
    }

Upvotes: 1

Cyril Gandon
Cyril Gandon

Reputation: 17058

You have to define a background worker in your splash screen form.
Here is an example of what your splash screen could look like :

public partial class SplashScreenForm<T> : Form
{
    private BackgroundWorker _backgroundWorker;
    private Func<BackgroundWorker, T> _func;

    private T _funcResult;
    public T FuncResult { get { return _funcResult; } }

    public SplashScreenForm(Func<BackgroundWorker, T> func)
    {
        this._func = func;

        InitializeComponent();

        this.label1.Text = "";

        this._backgroundWorker = new BackgroundWorker();
        this._backgroundWorker.WorkerReportsProgress = true;
        this._backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
        this._backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
        this._backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);

        _backgroundWorker.RunWorkerAsync();

    }

    private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = sender as BackgroundWorker;
        if (worker != null)
        {
            _funcResult = this._func.Invoke(worker);
        }
    }

    private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (e.UserState != null)
        {
            this.label1.Text = e.UserState.ToString();
        }
    }

    private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.Close();
    }
}

You can design it the way you want, maybe a pretty gif image, or whatever you can think of.

Call it this way :

private void HandleFormLoad(object sender, EventArgs e)
{
    this.Hide();
    var splash = new SplashScreenForm<bool>(PerformTask);
    splash.ShowDialog(); // function PerformTask will be launch at this moment
    this.Show();
}

private bool PerformTask(BackgroundWorker worker)
{
    //...Performing tasks to be done while splash screen is displayed
    worker.ReportProgress("loading..");

}

Upvotes: 0

Guffa
Guffa

Reputation: 700650

You should handle the splash screen in the main thread, and the background work of initialising in the background thread (just as logixologist commented).

That said, the reason that your changed message doesn't show up is because the main thread is busy so it doesn't handle the events that redraws the control. Calling DoEvents in a background thread will only handle messages in that thread, and the messages to update the splash screen is in the main thread.

The Refresh method only invalidates the control, which would cause it to be redrawn if the main thread handled the event. You can use the Update method to force the redraw of the control:

private void SplashScreen_Load(object sender, EventArgs e)
{          
    lblLoading.Text = "Loading.";
    lblLoading.Update();
    Thread.Sleep(500);
    lblLoading.Text = "Loading..";
    lblLoading.Update();
    Thread.Sleep(500);
    lblLoading.Text = "Loading...";
    lblLoading.Update();
}

But, this is only a workaround for the current code. You should really make the main thread handle the messages.

Upvotes: 1

noelicus
noelicus

Reputation: 15055

You want a BackgroundWorker that posts ProgressChanged event, which will update the splash screen. The progress changed object could be a string, for example, that you'll display on your splash screen (back on the GUI thread).

Upvotes: 1

Related Questions