Я немного новичок в MVVM, у меня есть сложная модель List, назовем ее доской, я хочу привязать ее к холсту< /code> в WPF холст должен действовать как карта прямоугольников. Каждый прямоугольник должен быть привязан к значению bool на доске, которое будет влиять на цвет Прямоугольник.
Мне удалось привязать его к Rectangle, но это была одноразовая статическая привязка, потому что в модели не было уведомлений, теперь я застрял, пытаясь обернуть его в ViewModel, чтобы сохранить полное разделение.
Model.cs
public class Cell(int x, int y)
{
public int X { get; set; } = x;
public int Y { get; set; } = y;
public bool IsOn { get; set;} = false;
// Other properties...
}
public partial class GameBoard(int width, int height) : IEnumerable
{
public int Height { get; private set; } = width;
public int Width { get; private set; } = height;
private List Board { get; set; } = Enumerable.Range(0, height)
.Select(y => Enumerable.Range(0, width)
.Select(x => new Cell(x, y)).ToList())
.ToList();
public IEnumerator GetEnumerator()
{
return Board.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Board.GetEnumerator();
}
}
public partial class BoardView : UserControl
{
private readonly BoardViewModel viewModel;
public BoardView()
{
InitializeComponent();
viewModel = new BoardViewModel();
DataContext = viewModel;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// For testing
CellWidth = 30;
CellHeight = 30;
var offset = new Point(
(int)ActualWidth / 2 - viewModel.Width * (int)(CellWidth + CellOffset) / 2,
(int)ActualHeight / 2 - viewModel.Height * (int)(CellHeight + CellOffset) / 2);
for (int y = 0; y < viewModel.Height; y++)
{
for (int x = 0; x < viewModel.Width; x++)
{
viewModel.Board[x, y] = true;
var rect = new Rectangle
{
Width = CellWidth,
Height = CellHeight,
Tag = new Point(x, y),
};
var binding = new Binding()
{
Source = viewModel,
Path = new PropertyPath($"Board[{y},{x}]"),
Converter = new IsOnToFillConverter() // convert from bool to Brush
};
rect.MouseUp += Any_Click;
rect.SetBinding(Shape.FillProperty, binding);
Canvas.SetTop(rect, offset.Y + y * (rect.Height + CellOffset));
Canvas.SetLeft(rect, offset.X + x * (rect.Width + CellOffset));
canvasBoard.Children.Add(rect);
}
}
}
// For testing
private void Any_Click(object sender, RoutedEventArgs e)
{
var location = (Point)(sender as Rectangle)!.Tag;
viewModel.Board[location] = !viewModel.Board[location];
}
#region Dependency Properties
public double CellWidth
{
get { return (double)GetValue(CellWidthProperty); }
set { SetValue(CellWidthProperty, value); }
}
// Using a DependencyProperty as the backing store for CellWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CellWidthProperty =
DependencyProperty.Register(nameof(CellWidth), typeof(double), typeof(BoardView), new PropertyMetadata(0d));
public double CellHeight
{
get { return (double)GetValue(CellHeightProperty); }
set { SetValue(CellHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for CellHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CellHeightProperty =
DependencyProperty.Register(nameof(CellHeight), typeof(double), typeof(BoardView), new PropertyMetadata(0d));
public double CellOffset
{
get { return (double)GetValue(CellOffsetProperty); }
set { SetValue(CellOffsetProperty, value); }
}
// Using a DependencyProperty as the backing store for CellOffset. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CellOffsetProperty =
DependencyProperty.Register(nameof(CellOffset), typeof(double), typeof(BoardView), new PropertyMetadata(5d));
#endregion
}
Есть ли способ обернуть GameBoard минимальным шаблоном и сохранить модель как есть?
Я немного новичок в MVVM, у меня есть сложная модель List, назовем ее доской, я хочу привязать ее к холсту< /code> в WPF холст должен действовать как карта прямоугольников. Каждый прямоугольник должен быть привязан к значению bool на доске, которое будет влиять на цвет Прямоугольник. Мне удалось привязать его к Rectangle, но это была одноразовая статическая привязка, потому что в модели не было уведомлений, теперь я застрял, пытаясь обернуть его в ViewModel, чтобы сохранить полное разделение. Model.cs [code]public class Cell(int x, int y) { public int X { get; set; } = x; public int Y { get; set; } = y; public bool IsOn { get; set;} = false; // Other properties... }
public partial class GameBoard(int width, int height) : IEnumerable { public int Height { get; private set; } = width; public int Width { get; private set; } = height; private List Board { get; set; } = Enumerable.Range(0, height) .Select(y => Enumerable.Range(0, width) .Select(x => new Cell(x, y)).ToList()) .ToList();
public IEnumerator GetEnumerator() { return Board.GetEnumerator(); }
public BoardView() { InitializeComponent(); viewModel = new BoardViewModel(); DataContext = viewModel; }
private void UserControl_Loaded(object sender, RoutedEventArgs e) { // For testing CellWidth = 30; CellHeight = 30; var offset = new Point( (int)ActualWidth / 2 - viewModel.Width * (int)(CellWidth + CellOffset) / 2, (int)ActualHeight / 2 - viewModel.Height * (int)(CellHeight + CellOffset) / 2); for (int y = 0; y < viewModel.Height; y++) { for (int x = 0; x < viewModel.Width; x++) { viewModel.Board[x, y] = true; var rect = new Rectangle { Width = CellWidth, Height = CellHeight, Tag = new Point(x, y), }; var binding = new Binding() { Source = viewModel, Path = new PropertyPath($"Board[{y},{x}]"), Converter = new IsOnToFillConverter() // convert from bool to Brush }; rect.MouseUp += Any_Click; rect.SetBinding(Shape.FillProperty, binding); Canvas.SetTop(rect, offset.Y + y * (rect.Height + CellOffset)); Canvas.SetLeft(rect, offset.X + x * (rect.Width + CellOffset)); canvasBoard.Children.Add(rect); } } }
// For testing private void Any_Click(object sender, RoutedEventArgs e) { var location = (Point)(sender as Rectangle)!.Tag; viewModel.Board[location] = !viewModel.Board[location]; }
#region Dependency Properties
public double CellWidth { get { return (double)GetValue(CellWidthProperty); } set { SetValue(CellWidthProperty, value); } }
// Using a DependencyProperty as the backing store for CellWidth. This enables animation, styling, binding, etc... public static readonly DependencyProperty CellWidthProperty = DependencyProperty.Register(nameof(CellWidth), typeof(double), typeof(BoardView), new PropertyMetadata(0d));
public double CellHeight { get { return (double)GetValue(CellHeightProperty); } set { SetValue(CellHeightProperty, value); } }
// Using a DependencyProperty as the backing store for CellHeight. This enables animation, styling, binding, etc... public static readonly DependencyProperty CellHeightProperty = DependencyProperty.Register(nameof(CellHeight), typeof(double), typeof(BoardView), new PropertyMetadata(0d));
public double CellOffset { get { return (double)GetValue(CellOffsetProperty); } set { SetValue(CellOffsetProperty, value); } }
// Using a DependencyProperty as the backing store for CellOffset. This enables animation, styling, binding, etc... public static readonly DependencyProperty CellOffsetProperty = DependencyProperty.Register(nameof(CellOffset), typeof(double), typeof(BoardView), new PropertyMetadata(5d));
#endregion } [/code] Есть ли способ обернуть GameBoard минимальным шаблоном и сохранить модель как есть?