Introduction
There are many different approaches to work with displaying various forms one after the other when developing a Windows application. Let it be clear that I am not talking about MDI applications, but those applications which consists of various forms displaying information, taking input, and any other task associated to them. At a time, one or more form is displayed and when some command or operation occurs, that form is cleared away and a new form is displayed. This is easily conquered by:
Dim newForm As New Form2()
newForm.Show()
Which can be easily followed by removing the previous form, and can also allow data to be passed among the forms using either user defined constructors or simply accessing the control's properties and setting them through the new object created, for instance:
newForm.lblName.Text = oldForm.txtName.Text.Trim()
This is one of the most widely used methods of developing a Windows application, at least to my knowledge. But considering that one has to develop such an application where most of the form�s layout is to be the same for every form, for instance, menus, pictures, etc., why should each common control be repeated and coded over again? I, myself, have been through this experience. And having sought out expertise on this, I found out that most people went using the form swapping method even though their application had to reconstruct or create a large number of controls over and over again. It was then that I decided to go with Tab controls. Although not that hot looking, they did their work.
After this I turned towards using User Controls. And thus this article explains how to go about and use them. The best use of user controls with panels I have seen, to date, is the My Movie Collection Started Kit included in the Visual Studio Beta 2. (Note: I still have to look at the factor of memory consumption, for it might be that the old method could be a bit jerky, but faster and feasible.)
Using Paneled Forms (VS2003)
I will start by explaining how I managed to accomplish the afore-mentioned task using Visual Studio 2003 and .NET 1.1. Let�s start by creating a simple Windows form. My main application form (myMainForm.vb) can be seen in Figure 1. This form consists of a menu, a label, a picture, and two buttons. The grayish area in the right-hand corner is a panel. This panel will hold the user controls. The panel in my application is of 450x350. I will, thus make my user controls to be 450 x 350 too.
Thus next are the user controls that I use, they are as shown in Figures 2, 3, & 4.
Now, let us explore the code. On application start, the myMainForm is displayed, as shown in figure 1. Now when we press the �First Form� button, we want to display our first control shown in figure 2, and when we press �Second Form�, we want to display our second control shown in figure 3. To accomplish this, we use the following lines of code:
Private Sub btnFirst_Click(... , ...) Handles btnFirst.Click
Dim tempObject As New firstControl
pnlMain.Controls.Clear()
pnlMain.Controls.Add(tempObject)
End Sub
Private Sub btnSecond_Click(... , ...) Handles btnSecond.Click
Dim tempObject As New secondControl
pnlMain.Controls.Clear()
pnlMain.Controls.Add(tempObject)
End Sub
This piece is quite simple actually, and the magic lies in the two statements pnlMain.Controls.Clear()
and pnlMain.Controls.Add(tempObject)
. The first line releases the panel of any controls it might be holding or displaying, and the other sets the new control to be displayed in the panel. And voila! On button click, the appropriate user control is loaded into the panel.
Now suppose we want to change our control from within the control. This is extremely useful when we want to transfer some data from one control to another. If you look at figure 2, you will see a button labeled �Next!�. What this button does is that it gets the first and last name entered by the user and passes it to a new control being built, figure 4, which will display this information and can process on this information. Here is the appropriate function:
Private Sub btnNext_Click(... , ...) Handles btnNext.Click
Dim tempObject As New thirdControl
tempObject.lblFirstName.Text = "You entered the following" & _
" Start Name: " + Me.txtFirstName.Text.Trim()
tempObject.lblLastName.Text = "You entered the following Last Name: " _
+ Me.txtLastName.Text.Trim()
Me.Parent.Controls(0).Controls.Clear()
Me.Parent.Controls(0).Controls.Add(tempObject)
End Sub
The first few lines are quite easy to understand, as they simply initialize an object of the thirdControl
and set the two labels to their value. The tricky part comes when we want to swap the current control being displayed by the new one. As we are in the current control, we cannot directly access the panel on the main form. But we know that the parent is the main form, thus we simply access the panel through Parent.Controls(0)
and set the controls to be cleared. And then add the new control that is to be displayed.
The major problem in this method, which is removed in Whidbey, is that we have to be sure as to which index our control lies on, or else we might be playing with the wrong controls� To get the index of the control, we have to look in the InitializeComponent()
in the �Windows Form Designer generated code�. After the initialization of every component, at the very end, the form/user-control is initialized. In this case, myMainForm
is initialized as:
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.BackColor = System.Drawing.Color.LightGray
Me.ClientSize = New System.Drawing.Size(628, 442)
Me.Controls.Add(Me.pnlMain)
Me.Controls.Add(Me.btnSecond)
Me.Controls.Add(Me.btnFirst)
Me.Controls.Add(Me.picMain)
Me.Controls.Add(Me.lblMain)
Me.Menu = Me.myMenu
Me.Name = "myMainForm"
Me.Text = "Paneled Form Example"
Me.ResumeLayout(False)
We see that the first to be added is pnlMain
, i.e., the Panel
. Thus, it is at index 0. Similarly btnSecond
is at index 1, btnThird
is at index 2, and so on. We can also change the index number of a control. Let�s say that I want the panel to be at index 2. To do so, we simply add the panel at the third position, i.e., after two controls�
There is another scenario that remains to be discussed. And that is: what if we want to get some information to the main form from a user control being displayed. The solution is again quite simple, Panel1
being a member of the main form, we can access its properties from the code there.
Panel1.Controls(0).Controls.Item(0).Text
Panel1.Controls(0).Controls.Item(1).Text
Panel1.Controls(0).Controls.Item(2).Text
Panel1.Controls(0).Controls.Item(3).Text
This accesses the Panel
�s control at index 0. As we have been displaying a single user control at a time, the user control will be at index 0 of the panel. (We can include various user controls at the same time by using panels within panels.) Next we get the collection of controls of the user control, and select the item with its index, whose value we want. We can also do stuff like set the background color of a control, or change its font, or set an image, etc. In other words, after having accessed the item, we can perform any function on it, as long as it is appropriate. The problem being that if you get some item indexes wrong, then you might be using some specific function, like the Lines()
function of RichTextBox
, on some item that doesn�t provide the function. This will result in an error at runtime. (But then, Whidbey removes this problem by allowing names also instead of just indexes, check it out below.)
Using Paneled Forms (VS2005 Beta2)
As I mentioned earlier, this method is extremely mind-boggling, especially with one keeping track of indexes and parents, etc�
The same task has been made simpler in Whidbey. The major change being that now we can access the controls by their name. For instance, I can use the statement Controls.Item(�PnlMain�)
to access the Panel
control instead of having to worry about index number. And Controls.Item(�txtTextBox1�)
to access items in a control. This absolutely makes life a lot easier (Figure 5).
Note: Figure 5 represents a previous approach I was trying to do, and as I said in an earlier version of this article that Controls("PnlMain")
would do the trick. It turned out that it threw back errors. I spent the day trying to figure it out and found the right statement to use:
Me.ParentForm.Controls.Item("myPanel").Controls.Add(tempObject)
Note that instead of using Me.Parent
, I am now using ParentForm
. This is easy to handle, as a parent might be a GroupBox
, Panel
itself, or any other container...
Notice that I am accessing the controls of the ParentForm
and selecting the item "myPanel
".
Last Words
I am planning on writing a whole program using these techniques. I will upload the link to this article when I have done so.
This is me signing out. Please feel free to contact me at msk.psycho@gmail.com. Comments & criticism are both highly appreciated...