Tuesday, November 13, 2007

Creating a Splash screen for your .Net CF Application

As you already know, performance is a critical issue in any mobile solution. We have to take care of many things, but probably the more important aspect of any mobile development improvement, should be the UI responsiveness.

If the user press a button and wait for 15 seconds with no feedback until the action is finally done, he'll feel the sensation of something going wrong with the application, or probably the need to press the button again. Sometimes a good option is to initialize some load-expensive resources at start-time. The problem here is we already have performance expensive tasks while the application is being launched, and due to adding optimization tries we can found ourselves running an application which takes 20 seconds until the first form is show.

Here's where splash screens help us. It's radically different an application that only shows a wait cursor during 20 seconds until the first form appears than an application which shows the splash screen after 4 seconds letting the user know something is going on, and showing progress during the next 25 seconds even if it's taking more than the original 20 seconds. The key here is feedback, information. The user knows what is going on. He has information, he knows the application is already running, even if it still being loaded.

image

Add a splash screen to your mobile application can looks like a piece of cake, but it's not. There are three important guidelines that you should follow if you want to get the advantages of having a splash screen:

  • It should be shown as soon as possible.
  • Show the splash screen should be a light weight process.
  • It should show progress (or activity at least).

As a .Net CF application usually starts showing a main form, it's recommendable to show the splash screen while the form is being initialized. As we've seen before, it should be a light weight process; ideally it should be as light as possible. A very good option is to create a very simple Splash Form and draw the splash screen directly overriding the OnPaint method. The Splash Form should be maximized (WindowState = Maximized) and totally empty (free of menus or controls).

Then, we can override OnPaintBackground leaving it empty to improve the performance (we don't need to paint a background here anyway), and draw the SplashScreen manually overriding OnPaint:

protected override void OnPaint(PaintEventArgs e)    
{
    Font font = new Font("Arial", 10, FontStyle.Bold);
    if (backgroundBmp == null)
        backgroundBmp = (Bitmap)Properties.Resources.ResourceManager.GetObject("SplashBitmap");
    e.Graphics.DrawImage(backgroundBmp, 0, 0);
    e.Graphics.DrawString("Splash Screen Sample", font, new SolidBrush(Color.Yellow), 37, 30);
    e.Graphics.DrawString("http://www.mobilepractices.com", font, new SolidBrush(Color.White), 7, 50);
    e.Graphics.DrawString("Loading...", font, new SolidBrush(Color.White), 65, 78);

    ShowProgress(0);
}

In this code, we're drawing the full splash screen, but the progress is painted in a different method called "ShowProgress". It will show the progress painting directly on the form graphics:

public void ShowProgress(int percentage)    
{
    Graphics gr = this.CreateGraphics();
    gr.DrawRectangle(new Pen(Color.Black), new Rectangle(32, 95, barwidth, 12));
    gr.FillRectangle(new SolidBrush(Color.Black), new Rectangle(32, 95, (barwidth * percentage) / 100, 12));
}

This method will update the progress quickly while your application is being initialized, without the need of paint the full splash screen for each update.

On the main form constructor on your application ("Form1.cs" in the sample code), you should create and show the splash form for first time, and let the system process the events (Application.DoEvents()) to get the splash visible. This will use OnPaint (and probably this will be the only time OnPaint is called) to draw the splash. From this point we'll use ShowProgress to update the splash showing progress, like in the following sample code (where thread.Sleep is used to simulate some expensive initialization tasks):

public void Initialize(SplashForm splash)    
{
    //Show the splash
    splash.ShowProgress(30);
    //... intialization first steps
    Thread.Sleep(1000);
    //... update splash
    splash.ShowProgress(50);
    //... some intialization steps

...

I'm including a sample application for Windows Mobile 6 Standard Edition (formerly Smartphone). Hope it helps:

5 comments:

Anonymous said...

Hi Jose,

I am new to windows mobile application,when i add the splashscreen form into my project im getting error as NullReference

backgroundBmp = (Bitmap)Properties.Resources.ResourceManager.GetObject("SplashBitmap");

SplashBitmap is in Resource Folder only,but still i cant find error can you help me.

Regards
Bahru

Trupti said...

Thanks for this nice and easy application. I was looking out for something like this only. Open a window from main class and close the window to release from memory. I didn't wanted the opened window to be as a main window and keep it hidden. It simply utilizes resources that are of no use.

Thanks

Trupti said...

Hello,

Thanks for this nice and simple article. It helped me achive what I was looking for.

But I have a query and hope you will be able to help me out:
I have added the screen and code to shpw the screen as you have mentioned. Now, in my Form_Load, I read 2 csv files. Form load is being executed after splash screen loading is completed. Shouldn't that be executed in Application.DoEvents().

What should I do to read those files during the splash screen is being shown? And how should be time be set based on the time required to read the files.

I look forward for your response at truptikm@hotmail.com or here on this site.

Thanks

Jose Gallardo said...

First of all, sorry for the delay, these were very busy weeks.
Regarding your question, in my sample you can find some placeholders that currently say something like:

//...Initialization first steps
Thread.Sleep(1000);

You should replace Thread.Sleep(1000) with a call to your LoadCSV code.
About how many percentage it should take, that's a very subjective question, it depends on your business actually. You should be able to estimate how many time it can take from the whole intialization process, and if it depends on the size of the csv file, you can use the file size to improve your estimation.
i.e. If you load a 100K file and it takes 2 seconds, you can estimate that each 100K will take 2 secs, so you can estimate better how it will take based on the actual size of the file to read.
Btw, if it will take several seconds, I recommend you to update the progress bar while you're loading the files.
i.e. if your file is 500K and you estimate that will take 50 percent of your intialization, then you can update it in 10 percentage steps every 100K loaded.
To achieve that, you may need to pass your Splash form and the current progress as parameters to your loading method.

HTH!

Anonymous said...

Excellent, thank you!