Как предположил OJ, вам следует использовать привязку данных. Это позволяет отделить проблему представления игры на экране от проблемы управления внутренней логикой игры.
Вам потребуется создать класс Cell
, который предоставляет свойства Row
, Column
и Owner
. Класс должен будет реализовать INotifyPropertyChanged
, чтобы при изменении Owner
он мог предупреждать пользовательский интерфейс, вызывая событие PropertyChanged
. Наконец, класс будет реализовывать SelectCellCommand
, который будет правильно устанавливать Owner
для ячейки при ее выполнении (а свойство CanExecute
будет установлено в значение false, как только будет установлен владелец ячейки).
Вы создадите коллекцию, содержащую девять из этих Cell
s, со свойствами Row
, Column
и Owner
, для которых установлены соответствующие начальные значения. Вся ваша игровая логика (например, чья это очередь? Кто-то победил?) Изучит эту коллекцию.
В пользовательском интерфейсе вы создадите ItemsControl
, привязанный к коллекции Cell
s. Каждый элемент в элементе управления будет ячейкой в сетке.
Вы установите ItemsPanel
на Grid
, который скажет ItemsControl
, как раскладывать свои предметы. Вы создадите ItemContainerStyle
, который назначит вложенные свойства Grid.Row
и Grid.Column
контейнерам элементов - ItemsControl
создаст ItemsPresenter
для каждого объекта в ItemsSource
, а Grid.Row
и Grid.Column
свойства должны быть установлены для этого объекта, чтобы Grid
мог видеть их и знать, куда их поместить.
Наконец, элемент управления будет содержать ItemTemplate
, который описывает, как должен отображаться каждый элемент. Я использую Button
(так как вы хотите, чтобы была команда, которая выполняется, когда пользователь нажимает на нее), которая содержит TextBlock
, который отображает X или O в Owner
.
Это может выглядеть примерно так:
<ItemsControl ItemsSource="{Binding {StaticResource Cells}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
<ItemsPanel.ItemContainerStyle>
<Style TargetType="ItemPresenter">
<Setter Property="Grid.Column" Value="{Binding Column}"/>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
</Style>
</ItemsPanel.ItemContainerStyle>
<ItemsPanel.ItemTemplate>
<DataTemplate DataType="local:Cell">
<Button Command="{Binding SelectCellCommand}" VerticalContentAlignment="Center" HorizontalContentAlignment="Center">
<TextBlock FontSize="72" Source="{Binding Owner}"/>
</Button>
</DataTemplate>
</ItemsPanel.ItemTemplate>
</ItemsControl>
</ItemsControl>
Зачем все это проходить?
Хорошо, давайте посмотрим на проблемы, с которыми сталкивается ваш текущий дизайн:
Если вы хотите изменить расположение ячеек в вашем дизайне, у вас есть девять различных элементов управления, которые нужно исправить. Здесь вы просто меняете DataTemplate
для ячеек.
Нет необходимости в девяти различных обработчиках событий. Поскольку кнопки привязаны к объектам команд, которые являются свойствами Cell
, вам не нужно реализовывать какой-либо способ определения ячейки, по которой пользователь только что щелкнул, - команда - это . Кроме того, вы можете отключить команду (и ее кнопку) очень просто после установки Owner
.
Вся ваша игровая логика может смотреть на простую структуру данных - набор Cell
объектов - не заботясь о том, как работает пользовательский интерфейс.
Наконец, и самое главное, привязка данных - это ядро разработки приложений WPF. Это делает разработку приложений WPF гораздо проще, чем реализацию приложений WinForms. Если вы изучаете WPF без обучения связыванию, вы теряете время.