To begin with, we will make our field square. A simple solution is to write
Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"
(about a better solution later).
Now, we need to show cells of the same size, a UniformGrid suitable for this:
<UniformGrid Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" Rows="3" Columns="3"> ... <UniformGrid/>
Now, crosses and zeros themselves. For their rendering it is better to apply the Path with a suitable geometry. As the geometry will be reused, we put it into resources. Geometry will stretch freely, so let's make it a unit size.
With a zero simply:
<EllipseGeometry x:Key="NoughtGeo" RadiusX="1" RadiusY="1"/>
With a cross a little more complicated, we will organize a "turtle graphics":
<Geometry x:Key="CrossGeo">M 0,0 L 1,1 M 0,1 L 1,0</Geometry>
Now, draw our Button . Indent, stretch the geometry and make the lines thicker:
<Button Padding="15"> <Path Data="{StaticResource NoughtGeo}" Stroke="DarkGray" Stretch="Uniform" StrokeThickness="3"/> </Button>
We get something like this:
<Window x:Class="Test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SO4WPF" Title="Тест" Height="350" Width="525"> <Grid> <Grid.Resources> <Geometry x:Key="CrossGeo">M 0,0 L 1,1 M 0,1 L 1,0</Geometry> <EllipseGeometry x:Key="NoughtGeo" RadiusX="1" RadiusY="1"/> </Grid.Resources> <UniformGrid Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" Rows="3" Columns="3" Background="WhiteSmoke"> <Button Padding="15" Margin="5"> <Path Data="{StaticResource NoughtGeo}" Stroke="DarkGray" Stretch="Uniform" StrokeThickness="3"/> </Button> <Button Padding="15" Margin="5"> <Path Data="{StaticResource CrossGeo}" Stroke="DarkGray" Stretch="Uniform" StrokeThickness="3"/> </Button> </UniformGrid> </Grid> </Window>
and the result:

Immediately we see a lot of duplicate code that needs to be thrown into the style. Since we have controls inside, we put them in the ContentTemplate . Content itself will be either X or O
<Style TargetType="Button"> <Setter Property="Padding" Value="15"/> <Setter Property="Margin" Value="5"/> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Path Stroke="DarkGray" Stretch="Uniform" StrokeThickness="3" Name="P"/> <DataTemplate.Triggers> <DataTrigger Binding="{Binding}" Value="X"> <Setter TargetName="P" Property="Data" Value="{StaticResource CrossGeo}"/> </DataTrigger> <DataTrigger Binding="{Binding}" Value="O"> <Setter TargetName="P" Property="Data" Value="{StaticResource NoughtGeo}"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </Setter.Value> </Setter> </Style>
Now the buttons are made simple:
<UniformGrid Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" Rows="3" Columns="3" Background="WhiteSmoke"> <Button>X</Button> <Button>O</Button> <Button> </Button> <Button> </Button> <Button>X</Button> <Button> </Button> <Button>X</Button> <Button> </Button> <Button>O</Button> </UniformGrid>
Result:

Now about the problem with a square field. Why is the original solution not quite good? Let's see how it behaves when the window is resized:

In the case when the width is too small, it turns ugly.
How to deal with it? Without the code it will not work, but you can use the auxiliary class AspectRatioDecorator from here :
<local:AspectRatioDecorator> <UniformGrid Rows="3" Columns="3" Background="WhiteSmoke"> <Button>X</Button> <Button>O</Button> <Button> </Button> <Button> </Button> <Button>X</Button> <Button> </Button> <Button>X</Button> <Button> </Button> <Button>O</Button> </UniformGrid> </local:AspectRatioDecorator>
We get the following result:

I did not like the fact that in the last screenshot, when the field is reduced, the crosses and zeros “collapse”. It is better that their boundary decreases proportionally. To do this, we will slightly change our geometry, we will include in it 15% of the margin from the edges.
For a dagger, it's easy, add “struts” to the beginning:
<Geometry x:Key="CrossGeo"> M 0,0 M 1,1 M 0.15,0.15 L 0.85,0.85 M 0.15,0.85 L 0.85,0.15 </Geometry>
With zeros have a little tricky. If you just reduce the radius, nothing will change, because we still stretch it to the full width of the button. So you have to put a separate invisible strut:
<GeometryGroup x:Key="NoughtGeo"> <EllipseGeometry RadiusX="0.85" RadiusY="0.85"/> <Geometry>M -1,-1 M 1,1</Geometry> </GeometryGroup>
Do not forget to remove Padding from the style!
We get:
