Tag Archives: chessboard

WPF’s Grid layout in XAML and C#

ZIndex

With the calculator example we’ve seen a simple example using WPF’s Grid layout. In that example, we saw how to position controls within a Grid and how to make a control span more than one column or row.

One feature of a Grid that we didn’t mention there was the ZIndex. Remember that every object placed in a Grid must have its row and column specified. You might think this is a bit tedious; why not just position objects from left to right, top to bottom, as they are added to the XAML?

The reason for this is that a Grid is flexible as to where objects can be placed, so that you need not fill up every cell in the grid with a control. We saw an example of this in the calculator example, with the 0 button placed in the centre of the bottom row, with no button on either side of it.

Another reason for requiring row and column to be given explicitly is that you can place more than one object in the same cell. The order in which these objects are stacked in the cell is given by each object’s ZIndex. The term ‘ZIndex’ arises from thinking of the plane in which the design is drawn as the x-y plane, with a z axis sticking out of the screen towards you. Objects with a higher ZIndex are further away from the x-y plane and thus appear stacked on top of objects with lower ZIndexes.

As a simple example (which isn’t exactly good interface design, but never mind), suppose we wanted to stack 3 buttons into the same cell in a Grid. The buttons decrease in size as we increase the ZIndex, so smaller buttons are stacked on top of larger ones.

The program is called NestedButtons and looks like this when run:

The interface was designed entirely in EB without writing any XAML code by hand, but the resulting code looks like this:

	<Grid x:Name="LayoutRoot" HorizontalAlignment="Center" VerticalAlignment="Center">
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="Auto"/>
		</Grid.ColumnDefinitions>
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
		</Grid.RowDefinitions>
		<Button Content="Stop" HorizontalContentAlignment="Center" Width="150" Height="150" HorizontalAlignment="Center" Background="Red" VerticalContentAlignment="Top" Focusable="False" FontWeight="Bold"/>
		<Button Content="Pause" Background="#FFE6FF00" Width="100" Height="100" Panel.ZIndex="1" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Top" Focusable="False" FontWeight="Bold"/>
		<Button Content="Go" Margin="0" VerticalAlignment="Center" Background="#FF00FF04" Panel.ZIndex="2" Width="50" Height="50" HorizontalAlignment="Center" Focusable="False" FontWeight="Bold"/>
	</Grid>

We’ve created a single row and column and placed all three buttons in that cell. The ‘Stop’ button in red is on the bottom and has a ZIndex of 0 (this is the default value). The yellow ‘Pause’ button has a ZIndex of 1 and the green ‘Go’ button a ZIndex of 2. When the program is run, pressing an exposed area of a button will trigger any events connected to that button but not to the button beneath it. (We haven’t included event handlers here but they are easily added as we showed in the earlier post.)

WPF in C#

For simple layouts, or layouts containing a lot of different controls placed at various positions around the Grid, it makes sense to use Expression Blend (EB) to do the layout. However, if we’re creating a layout that has a lot of elements positioned at easily calculable locations, it can get tedious positioning all of them using EB. In this case it makes a lot more sense to write C# code to do the job for us.

For example, suppose we wanted to design a chessboard layout like this:

We need 8 rows and 8 columns, and we need to fill in each square with the correct colour. Doing all this in EB is possible, but tedious. It is easier (once you know what code to write) to do this in the C# code. It’s also useful to know how to access the various components of a layout in C#, since sometimes you’ll want to change some of their properties in response to an event.

The complete project is available here.

By the way, I should add that if you find some of the following code obscure, in the sense of “I’d never have known how to do that.”, I was in much the same position when I started this example and discovered the bits and pieces by googling. Remember, google is your friend when you’re writing code that relies on a lot of library functions.

First, let’s look at the complete XAML code for this example. There’s not much of it, since most of the code is in C#.

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	x:Class="Chessboard.MainWindow"
	x:Name="Window"
	Title="Chessboard" SizeToContent="WidthAndHeight" ResizeMode="NoResize">

	<Grid x:Name="LayoutRoot"/>
</Window>

The only things we’ve done in EB are the addition of the SizeToContent and ResizeMode properties in the main window. SizeToContent makes the main window size itself to the width and height of the control it contains (in this case the Grid). This means we can change the size of the chessboard without having to edit the code to change the size of the window. The NoResize option means (surprise!) that the main window can’t be resized.

Now we switch over to Visual Studio (VS) to do the editing of the C#. (You can edit C# code in EB, but it’s not a pleasant experience, so VS is strongly recommended.) Here’s the complete code (apart from the ‘using’ statements at the top, which are just those generated by VS) for the MainWindow class in the C# file:

  public partial class MainWindow : Window
  {
    const int squareSize = 50;
    public MainWindow()
    {
      this.InitializeComponent();

      // Insert code required on object creation below this point.
      InitializeChessboard();
    }

    private void InitializeChessboard()
    {
      GridLengthConverter myGridLengthConverter = new GridLengthConverter();
      GridLength side = (GridLength)myGridLengthConverter.ConvertFromString("Auto");
      for (int i = 0; i < 8; i++)
      {
        LayoutRoot.ColumnDefinitions.Add(new ColumnDefinition());
        LayoutRoot.ColumnDefinitions[i].Width = side;
        LayoutRoot.RowDefinitions.Add(new RowDefinition());
        LayoutRoot.RowDefinitions[i].Height = side;
      }

      Rectangle[,] square = new Rectangle[8,8];
      for (int row = 0; row < 8; row++)
        for (int col = 0; col < 8; col++)
        {
          square[row, col] = new Rectangle();
          square[row, col].Height = squareSize;
          square[row, col].Width = squareSize;
          Grid.SetColumn(square[row, col], col);
          Grid.SetRow(square[row, col], row);
          if ((row + col) % 2 == 0)
          {
            square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(255, 255, 255));
          }
          else
          {
            square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(0, 0, 0));
          }
          LayoutRoot.Children.Add(square[row, col]);
        }
    }
  }

A fair number of things are going on here, so let’s step through it.

Line 3 defines the size of each square on the board. This is the only place where an actual number is used for the size, so changing this value will change the board size and the size of the window that contains it.

Line 9 calls InitializeChessboard() which is where we’ve written all the new code.

Lines 14 and 15 create a GridLengthConverter which is used for setting the width and height of the Grid cells. The reason we need something fancy like this is because the width and height could be just numbers, but they can also be things like ‘Auto’, so they fit their contents. All these possibilities are encapsulated in a GridLength object, so we need to create one of them. In this case, we want the Grid cells to fit themselves to their contents, so we specify ‘Auto’ as the value.

The loop on lines 16 to 22 creates the columns and rows within the grid. This is what we’ve been doing up till now by using EB’s editor to add columns and rows. You can see that once you know what to call in the C#, it is a lot more concise if you have a lot of rows and columns to add.

On line 24, we declare a two-dimensional array of Rectangle objects. A Rectangle is a graphical element that just draws a rectangle. Within the double loop starting on line 25, we create each square on the board as a separate Rectangle and assign its height and width (these are absolute values).

On lines 31 and 32 we call static methods in the Grid class to assign the grid row and column to each Rectangle. Remember if we had done this in XAML, the row of a control is specified in the control’s properties as ‘Grid.Row=1’. At this point we are assigning properties to the Rectangle; we haven’t yet provided any connection between the Rectangle and the actual Grid (LayoutRoot) that it will be attached to.

The if statement on lines 33 to 40 determines the colour of each rectangle. We use the fact that if the sum of the row and column indexes is even, the square is white, otherwise it is black. The cryptic method for filling a Rectangle with a colour was found just by googling; nobody would expect you to be able to figure this out on your own (I’d guess).

Finally, on line 41, we add the Rectangle to the specific LayoutRoot Grid.

Advertisements