In this article I will finish my Tic Tac Toe game tutorial. In my
previous lesson I have created a custom user control and I am going to use that control also in this one. Basically, application will consist of two custom XAML files:
- TicTacToeButton.xaml (from my previous lesson)
- Gameboard.xaml (gameboard itself the functionality to initialize UI elements and to analyze player moves)
Requirements:
- Visual Studio 2010 with Windows Phone Developer Tools
- Windows Phone 7 emulator
- Silverlight 4+
- Custom User Control created in my previous lesson
Source code:
- Latest version of Tic Tac Toe game is located on CodePlex website.

Step 1: Creating new project
First of all you need to create a new project. To do so open Visual Studio 2010 -> File -> New Project -> select
Windows Phone Application there as it is shown on picture bellow.
Step 2: Changing default XAML page
After new project is created we can change the default XAML page from MainPage.xaml to Gameboard.xaml. It can be done within WMAppManifest.xml file (Properties package).
There you need to change the NavigationPage value of DefaultTask element:
<Tasks>
<DefaultTask Name ="_default" NavigationPage="Gameboard.xaml"/>
</Tasks>
Step 3: Adding and setting custom controls up
After that we import a custom user control TicTacToeButton from my previous lesson (check source files)
Then we add following property to a TicTacToeButton control:
private bool? isCross = null;
public bool? IsCross
{
get
{
return isCross;
}
set
{
VisualStateManager.GoToState(this, (value.HasValue ? (value.Value ? "Cross" : "Nought") : "Default" ) , false);
isCross = value;
}
}
Changing this property will make TicTacToeButton change it's visual state to
cross,
nought or
default. If value in setter is "null" then button has "Default" state applied (meaning nothing is shown), if value is "true" then state of button will change to "Cross" and, finally, if value is "false" then "Nought". For example, if TicTacToeButton's state is "Default" then neither of players have not pushed that button yet. Also make sure that LayoutRoot grid in TicTacToeButton had
Opacity set to 1.
<Grid x:Name="LayoutRoot" Width="60" Height="60" Opacity="1" Background="Transparent">
Step 4: Creating a gameboard
Now let's move on to our Gameboard. Create a new Windows Phone portrait page and call it Gameboard. There we need to change ContentGrid in XAML. We will add a grid for a gameboard with 9 instances of TicTacToeButton, 2 horizontal lines and 2 vertical lines as inner borders, a textblock displaying current status of application and a "Play again?" button. My advice to you is to use
Microsoft Expression Blend 4 (with Silverlight 4 support), for example, for drawing custom lines as grid borders to make them look more realistic. ContentGrid XAML will now look like that:
<Grid x:Name="ContentGrid" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Height="53" HorizontalAlignment="Left" Margin="72,78,0,0" Name="tbStatus" Text="TextBlock" VerticalAlignment="Top" Width="340" FontSize="32" TextAlignment="Center" Grid.Row="0" />
<Canvas x:Name="canvasGameboard" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="1" Height="200" Width="200">
<Grid x:Name="gridGameboard">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Path Grid.Column="1" Data="M64.333336,4.6666665 C62.924431,20.164593 64.127037,36.65147 65.333336,52.333332 C66.558273,68.257599 63.498032,84.145035 65,100.66666 C66.361046,115.63819 64.666664,132.64523 64.666664,148.33333 C64.666664,156.89328 64.333336,165.07155 64.333336,173.66667 C64.333336,182.08456 67.718994,189.40503 66,198 C65.777779,197.55556 65.623878,197.06978 65.333336,196.66667 C65.241455,196.5392 65.090111,196.46207 65,196.33333 C64.909889,196.20461 64.758545,196.12747 64.666664,196 C64.521393,195.79845 64.534637,195.18771 64.333336,195.33333" Margin="3.365,4.667,2.513,1" Grid.RowSpan="5" StrokeStartLineCap="Flat" Stretch="Fill" StrokeEndLineCap="Flat" Stroke="White" StrokeThickness="2" StrokeMiterLimit="10" StrokeLineJoin="Miter" UseLayoutRounding="False"/>
<Path Grid.Column="3" Data="M134.33333,4.3333335 C132.37093,20.567755 133.83511,35.186302 135.33333,51.666668 C136.78621,67.648277 134,82.84716 134,99 C134,115.52766 130.90732,134.11053 133.66667,150.66667 C135.1199,159.38612 133.33333,166.49176 133.33333,175 C133.33333,183.39162 131.75618,189.55441 134,197.66667" Margin="1.521,3.333,3.247,1.333" Grid.RowSpan="5" StrokeStartLineCap="Flat" Stretch="Fill" StrokeEndLineCap="Flat" Stroke="White" StrokeThickness="2" StrokeMiterLimit="10" StrokeLineJoin="Miter" UseLayoutRounding="False"/>
<Path Grid.ColumnSpan="5" Data="M195.66667,65.333336 C180.5721,67.724548 165.25514,67.598076 149.66667,65 C132.7099,62.173874 116.64419,66.333336 99.666664,66.333336 C67.673195,66.333336 35.10807,66.211624 3,65" Margin="2,3.016,3.333,1.958" Grid.Row="1" StrokeStartLineCap="Flat" Stretch="Fill" StrokeEndLineCap="Flat" Stroke="White" StrokeThickness="2" StrokeMiterLimit="10" StrokeLineJoin="Miter" UseLayoutRounding="False"/>
<Path Grid.ColumnSpan="5" Data="M2.3333333,136.66667 C10.924497,136.66667 18.328968,134.33333 26.666666,134.33333 C34.2099,134.33333 40.833973,135.33333 48.333332,135.33333 C63.764233,135.33333 79.204323,135.33333 94.666664,135.33333 C128.60835,135.33333 161.59547,137.31277 195.33333,134.66667" Margin="1.333,3.333,3.667,2.333" Grid.Row="3" StrokeStartLineCap="Flat" Stretch="Fill" StrokeEndLineCap="Flat" Stroke="White" StrokeThickness="2" StrokeMiterLimit="10" StrokeLineJoin="Miter" UseLayoutRounding="False"/>
<local:TicTacToeButton HorizontalAlignment="Left" x:Name="ticTacToeButton1" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonDown="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.Column="2" HorizontalAlignment="Left" Margin="1,0,0,0" x:Name="ticTacToeButton2" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonDown="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.Column="4" HorizontalAlignment="Left" Margin="2,0,0,0" x:Name="ticTacToeButton3" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonDown="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.ColumnSpan="2" Grid.Row="2" HorizontalAlignment="Left" Margin="2,1,0,0" x:Name="ticTacToeButton4" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonUp="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.Column="2" Grid.Row="2" HorizontalAlignment="Left" Margin="1,0,0,0" x:Name="ticTacToeButton5" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonUp="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.Column="4" Grid.Row="2" HorizontalAlignment="Left" Margin="2,1,0,0" x:Name="ticTacToeButton6" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonUp="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.ColumnSpan="2" Grid.Row="4" HorizontalAlignment="Left" Margin="2,0,0,0" x:Name="ticTacToeButton7" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonUp="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.Column="2" Grid.Row="4" HorizontalAlignment="Left" Margin="1,0,0,0" x:Name="ticTacToeButton8" VerticalAlignment="Top" Height="60" Width="60" MouseLeftButtonUp="button_MouseLeftButtonDown" />
<local:TicTacToeButton Grid.Column="4" Grid.Row="4" VerticalAlignment="Top" MouseLeftButtonUp="button_MouseLeftButtonDown" HorizontalAlignment="Left" Height="60" Width="60" Margin="2,0,0,0" x:Name="ticTacToeButton9" />
</Grid>
</Canvas>
<Button Content="Play again?" Height="70" HorizontalAlignment="Right" Margin="0,20,123,0" Name="btnReplay" VerticalAlignment="Top" Width="230" Click="btnReplay_Click" Grid.Row="2" />
</Grid>
Also I have modified XAML within TitlePanel to look like that:
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle" Margin="-3,-1,0,0" Text="EUGENEDOTNET.COM" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="Tic Tac Toe" Margin="-3,-1,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
Step 5: Game logic or gameboard code-behind
I will not describe each part of code file in my lesson, you can view the full code of application on CodePlex(link above). Definitely, most important method is a mouse click event on one of TicTacToe buttons. This method checks if the game is still active (if there are some unpressed buttons and none of player has won), changes a state of a pressed TicTacToeButton, checks if the current player has won and if the game is over. Here is a code for that method:
void button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (IsActive)
{
TicTacToeButton button = sender as TicTacToeButton;
// if button haven't been pressed before
if (button != null && !button.IsCross.HasValue)
{
button.IsCross = IsCross;
if (!CheckGameboard())
{
// change visual state of a button
IsCross = !IsCross;
}
else
{
// game over
IsActive = false;
}
}
}
}
Here is a video demonstration: