I've seen it a thousand times. Questions on message boards asking about multiple forms in the .NET era of languages. Heck, I was one of them. "The old way of Form2.Show doesn't work anymore." I'm not sure how true this is in desktop application development, but when it comes to smart device applications, it's true. To my knowledge, the Compact Framekwork doesn't (natively) support Form1.Show at the time of writing.

This tip is intended to help those trying to develop in the Compact Framework for SmartDevices. The example code below was written and tested in VisualStudio 2005 on the device emulator and an HP iPAQ rx1955 running Windows Mobile Version 5.0. Other devices have not been tested, but I see no reason why it wouldn't work.

Rather than using multiple forms, we will use multiple panels. Each new instance of a form, without a lot of complex code, uses a large of amount of precious memory--at least in SmartDevices. It also creates a longer load time.

Since panels can contain other controls, we can draw controls on separate panels like we would do on forms. Then, when we need to switch forms (panels), we just bring the panel to the front of the z-order stack. This can make for a very busy document outline on your form, but if you keep yourself organized, it shouldn't be too bad.

One thing before we get started. I'd like to pass some advice on how to keep organized. On long-time-held belief of mine is that you should name every control in your application with something that describes what type of control it is, what information it contains, AND where it is. For example, suppose you have a textbox called TextBox1 on PanelA, and another textbox called TextBox2 on PanelB. Now suppose you want to grab the value of the user's phone number which they entered into TextBox2, you'd have to remember that TextBox2 is where that phone number was entered. You'd be better off renaming it to txtPhoneNumber. But wait, what if that phone number was their home phone number and you asked for their work number in TextBox1 on PanelA? You can't use txtPhoneNumber twice because while they are on different panels, the panels are on the same form. Only on different forms can you use the same control name twice, though I wouldn't advise it even then. The best way to name your controls, in my opinion, is to be very specific about what information its supposed to contain and where its at. So, in our example, PanelA should be named pnlWorkInformation and TextBox1 should be named txtWorkInformation_WorkPhoneNumber. Thus, PanelB would be pnlHomeInformation, and TextBox2 txtHomeInformation_HomePhoneNumber. You're using the panel's name in the name of each control to identify where that control is drawn and a descriptive name for the information that the control contains. Alas, I digress.

For the purpose of this example, we are going to be making a home inventory application, specific to entertainment items. It will allow us to add, delete, modify, and list your collection of board games, movies, console games, computer games, and music CDs. I'm only going to show you how to make the program's user interface and code the navigational items; we aren't going to be coding the add, remove, etc., functions or messing with a database. I'll leave that up to you.

Step 1.) First, make a new device application project.
Step 2.) In the form properties, set the following:
Step 2A: Name: frmHomeEntertainmentInventory
Step 2B: Text: Home Entertainment Inventory
Step 3.) In the toolbox, select a panel and draw it on the form.
Step 4.) In the panel's properties, set the following:
Step 4A: Name: pnlBlankPanel
Step 4B: Location: 0, 0
Step 4C: Size: 240, 268
Step 4D: Enabled: True
Step 4E: Locked: False
Step 5.) Now right click, copy the new panel to the clipboard, and then paste. What we just did was create a blank panel from which to create all your other panels so that they all have the same properties. You could also set color preferences here, etc.
Step 6.) In the copy of pnlBlankPanel, edit the following properties:
Step 6A: Name: pnlMainMenu
Step 6B: Location: 0,0

Open the document explorer and notice that pnlMainMenu is a child of pnlBlank! This isn't what we want and it will give us some unexpected results in a moment if we don't fix it. So...

Step 7.) To fix, click the left arrow button at the top of the document explorer to make it a child of the Form, just as pnlBlank is a child of the form. Remember to do this for every copy of pnlBlank you make as I will not remind you past this step. Also remember to set the location to 0, 0.
Step 8.) Draw five buttons on pnlMainMenu.
Step 9.) Name the buttons as follows: btnMainMenu_BoardGames, btnMainMenu_Movies, btnMainMenu_ConsoleGames, btnMainMenu_ComputerGames, btnMainMenu_MusicCDs.
Step 10.) For the text property of each button respectively, type: "Board Games", "Movies", "Console Games", "Computer Games", and "Music CDs".
Step 11.) Create five copies of pnlBlankPanel, and name them "pnlBoardGames", "pnlMovies", "pnlConsoleGames", "pnlComputerGames", and "pnlMusicCDs".
Step 12.) Add a single button to each panel, naming them "btnXYZ_MainMenu", where XYZ is the name of the panel which that button is on, and set the text property for each button to "Main Menu."

Note: If you haven't noticed yet, you'll need to use the document explorer to navigate between the panels, bringing them to the front in order to work on them. Click on the panel you want in the document explorer and click the up button.

Now that we have all the panels we need, we need to add functionality. To do that, we need to add code to each of the buttons on the panels to have the panels brought to the front of the z-order stack. We have two options here. For each button, we can write pnlNameHere.BringtoFront(), or we can create a subroutine to do the work and just call the subroutine from the button's click event. Whichever you choose is up to you, but I like the subroutine approach. We'll start by creating our subroutine.

Step 13.) Bring up the code window for the form. It should have nothing in it but "Public Class frmHomeEntertainmentInventory" and "End Class".
Step 14.) Add the following code to it:

VB Code:
  1. 'Main subroutine to switch between panels...my "psuedo-forms".
  2.     Private Sub ShowPanel(ByVal strPanelToShow As String)
  3.         Select Case strPanelToShow
  4.             Case "pnlMainMenu"
  5.                 pnlMainMenu.BringToFront()
  6.             Case "pnlBoardGames"
  7.                 pnlBoardGames.BringToFront()
  8.             Case "pnlMovies"
  9.                 pnlMovies.BringToFront()
  10.             Case "pnlConsoleGames"
  11.                 pnlConsoleGames.BringToFront()
  12.             Case "pnlComputerGames"
  13.                 pnlComputerGames.BringToFront()
  14.             Case "pnlMusicCDs"
  15.                pnlMusicCDs.BringToFront()
  16.             Case Else
  17.                 MessageBox.Show("Panel requested (" & strPanelToShow & ") could not be " & _
  18.                 "found.", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, _
  19.                 MessageBoxDefaultButton.Button1)
  20.         End Select
  21.     End Sub

Let's discuss this code, line by line. The first line is obviously a comment to describe the purpose behind the subroutine. The second declared the subroutine, names it "ShowPanel" and sets the subroutine to receive a single string of information and save it in a string variable called "strPanelToShow". The third line opens a select case statement to test our strPanelToShow variable. In lines 4 through 10, test the variable for certain values and, based on those values, bring a specific panel to the front with the BringToFront() method. On line 11, we added some logic error handling to catch a possible typo on our part by using a Case Else statement. If our subroutine can't, for some reason, determine which panel to show, we show the user a message box describing what has happened. By the time your application is ready for packaging and deployment, there should be a 0% chance the user will ever see this code. It's mostly there while your application is in development and you may choose to comment this bit out prior to deployment. Finally, lines 12 and 13 close our select case statement and subroutine, respectively. Alternatively, you could set the .Visible() property to True, but this would require setting the other panels' visible property to False. In more complex applications, you may not know which panel we are switching from, so you'd also have to pass your subroutine this information in a variable called, for example, "strPanelToHide".

Now that we have our subroutine, we need to make use of it.

Step 15.) Navigate to pnlMainMenu in the document explorer and bring it to the front of your stack of panels.
Step 16.) Double click on the button you made for Board Games.
Step 17.) Add this code:

VB Code:
  1. ShowPanel("pnlMainMenu")

Step 18.) For each of the buttons click events, copy and paste the above code and change the text in the quotations as appropriate.
Step 19.) For each of the panels' buttons, copy and paste the above code and change the text to "pnlMainMenu". Your panels are done. Or, are they?
Step 20.) Have any panel but the main menu visible in the IDE and then run the application in debug mode on the device emulator or an actual device, whichever you prefer.