Пишу простые крестики-нолики, чтобы протестить алгоритм Минимакс. Идея такая : в xaml создаю обычный Canvas размером примерно 3/4 окна, в нем рисую Button'ы 3 x 3, получается что-то вроде:
Дальше, я делаю так: при клике на кнопку, если номер клика чётный, рисую крестик, иначе кружок в середине кнопки. Вот код:
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++;
}
К сожалению, кружок не рисуется в середине кнопки, и вообще не в самой кнопке. Подскажите, как правильно нарисовать кружок
P.S У меня очень мало опыта работы с WPF, по этому советы по тому, как сделать создание крестиков-ноликов удобнее, а сами крестики-нолики красивее приветствуются!
Для начала, сделаем наше поле квадратным. Простое решение — написать
Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"
(о более хорошем решении потом).
Теперь, нам нужно показывать ячейки одинакового размера, для этого подойдёт UniformGrid
:
<UniformGrid Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"
Rows="3" Columns="3">
...
<UniformGrid/>
Теперь, сами крестики и нолики. Для их отрисовки лучше по идее применять Path
с подходящей геометрией. Поскольку геометрия будет повторно использоваться, кладём её в ресурсы. Геометрия будет свободно растягиваться, поэтому сделаем его единичного размера.
С ноликом просто:
<EllipseGeometry x:Key="NoughtGeo" RadiusX="1" RadiusY="1"/>
С крестиком немного сложнее, организуем «черепашью графику»:
<Geometry x:Key="CrossGeo">M 0,0 L 1,1 M 0,1 L 1,0</Geometry>
Теперь, рисуем наш Button
. Сделаем отступ, растянем геометрию и сделаем линии потолще:
<Button Padding="15">
<Path Data="{StaticResource NoughtGeo}" Stroke="DarkGray"
Stretch="Uniform" StrokeThickness="3"/>
</Button>
Получаем примерно такой код:
<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>
и результат:
Сразу видим много повторяющегося кода, который нужно забросить в стиль. Поскольку у нас внутри контролы, положим их в ContentTemplate
. Сам Content
будет либо X
, либо 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>
Теперь кнопки оформляются просто:
<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>
Результат:
Теперь о проблеме с квадратным полем. Почему исходное решение не вполне хорошо? Посмотрим, как оно себя ведёт в случае изменения размеров окна:
В случае, когда ширина слишком маленькая, получается некрасиво.
Как с этим бороться? Без кода не получится, но можно воспользоваться вспомогатальным классом AspectRatioDecorator
отсюда:
<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>
Получаем вот такой результат:
Мне не понравилось, что в последнем скриншоте при уменьшении поля крестики и нолики «схлопываются». Лучше, чтобы их граница уменьшалась пропорционально. Для этого немного поменяем нашу геометрию, включим в неё 15% отступа от краёв.
Для крестика это просто, добавим в начало «распорки»:
<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>
С ноликом придётся немного повозиться. Если просто уменьшить радиус, ничего не поменяется, ведь мы всё равно растягиваем его на всю ширину кнопки. Поэтому придётся положить отдельную невидимую распорку:
<GeometryGroup x:Key="NoughtGeo">
<EllipseGeometry RadiusX="0.85" RadiusY="0.85"/>
<Geometry>M -1,-1 M 1,1</Geometry>
</GeometryGroup>
Не забываем убрать Padding
из стиля!
Получаем:
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Доброго времени суток, не подскажите, как получить имена (или пути) всех сетевых соединений ?
Здравствуйте! Написал 2х ботов, на С# (Robin Telegram API) и Python (Telebot)Проблема в том, что со временем (несколько дней) боты падают
Добрый день, подскажите пожалуйста, как сделать активные области как ссылки на изображении, которое вызывается в js файле, и все это дело происходит...