I write simple tic-tac-toe to test the Minimax algorithm . The idea is this: in xaml I create an ordinary Canvas about 3/4 windows in size, in it I draw Button 3 x 3, it turns out something like:

enter image description here

Next, I do this: when I click on the button, if the number of the click is even, I draw a cross, otherwise the circle in the middle of the button. Here is the code:

private void BoardButton_Click(object sender, RoutedEventArgs e) { if (ClickCount > xDimension * yDimension) //3 * 3 = 9 throw new Exception(); //Всего 9 клеток, если кликнули 9 раз - все клетки заполнены Button _clicked = sender as Button; if (ClickCount % 2 == 0) { //Нарисовать крестик } else { Ellipse toDraw = CopyPlayer2Object; //Создает Ellipse со стандартными параметрами Width, Height, Stroke //Нужно нарисовать круг так, чтобы его центр был центром кнопки Canvas.SetLeft(toDraw, Canvas.GetLeft(_clicked) / 2); Canvas.SetTop(toDraw, Canvas.GetTop(_clicked) / 2); CurrentLocation.Children.Add(toDraw); //Мой Canvas } ClickCount++; } 

Unfortunately, the circle is not drawn in the middle of the button, and generally not in the button itself. Tell me how to draw a circle

PS I have very little experience with WPF, for this advice on how to make the creation of noughts and crosses more convenient, and the tic-tac-toe themselves are more beautifully welcomed!

    1 answer 1

    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:

    picture

    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:

    picture again


    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:

    Fu, bad dog!

    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:

    malades!


    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:

    safsem harasho

    • @Andrey : Just added this in response :) - VladD
    • Perhaps it would be more beautiful if the form was always square (of fixed proportion), the empty spaces on the sides or bottom are not ice ... - Andrey NOP
    • And where to put the style? - Philippe
    • @VladD And also, how to create a Button with a circle or a cross using C # code (those styles that were made in xaml) - Philippe
    • @Andrey: Well, this is let the vehicle itself. - VladD