5.1. Without Styles
As an example of how styles can make themselves useful in WPF,
let's take a look at a simple implementation of tic-tac-toe in
Example 5-1.
Example 5-1. A simple tic-tac-toe layout
<!-- Window1.xaml -->
<Window
x:Class="TicTacToe.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Text="TicTacToe">
<!-- the black background lets the tic-tac-toe -->
<!-- crosshatch come through on the margins -->
<Grid Background="Black">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Margin="0,0,2,2" Grid.Row="0" Grid.Column="0" x:Name="cell00" />
<Button Margin="2,0,2,2" Grid.Row="0" Grid.Column="1" x:Name="cell01" />
<Button Margin="2,0,0,2" Grid.Row="0" Grid.Column="2" x:Name="cell02" />
<Button Margin="0,2,2,2" Grid.Row="1" Grid.Column="0" x:Name="cell10" />
<Button Margin="2,2,2,2" Grid.Row="1" Grid.Column="1" x:Name="cell11" />
<Button Margin="2,2,0,2" Grid.Row="1" Grid.Column="2" x:Name="cell12" />
<Button Margin="0,2,2,0" Grid.Row="2" Grid.Column="0" x:Name="cell20" />
<Button Margin="2,2,2,0" Grid.Row="2" Grid.Column="1" x:Name="cell21" />
<Button Margin="2,2,0,0" Grid.Row="2" Grid.Column="2" x:Name="cell22" />
</Grid>
</Window>
This grid layout arranges a set of nine buttons in a 3 x 3 grid
of tic-tac-toe cells, using the margins on the button for the tic-tac-toe
crosshatch. A simple implementation of the game logic in the XAML code-behind
file looks like Example 5-2.
Example 5-2. A simple tic-tac-toe implementation
// Window1.xaml.cs
...
namespace TicTacToe {
public partial class Window1 : Window {
// Track the current player (X or O)
string currentPlayer;
// Track the list of cells for finding a winner etc.
Button[] cells;
public Window1( ) {
InitializeComponent( );
// Cache the list of buttons and handle their clicks
this.cells = new Button[] { this.cell00, this.cell01, ... };
foreach( Button cell in this.cells ) {
cell.Click += cell_Click;
}
// Initialize a new game
NewGame( );
}
// Wrapper around the current player for future expansion,
// e.g. updating status text with the current player
string CurrentPlayer {
get { return this.currentPlayer; }
set { this.currentPlayer = value; }
}
// Use the buttons to track game state
void NewGame( ) {
foreach( Button cell in this.cells ) {
cell.Content = null;
}
CurrentPlayer = "X";
}
void cell_Click(object sender, RoutedEventArgs e) {
Button button = (Button)sender;
// Don't let multiple clicks change the player for a cell
if( button.Content != null ) { return; }
// Set button content
button.Content = CurrentPlayer;
// Check for winner or a tie
if( HasWon(this.currentPlayer) ) {
MessageBox.Show("Winner!", "Game Over");
NewGame( );
return;
}
else if( TieGame( ) ) {
MessageBox.Show("No Winner!", "Game Over");
NewGame( );
return;
}
// Switch player
if( CurrentPlayer == "X" ) {
CurrentPlayer = "O";
}
else {
CurrentPlayer = "X";
}
}
// Use this.cells to find a winner or a tie
bool HasWon(string player) {...}
bool TieGame( ) {...}
}
}
Our simple tic-tac-toe logic uses strings to represent the
players and uses the buttons themselves to keep track of the game state. As
each button is clicked, we set the content to the string indicating the current
player and switch players. When the game is over, the content for each button
is cleared. The middle of a game looks like
Figure 5-1.
Notice in Figure 5-1
how the grid background comes through from the margin. These spacers almost
make the grid look like a drawn tic-tac-toe board (although we'll do better
later). However, if we're really looking to simulate a hand-drawn game, we've
got to do something about the size of the font used on the buttons; it doesn't
match the thickness of the lines.
One way to fix this problem is by setting the size and weight
for each of the Button objects, as in Example
5-3.
Example 5-3. Setting control properties individually
<Button FontSize="32" FontWeight="Bold" ... x:Name="cell00" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell01" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell02" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell10" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell11" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell12" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell20" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell21" />
<Button FontSize="32" FontWeight="Bold"... x:Name="cell22" />
While this will make the X's and O's look better according to my
visual sensibilities today, if I want to change it later, I've now committed
myself to changing both properties in nine separate places, which is a
duplication of effort that offends my coding sensibilities. I'd much prefer to
refactor my decisions about the look of my tic-tac-toe cells into a common
place for future maintenance. That's where styles come in handy.
 |