Click here to Skip to main content
Click here to Skip to main content

Presidential Memory

, 24 May 2004
Rate this:
Please Sign up or sign in to vote.
This article describes a VB .NET project that has two Forms that do not have an MDI parent-child relationship

Introduction

Two scenarios that were easier to code in Visual Basic Version 6 are

  1. Having multiple forms which do not have an MDI parent-child relationship.
  2. Having a dynamic array of controls which are indexed and have a single method to handle a particular event for any member of the array.
This work illustrates ways to handle such situations in VB .NET using the context of a game.

Details

The game Memory, also known as Concentration, has pairs of images arranged randomly within a rectangle. A turn consists of a player's clicking on two PictureBoxes. If they match (i.e. have the same image), the player scores a point and continues. If the images do not match, the images are covered again and the next player takes a turn. Covering the mismatched images is done in code using a timer's Tick event -- a delay is required so that the player can see the images' locations and that they do not match. This version of the game also allows for a single player who plays against the clock.

This version of the game consists of two Windows Forms.

  1. The first Form is the setup Form (see the first screen capture above). It has two Panels. The upper Panel allows the player to select the size of the board by selecting the number of rows and columns using NumericUpDown controls. The lower Panel allows for various players' names to be added to a ListBox. The names can be removed as well as promoted or demoted within the list to alter the order of players. Below the Panels is the Play Button which moves one to the next Form.
  2. The second Form is the board Form (see the second screen capture above). It has two Panels. The upper Panel will hold a dynamic array of UserControls that will display the players' names and scores. The lower panel will hold a dynamic array of PictureBoxes that serve as the basic elements of the Memory game. Below the Panels are two Buttons. The first of these allows one to return to the setup form, and the second allows one to start a new game.

The code

Multiple Form Code

In order to move back and forth between two Forms, the specific names of the both Form objects (as opposed to just the names of the Form classes) must be known to the programmer. This can be achieved by explicitly instantiating both Form objects (with Public access) in the Sub Main of a code module and choosing the code module as the project's Startup object. Sub Main then calls the setup Form's ShowDialog method. The code module is shown below:

Module modStart
    'declare and instantiate an object of each of the form classes
    Public FrmBoard As New FrmPresidentialMemory
    Public FrmSetup As New FrmGameSetup

    'the program starts here and moves on to display the setup form object
    Public Sub Main()
        FrmSetup.ShowDialog()
    End Sub
End Module
It is important here to use the ShowDialog method and not the Show method since the ShowDialog method is modal and hence does not allow the Sub Main to complete execution until the Form in question is closed. With the modeless Show method, the Form appears briefly on the screen but is closed when Sub Main completes execution.

To move from the first Form (setup) to the second Form (board), one uses the combination

    Me.Hide()
    FrmBoard.ShowDialog()
again using the ShowDialog method. However, to move from the second Form (board) to the first Form (setup), one uses
     Me.Hide()
     FrmSetup.Show()
As the setup Form was previously hidden and not closed, use of the ShowDialog method here would cause an error. The Forms are not displayed in independent threads, closing either Form returns one to Sub Main and terminates the project.

Another question that arose was which Form Event should be used to load the dynamic control arrays. (Recall that the dynamic control arrays may be repeatedly loaded as the user can change the number of players or size of the game board.) The Help for the Load event states that it "occurs before a form is displayed for the first time" (author's emphasis). However, placing a MessageBox in the Load event code showed that it executed more than once as the user moved between the two forms despite the fact that the forms were hidden and not closed. (Changes made by the user to the setup Form persist implying that the Form remains in memory.) Because the description of Load in Help and its behavior in this program appeared to be inconsistent, using it was avoided. The code that only needed to be executed once (the reading of the file with the image file names) was moved to the constructor (Sub New) in the Windows Form Designer generated code after InitializeComponent(). On the other hand, the code that was to be executed each time the user moved from the setup form to the board form was placed in the VisibleChanged method as shown below.

Private Sub FrmPresidentialMemory_VisibleChanged(ByVal sender As Object, _
 ByVal e As System.EventArgs) Handles MyBase.VisibleChanged
        If Me.Visible Then
            SetUpBoard()
        End If
    End Sub
Since the VisibleChanged event is raised when the form is hidden and when it is shown, the If statement above limits the setup code execution to when the form is shown. Events such as Activated and GotFocus can be raised more often than desired for present purposes -- for instance, if the user is interacting with other applications on the desktop.

Dynamic Control Array Code

Setting up the board requires generating a dynamic array of picture boxes which consists of

  • Clearing the panel of any previous controls
  • Instantiating an array of PictureBoxes
  • Instantiating each element of the PictureBox array
  • Adding the new control to the panel
  • Determining the control's size
  • Determining the control's location
  • Determining any other (non-default) property of the control
  • Assigning the control a handler (or handlers)
This is shown is the code below.
    'remove game elements of any previous board setup
    panBoard.Controls.Clear()

    'instantiate array of pictureboxes for game elements 
    GameElement = New PictureBox(nRowNumber * nColumnNumber - 1) {}

    'instantiate and position individual game elements (pictureboxes) 
    'creates rows of game elements with nColumnNumber elements in each row
    'and assigns them an eventhandler
    For i = 0 To GameElement.GetUpperBound(0)
        GameElement(i) = New PictureBox
        panBoard.Controls.Add(GameElement(i))
        GameElement(i).Size = New System.Drawing.Size( _
          ELEMENT_WIDTH, ELEMENT_HEIGHT)
        GameElement(i).Location = New System.Drawing.Point( _
          GameElement(0).Width * (i Mod nColumnNumber), _
          GameElement(0).Height * (i \ nColumnNumber))
        GameElement(i).SizeMode = PictureBoxSizeMode.StretchImage
        AddHandler GameElement(i).Click, AddressOf GameElementHandler
    Next
In VB Version 6 the method handling the event of a dynamic array included an Index as part of its signature. In VB .NET the handler has the same signature as the method for a single control. The index is then determined by performing a search as shown in the code below.
    nFirstElementIndex = Array.IndexOf(GameElement, sender)

Strictly speaking one does not need an array of PictureBoxes, one could simply add individual controls to the panel as done in the article on Control Arrays by ManoRajan (http://www.codeproject.com/vb/net/Control_Arrays.asp) and use the panel's collection of controls. But there are at least two conveniences provided by having a genuine array.

  1. In the game of Memory, a turn consists of a player turning over two PictureBoxes that must be compared. Thus information about the first must be stored, and it is convenient simply to store its index.
  2. If one uses the control collection to loop over the PictureBoxes, one must cast the generic control as a PictureBox. Having an explicit array of a specific type eliminates the need for casting.
The use of control collection can be even more involved if there is more than one type of control on the Panel. In the upper Panel of the board form, there is a Label and a Textbox associated with each player. To make managing this area easier, a UserControl was introduced. The main purpose of the UserControl was the association of the Label and the TextBox, which requires no code beyond the Designer generated code. However, the PlayerName property was introduced so that one did not have to access the Text property of the Label within the UserControl, but instead needed only the UserControl's PlayerName property (e.g. MyScore(i).PlayerName replaces MyScore(i).lblName.Text). Similarly a Score Property and Increment subroutine were added. Furthermore, the LabelTextbox UserControl was designed specifically for this project so that the default size could be used when loading them onto the panel.

Other Minor Matters

  • The game Memory requires an even number of game elements. This condition was accomplished by insisting that the number of columns was even. The Value property of nudColumn (a NumericUpDown control) was set to 4 and the Increment property was set to 2. These settings impose the condition without the need for any additional code.
  • Most of the resizing/repositioning was handled using the anchor properties of the controls, but some code was still required as the number of players and number of game elements are unknown, and thus the proportions of the board are not known until runtime.
  • One must guarantee that a player clicks two and only two game elements in a turn. This was accomplished by setting the selected PictureBoxes' Enable property to False and also setting the panel's Enable property to False for mismatches. The Enable properties must be returned to True after the mismatched images are turned over.
  • There had to be logic for stopping the Timer when the game was finished or when the user disrupted a game by returning to the setup Form or by clicking the Play Again Button.
  • If no players are in the setup Form's ListBox when the Play Button is clicked, a single player named "Player 1" is added.
  • If there is only one player, then a TextBox is made visible since in this case the user is playing against the clock. If there are multiple players, then this TextBox is made invisible.
  • If there are multiple players, then the current player's name is underlined in the score display area. When a mismatch occurs, the player index changes as does the font of two labels. This action is accomplished by the following code
        'Move to next player and underline his or her name
        MyScore(nPlayerIndex).lblName.Font = New Font( _
          New FontFamily(MyScore(0).lblName.Font.Name), _
          MyScore(0).lblName.Font.Size, FontStyle.Bold)
        nPlayerIndex = (nPlayerIndex + 1) Mod MyScore.GetLength(0)
        MyScore(nPlayerIndex).lblName.Font = New Font( _
          New FontFamily(MyScore(0).lblName.Font.Name), _
          MyScore(0).lblName.Font.Size, FontStyle.Underline _
          Or FontStyle.Bold)
    

Acknowledgements

I would like acknowledge useful discussions with S. Longo and S. Wiley.

History

  • Original submission May 20, 2004.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Thomas Blum

United States United States
No Biography provided

Comments and Discussions

 
QuestionHow to dispose off the calling form Pinmemberaspireneo13-Aug-07 16:49 
GeneralPresidential Memory Pinmemberbghodsi25-Nov-06 8:42 
GeneralThis is good... Pinmemberdselec13-Jul-05 7:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140718.1 | Last Updated 25 May 2004
Article Copyright 2004 by Thomas Blum
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid