class VM : INotifyPropertyChanged
{
protected bool Set<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
NotifyPropertyChanged(propertyName);
return true;
}
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged;
}
一些辅助结构:
struct FieldSize
{
public int Width { get; }
public int Height { get; }
public FieldSize(int w, int h) { Width = w; Height = h; }
}
struct FieldPosition
{
public int X { get; }
public int Y { get; }
public FieldPosition(int x, int y) { X = x; Y = y; }
}
当然,它们代表了场地的大小和在场地上的位置。
现在虚拟机是一个单元格。这里的一切都很明显:
class CellVM : VM
{
public CellVM(int row, int column)
{
Position = new FieldPosition(row, column);
Activate = new RelayCommand(OnActivate);
}
public FieldPosition Position { get; }
public ICommand Activate { get; }
bool isActive = false;
public bool IsActive
{
get { return isActive; }
private set { Set(ref isActive, value); }
}
void OnActivate()
{
IsActive = !IsActive;
}
}
现在全场。在改变字段的大小时,我们生成单元格。虽然单元格表示为列表,但如果您需要将它们作为二维数组,请将 save to array 添加到GenerateCells.
class BoardVM : VM
{
FieldSize fieldSize;
public FieldSize FieldSize
{
get { return fieldSize; }
set { if (Set(ref fieldSize, value)) GenerateCells(); }
}
IEnumerable<CellVM> cells;
public IEnumerable<CellVM> Cells
{
get { return cells; }
private set { Set(ref cells, value); }
}
void GenerateCells()
{
var list = new List<CellVM>(FieldSize.Width * FieldSize.Height);
for (int j = 0; j < FieldSize.Height; j++)
for (int i = 0; i < FieldSize.Width; i++)
list.Add(new CellVM(i, j));
Cells = list;
}
}
<Path Data="M -1,-1
M 0,-1
L 0.86602540378443864676372317075294,-0.5
L 0.86602540378443864676372317075294,0.5
L 0,1
L -0.86602540378443864676372317075294,0.5
L -0.86602540378443864676372317075294,-0.5
L 0,-1
M 1,1"
Stretch="Uniform"
Stroke="Black"
StrokeThickness="0.5">
最简单的方法是使用
ItemsControl/Canvas,并使用 设置表单Path。我以这段代码为基础:Playing on WPF, porting WinForms code to MVVM。
首先,VM部分。基本 VM 类是标准的:
一些辅助结构:
当然,它们代表了场地的大小和在场地上的位置。
现在虚拟机是一个单元格。这里的一切都很明显:
唯一可以改变的字段是
IsActive,所以改变它发送NotifyPropertyChanged。Activate单击时将调用该命令。现在全场。在改变字段的大小时,我们生成单元格。虽然单元格表示为列表,但如果您需要将它们作为二维数组,请将 save to array 添加到
GenerateCells.这就是 VM 部分的全部内容,让我们转向 View。这里会有一些技巧。
首先,我们需要转换器将坐标转换为屏幕上的位置。他们对偶数行和奇数行的计算略有不同。像元大小作为参数传递。
现在 XAML。将单元格大小和转换器连接到窗口资源:
接下来,内容本身。我没有自动调整窗口大小,为此您需要另一个转换器。
单元格通过以下方式从集合中连接
ItemsControl:由于我们会“手动”安排坐标,因此我们需要
Canvas作为载体:现在,一个细胞。
Grid让我们把它放在一个所需大小的正方形中:我们通过 定义内部部分
Path,计算坐标如下:接下来,根据 的值更改颜色
IsActive。编写另一个转换器很丑陋,让我们使用触发器。DataTrigger仅在样式中可用,因此样式将负责设置背景:现在,我们需要组织点击。由于
Path它不能自己做到这一点,因此有三种方法:要么使用程式化的 'thButton(你把它放在Path中ControlTemplate),要么定义InputBinding,或者包括 System.Windows.Interactivity.dll(来自通过安装的 Expression Blend SDK Visual Studio Installer,或来自 nugetSystem.Windows.Interactivity.WPF),并将命令挂在鼠标事件上。最简单的方法是第二种方法(@AndreyNOP 建议):虽然我走的是第三条路(
i:是前缀定义为xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity")。一切都与细胞有关。
还需要什么?您需要将单元格放置在所需的坐标处。它本身
Grid是DataTemplate装在一个容器中的,所以给它设置坐标是没有用的。因此有必要移动容器本身。这是这样做的:这是完整的窗口代码:
那么,标准的 App.xaml/App.xaml.cs:
结果:
一切!