K
(Renewed the code from the end of 2016.)That's right. First, you need to separate the logic of the game from the performance. Once and for all.Remember, you must have an object in a layer of logic that is a field, an obstacle and all that, and his reflection should be in the field of vision. To laugh the logic and show it all up.For starters, general support base class, INotifyPropertyChanged (usually in your MVM-Freimvorke, or you carry it from the project to the project):class VM : INotifyPropertyChanged
{
protected bool Set<T>(ref T storage, T value,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
RaisePropertyChanged(propertyName);
return true;
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged;
}
Okay, let's go make a sketch.class BoardVM : VM // это наша доска, понятно
{
#region property int NumberOfRows
int numberOfRows;
public int NumberOfRows
{
get { return numberOfRows; }
set { Set(ref numberOfRows, value); }
}
#endregion
#region property int NumberOfCols
// аналогично
#endregion
Okay, now we need obstacles. They're between cells, and they're supposed to belong to the board. Okay. We need a separate type that describes the obstacle. Obstacle♪ The board contains a list of obstacles which, of course, can change. public ObservableCollection<Obstacle> Obstacles { get; private set; }
}
There's no need for anything else, but we might need a list of the figures.So, an obstacle. Obstacles are not " mourning " , so their position may be ordinary property. Let the obstacle be vertical/horizontal, we'll put in an initial cell and direction. We may have many types of obstacles, so we need to envisage the possibility that we will have the classes created.class Obstacle
{
public int X { get; }
public int Y { get; }
public int Length { get; }
public bool IsVertical { get; }
}
Okay, it's time to paint. Because the board is a non-trivial object, we'll create it. UserControl♪<UserControl
x:Class="YourCoolGame.View.BoardPresentation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- здесь будут края вашей доски, возможно, вы хотите на них
что-то нарисовать -->
<Grid>
<!-- А это сама доска. Заполнять будем по рабоче-крестьянски,
в code-behind -->
<Grid x:Name="CellsHost"/>
<!-- тут ещё какие-нибудь контролы, если надо -->
</Grid>
</UserControl>
Now the code-behind.// там где-то выше namespace YourCoolGame.View
class BoardPresentation : UserControl
{
public BoardPresentation()
{
InitializeComponent();
// окей, нам надо подписаться на изменение объекта из слоя логики
// который, как вы уже обязаны знать, придёт к нам через DataContext
DataContextChanged += (sender, args) =>
OnBoardChanged((BoardVM)args.OldValue, (BoardVM)args.NewValue);
OnBoardChanged(null, (BoardVM)DataContext);
}
void OnBoardChanged(BoardVM prev, BoardVM curr)
{
// окей, у нас новая доска. нам надо подписаться на изменение всего,
// что нам интересно. но для начала отписаться от старой доски
if (prev != null)
prev.PropertyChanged -= OnBoardGeometryChanged;
OnBoardGeometryChanged();
if (curr != null) // ну и подписываемся
curr.PropertyChanged -= OnBoardGeometryChanged;
}
Here, the preparations are over. Now there's only one thing left to do. // аргументы игнорируются
void OnBoardGeometryChanged(object sender, EventArgs e)
{
BoardMV board = (BoardMV)DataContext;
if (board != null)
SetBoardSize(board.NumberOfCols, board.NumberOfRows);
else
SetBoardSize(0, 0);
}
void SetBoardSize(int cols, int rows)
{
// Будем держать в Grid'е 1 + 2 * cols столбцов:
// для клеток и для препятствий. То же со строками.
var neededNumberOfCols = 2 * cols + 1;
var actualNumberOfCols = CellsHost.ColumnDefinitions.Count;
if (neededNumberOfCols > actualNumberOfCols)
{
// добавляем
for (int i = actualNumberOfCols; i < neededNumberOfCols; i++)
{
bool isBorderCell = (i % 2 == 0);
CellsHost.ColumnDefinitions.Add(
new ColumnDefinition()
{
Width = isBorderCell ? BorderThickness : CellSize
});
// добавили столбец? теперь его надо заполнить
// во все строки добавляем по клетке
if (!isBorderCell)
for (int j = 1; j < CellsHost.RowDefinitions.Count; j += 2)
AddCellAt(i / 2, j / 2);
}
}
else
{
// убираем
for (int i = actualNumberOfCols, i > neededNumberOfCols; i--)
{
bool isBorderCell = (i % 2 == 0);
if (!isBorderCell)
for (int j = 1; j < CellsHost.RowDefinitions.Count; j += 2)
RemoveCellAt(i / 2, j / 2);
CellsHost.ColumnDefinitions.RemoveAt(i);
}
}
// ну и то же самое для строк
}
We have gambling cells. They can do all the interesting things, like being painted in different colors, intercepting things from mice, and all that. We'll write a job for them. void AddCellAt(int i, int j)
{
var cell = new Cell(i, j, GetColorForCell(i, j))
{
DataContext = DataContext
};
int colInGrid = 2 * i + 1;
int rowInGrid = 2 * j + 1;
Grid.SetColumn(cell, colInGrid);
Grid.SetRow(cell, rowInGrid);
CellsHost.Children.Add(cell);
}
void RemoveCellAt(int i, int j)
{
var cell = CellsHost.Children.OfType<Cell>()
.Where(c => c.Col = i && c.Row == j)
.Single();
CellsHost.Children.Remove(cell);
}
}
All right, there's more to be added. ObsacleBut you'll figure it out. In the meantime, describe. Cell♪ CellI see. UserControla simple one.<UserControl
x:Class="YourCoolGame.View.Cell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Name="ContentElement" Click="OnClick"/>
</UserControl>
And code-behind:// там где-то выше снова namespace YourCoolGame.View
class Cell : UserControl
{
int col, row;
// конструктор по умолчанию нужен
public Cell() : this(-1, -1, Colors.Transparent)
{
}
public Cell(int col, int row, Color color)
{
InitializeComponent();
this.col = col;
this.row = row;
ContentElement.Background = new SolidColorBrush(color);
}
// это на самом деле не очень хороший момент, т. к. получается
// сильная связность. развязаться можно при помощи команд, например.
void OnClick(object sender, RoutedEventArgs e)
{
BoardVM board = (BoardVM)DataContext;
board.ActivateCell(col, row);
}
}
That's all. Work as hard as you do with cages, sign for change. ObstaclesBring it in. UserControlpresenting the obstacle and add it to Grid To the right place.