ALLINSIGHT

Home of the AlmostImplementedException

WPF: How to create squared custom controls and use them for board games

It may seem simple to generate a squared control, but it isn’t. At least when the control should work with different screen resolution and window sizes. Thats a must have for any WPF-Control.

In the old Windows-Forms days we would have listened to a resize-event on the height or width-property and told the otherone the new size. Of course, we can use the same way in a WPF-Application, but thats not the way to do it.
In WPF we use XAML to do all the surface-coding. Thats important to separate code and view for a better readability and maintainability.

But how to do it in WPF?
With bindings, of course! When you fully understood how bindings work, there are easy to use.
Except, when you try to bind to your own property in the same control.

Here is the way to do it:

In this example we do a binding between the actualheight and the width. If the actualheight is changed, width will get the same value.
For this, we have to give a self-reference with “RelativeSource Self” and a path to the property we want to listen to. That’s all we need to get our control squared.

But what do we do with this? A self-reference is useful for many things, as you will see on many occasions.In this blog-post we will use it to generate a squared board for games such as Sudoku, Chess or Go (a.k.a Baduk or Weiqi).

All these games use squared boards with cells. You need lines between the cells and logic for each cell.
But here we’ll lock at the frontend only. Instead of drawing all lines and cells with code, we’ll use wpf to do it. And you’ll see its usable on all devices, much faster on resize and supports diffrent board-sizes without codechange.

To archive all this, we use a DataTemplate like this one

This DataTemplate draws a black 1 pixel border and represents a cell on a borad. You can write the same directly in a WPf-Control, but this way we can change it more easily and support different styles.

Now we create another DataTemplate to use our CellTemplate

This DataTemplate is much more complicated, but for now we’ll ignore the UniformGrid and the ItemsControl. You will learn more about them in later blog-posts.
As ItemTemplate we use a StaticResource, our CellTemplate. That means for every Item that is generated from the ItemsControl, the CellTemplate is used to generate the look.
We use another binding as ItemSource. {Binding} may look like a self-reference, but it isn’t. Otherwise we wouldn’t need a complicated way liek discriped aboth. {Binding} is equal to {Binding Path=.} and thats a reference to the DataSource of the control.

What does this code do? It generates columns, as much as there are dataobjects in the Itemssource. Well, in other words a complete row.

With this snippet our code is complete. This time we use the RowTemplate as ItemTemplate and bind to a property called RowCollection.

As the DataObject we use a collection of collections, wich means its a 2-dimension array (matrix).
This ItemsControl use the outer collection and give every inner collection (row) into the RowTemplate. All generated row will be added to the UniformGrid, which will display them.

Now we’ll put the code together and add a litle change to the CellTemplate.

This class is the resulting XAML-Code, where the CellTemplate now use a Label to show us the value of a property called “Value” and on the other hand the C#-CodeBehind. As you may have noticed, I use 2 internal classes. That only for the sake of example. Don’t do that in a real programm. Thats bad coding-style.

In the WPF-Constructor Board(), after the initialize we assign a new CellCollection to the DataContext of the ItemsControl. The constructor of the CellCollection generates a collection of collections with Cell-Objects. The parameter “size” gives the size of the collection.
A numeric value is given to each cell, which will be displayed later through the CellTemplate.

And thats it! Now we have a dynamic game board which work only any resolution and windowsize.

To use this code, you need a WPF-ApplicationProject, the Code above as XAML-Class in the same project and the following 2 line in the MainWindow.xaml
The first line needs to be in the Window-Tag and the second one between the Grid.Tags.

Share :

, , ,

Comments are currently closed.