Making a Splash
By: Kenn Scribner for TechTalk
Background: I had played a bit with the splash screen component in Developer Studio while working on a prototype application on a new job. I wanted to show my new employer how a splash screen could be used to convey information about a client/server connection we needed to establish. But, the component really didn't do the job, as it was a static deal...no good, since I wanted to be able to update some text to show the progress of the server connection. So, I took a stab at it myself, and viola! It turned out well. Of course, my employer wasn't that impressed with a splash box, and they didn't use the prototype, but that's the developer's lot at times. :)
Special Thanks: Thanks to Mike Bryeans for catching a bug! Whenever you new, so shall you delete. Oops! Thanks, Mike!
Before we begin, let me apologize for the size of this issue's demonstration program. I wanted to give you an interesting application (shows some space shuttle photos), and the high-color bitmaps took up a lot of memory. Hopefully you'll find the download time worth the price, as we're going to do something I find very interesting in this issue--splash screens. What is a splash screen, you ask? Well, it's the window that "pops up" while your application is loading and initializing (for an example, bring up Word).
There are probably a hundred ways to bring up a splash screen, though I'll describe only two methods here, and one of those only in passing. The idea is to provide the user with something interesting to view while you're making lengthy initialization calls, or to show them registration or other information. I like use splash screens for showing the user the current state of my initialization sequence, such as when I'm establishing a connection to my server code. This provides visual feedback during a sometimes-lengthy process.
It's this informational exchange which makes me shy away from using the built-in splash screen which comes with Visual C++ (versions 4.2 and 5.0 both had/have this component). However, if you want a "quick and dirty" splash screen that merely shows an image, be sure to check out the splash screen component (menu item Projects->Add to Project->Components and Controls). This component is pretty limited--you may only use it with MDI/SDI applications and it uses a 16-color bitmap only. Hmm we can do better, at the cost of some additional code on our parts. Let's dig in!
Before we actually build a splash screen, let's see where the best code location might be for actually bringing one up. If you open your application source file, the one named "project".cpp, you'll find code in there to initialize 3D controls, the document/view architecture, read the command line parameters, and actually bring the window up on the screen (you may have other code, as well, depending upon what options you selected when you created the project). It's here we'll add the code to view the splash screen, as it's here we control when the application displays the main window. We can do any on-screen work now before we show the application's main window.
So where do we start when we build one of these puppies? First, we need to create a dialog box in the resource editor. I used the identifier IDD_SPLASH and these styles:
Dialog Box Properties Tab
|More Styles||Center Checkbox||Checked|
|More Styles||Visible Checkbox||Checked|
This will create a dialog box that is both visible and centered, with a "thick" border but no caption bar or system menu. The size of the dialog box doesn't matter, as we'll change that later, but I did remove the "OK" and "Cancel" buttons. If you want modifiable messages, such as "registered user name" and "company", definitely add the static text controls now (their location also won't matter at this time, though their viewable area length will).
Next, you need to get out your artist's hat and create a nice bitmap. Got it? Okay! If you want to be able to update text on the bitmap (using the static text controls you inserted), be sure to set an area aside specifically for that. Also, while you're in your paint program, determine the X and Y coordinates of the location(s) for your text messages (we'll hard-code these into the initialization of the splash screen).
Given our dialog box resource and our bitmap, we'll use the technique we examined last issue to draw the bitmap as the dialog box's background. We'll create a new dialog box class, CSplashDlg, and process WM_INITDIALOG. During our dialog box's initialization, we'll load the bitmap, resize the dialog, and move the static text controls in place, as in this CDialog::OnInitDialog() code segment from the demo:
// Size the window CRect rcRect; GetWindowRect(&rcRect); SetWindowPos(&wndTopMost, rcRect.left, rcRect.top, m_sBitmap.cx + GetSystemMetrics(SM_CXDLGFRAME), m_sBitmap.cy + GetSystemMetrics(SM_CYDLGFRAME), SWP_HIDEWINDOW); // Relocate and load text controls CSize sSize; CWnd* ctrl = GetDlgItem(IDC_USER); ctrl->GetWindowRect(&rcRect); sSize = rcRect.Size(); ctrl->MoveWindow(110,220,sSize.cx,sSize.cy,FALSE); ctrl = GetDlgItem(IDC_COMPANY); ctrl->GetWindowRect(&rcRect); sSize = rcRect.Size(); ctrl->MoveWindow(110,240,sSize.cx,sSize.cy,TRUE);
Notice the SetWindowPos() call here, we not only resize the window, but we also tell Windows that this dialog box needs to remain the topmost window. If we didn't do this, the splash screen could be "lost" if other windows were placed "on top" of it, such as the main application window as it initializes. When you examine the splash dialog's source code, you'll see I added the code from last issue to handle CWnd::OnEraseBkgnd() and CWnd::OnCtlColor(), as we did last issue.
The dialog box will be displayed when we process CWinApp::InitInstance(). Essentially, we'll bring the dialog box up as a modeless dialog (which we'll need to "kill off" later). Here is some code from the demonstration program:
// Bring up splash dialog (modeless) CSplashDlg* dlgSplash = new CSplashDlg; dlgSplash->Create(IDD_SPLASH,NULL); dlgSplash->m_strUser = dlgQuery.m_strUser; dlgSplash->m_strCompany = dlgQuery.m_strCompany; dlgSplash->UpdateData(FALSE); dlgSplash->RedrawWindow(NULL,NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ERASENOW);
Notice it's here I actually insert text into the splash dialog's static text controls. This is an advantage of doing a splash dialog and handling it here, as the built-in MFC support for Registry access (which we'll look at next issue) also comes from CWinApp. If you were to store the registration information within the Registry, you could easily access it now and use that information when updating the splash dialog's static text controls.
A good question would be "How long to keep this dialog box up?" That answer depends upon your application. I used the Win32 Sleep() API call to delay the application's main thread for a short time. This was just to keep the dialog visible. In reality, you'd process any command line arguments, explicitly load any dynamic link libraries, make server connections, or do some other processing which took some time to complete. Since the splash screen is just another dialog box, you are free to update the static text controls at will, such as when you send notice to the user regarding your processing status (waiting for this, doing that, etc.).
When you execute the demonstration program, you may notice some other small tidbits I added. For one, I removed the FWS_ADDTOTITLE bit in CMainFrame::PreCreateWindow() so MFC didn't try to add the title of my "document" to the caption bar. I may now set the window caption to whatever I like using CWnd::SetWindowText(). For another, I added a DrawBitmap() function to my view code to make it easier to "draw" a bitmap in the view window. You may find this is a handy routine sometime I know I do.
I also loaded 24-bit color bitmaps into the resource editor. Why is this a neat trick? Well, the resource editor knows about bitmaps, but only monochrome, 16 color, and 256 color bitmaps. With my scanned photos, though, this was inadequate. When I saw the 256 color renditions, I was appalled. To fix this, though, is very simple, and to my knowledge very undocumented (though I could be wrong!). Essentially, you load your bitmap into the resource editor as a 256 color bitmap. When you save it to disk, it will save the actual bitmap data in a file (typically in \res, but I "flatten" the project to make it easier to compress for your use). To have it load in a 24-bit color bitmap, simply overwrite the stored, 256 color bitmap file with the high-color bitmap file. That's it!
Personally, I like this splash dialog box technique, and I hope it adds that something extra to your applications. Next issue, we'll examine the Registry and the basic support MFC provides you when you need to deal with it. Don't worry! It isn't as scary as it seems. I've created the demonstration program in Visual C++ 5.0, so if you're using an earlier version, you'll need to rebuild the project file and add in my source files. You may download the demonstration source code (see top of page) -- it's about 1.5MB, so it'll take a little while to download, but as I mentioned earlier, I hope you'll find it's worth it. Until next issue, good luck!
Comments? Questions? Find a bug? Please send me a note!