[C# Helper]
Index Books FAQ Contact About Rod
[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

[C# 24-Hour Trainer]

[C# 5.0 Programmer's Reference]

[MCSD Certification Toolkit (Exam 70-483): Programming in C#]

Title: Create controls for a tic-tac-toe application at run time in C#


This example shows how a C# program can create controls at runtime. It demonstrates several techniques that may be useful if you're creating a board game such as tic-tac-toe (naughts and crosses). They include:

Creating and Configuring a TableLayoutPanel Control at Run Time

This example makes a tic-tac-toe board. Each square is implemented by a Label control. The Labels and the Clear Button are arranged in a TableLayoutPanel.

The program uses the following code at the class level to make using the controls easier.

// Make an array to hold the Labels. private const int NumRows = 3; private const int NumCols = 3; private Label[,] Squares = new Label[NumRows, NumCols];

This code just declares a couple of constants (which you can change to make a bigger board, such as a chess board) and creates an array to hold references to the board's Label controls. That'll make using the controls easier later.

The form's Load event handler creates all of the form's controls. The following code shows how it creates and configures the TableLayoutPanel control.

// Make a TableLayoutPanel. TableLayoutPanel tlpBoard = new TableLayoutPanel(); tlpBoard.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; tlpBoard.Location = new Point(10, 10); tlpBoard.Size = new Size( ClientSize.Width - 20, ClientSize.Height - 20); Controls.Add(tlpBoard); // Make the TableLayoutPanel rows and columns. tlpBoard.RowCount = NumRows + 1; tlpBoard.ColumnCount = NumCols; // Give the first row a fixed height. tlpBoard.RowStyles.Clear(); tlpBoard.RowStyles.Add(new RowStyle(SizeType.Absolute, 30)); // Give the other rows and columns the same percent size. for (int row = 0; row < NumRows; row++) tlpBoard.RowStyles.Add( new RowStyle(SizeType.Percent, 1)); tlpBoard.ColumnStyles.Clear(); for (int col = 0; col < NumCols; col++) tlpBoard.ColumnStyles.Add( new ColumnStyle(SizeType.Percent, 1));

The code first creates the TableLayoutPanel and sets the control's Anchor property so it attaches to all four of the form's edges. It then sets the control's location and size so it remains 10 pixels from the edges of the form. The code then adds the control to the form's Controls collection. (If you forget to do that, the TableLayoutPanel and the controls it contains will not appear.)

Next the code defines the TableLayoutPanel's rows and columns. It sets the control's RowCount and ColumnCount properties, adding an extra row to hold the Clear Button.

The TableLayoutPanel's RowStyles and ColumnStyle collections contain objects that determine the row and column sizes. The objects in those collections set the styles for the corresponding row or column. For example, the second object in the RowStyles collection sets the style for the second row.

This code clears the RowStyles and ColumnStyle collections and then adds new RowStyle and ColumnStyle objects to them to set the row and column styles. This example gives the first row (which will hold the Clear Button) a fixed height of 30 pixels. It makes the other rows and all of the columns have Percent sizes. It sets all of their percent values to 1 so they all have the same relative sizes. (In other words, all of the other rows have the same heights, and all of the columns have the same height.)

Making a Button and Giving it an Event Handler

The following code shows how the program creates its Clear Button.

// Make a Clear button. Button btn = new Button(); btn.Text = "Clear"; btn.Click += btnClear_Click; btn.Dock = DockStyle.Fill; tlpBoard.SetRow(btn, 0); tlpBoard.SetColumn(btn, NumCols / 2); tlpBoard.Controls.Add(btn);

This code creates a new Button object. It then "adds" the btnClear_Click event handler to the Button's Click event. That registers the event handler to catch the Button's Click event.

Next the code sets the Button's Dock property to Fill so it fills its cell in the TableLayoutPanel.

The program then uses the TableLayoutPanel's setRow and setColumn methods to set the control's position in the TableLayoutPanel. (This is typical of the way you set properties provided by an "extender provider." An extender provider is a class that adds new properties to existing controls. In this example, the TableLayoutPanel is adding Row and Column properties to the controls it contains. To set extender provider properties, you typically call a method provided by the extender provider.)

Finally the code adds the new Button to the TableLayoutPanel's Controls collection.

Storing Controls in an Array

The following code shows the rest of the form's Load event handler, which creates the Labels that represent the board's squares.

// Make a big font. Font square_font = new Font("Times New Roman", 40); // Add Labels. for (int row = 0; row < NumCols; row++) for (int col = 0; col < NumCols; col++) { // Create the Label. Label lbl = new Label(); lbl.Margin = new Padding(0); lbl.Dock = DockStyle.Fill; lbl.TextAlign = ContentAlignment.MiddleCenter; lbl.Font = square_font; lbl.BorderStyle = BorderStyle.FixedSingle; Squares[row, col] = lbl; tlpBoard.Controls.Add(lbl); // Set its row and column. tlpBoard.SetRow(lbl, row + 1); tlpBoard.SetColumn(lbl, col); // Hook up a Click event handler. lbl.Click += square_Click; }

For each row and column on the board, this code creates a new Label control and sets some fairly obvious properties. It then saves a reference to the new control in the Squares array. It then adds the Label to the TableLayoutPanel's Controls collection, sets its row and column, and adds the following square_Click event handler to the control's Click event.

// The user clicked a square. private void square_Click(object sender, EventArgs e) { Label lbl = sender as Label; if (lbl.Text == "X") lbl.Text = "O"; else lbl.Text = "X"; }

This event handler simply toggles a Label's text between X and O.

Using Controls in an Array

The reason for adding the Label controls to the Squares array is so the code can refer to the controls by row and column. The following btnClear_Click event handler uses the array to clear each Label's text.

// Clear the labels. private void btnClear_Click(object sender, EventArgs e) { foreach (Label lbl in Squares) lbl.Text = ""; //for (int row = 0; row < NumRows; row++) // for (int col = 0; col < NumCols; col++) // Squares[row, col].Text = ""; }

This code uses a foreach loop to through the Labels in the Squares array and sets each one's Text property to an empty string. (The commented code shows another way to loop through the array.)

In a real tic-tac-toe program, you might also want methods that loop through the array looking for wins.

Download the example to experiment with it and to see additional details.

© 2009-2023 Rocky Mountain Computer Consulting, Inc. All rights reserved.