Как полный новичок в XAML/MVVM, я хотел бы попросить помощи в реализации экрана писем 24x14.
Реализация XAML довольно проста, я думаю, с чем-то вроде
<Grid x:Name = "ScreenGrid"
RowDefinitions = "*, *, *, *, *, *, *, *, *, *, *, *, *, *"
ColumnDefinitions = "*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*"/>
Однако то, с чем я борюсь, - это привязка данных.
Я хотел бы написать что-то вроде метода «UpdateScreen()», который заполняет эту сетку данными, где
public partial class ScreenViewModel : ObservableObject
{
[ObservableProperty]
private ScreenText[] _screen;
//ScreenText consists of char value, Color color, bool isBig
[ICommand]
private void ButtonPressed(AppButton button)
{
System.Diagnostics.Debug.WriteLine($"Button {button} was pressed");
}
private void UpdateScreen()
{
[.?.]
}
}
Который должен связываться с фактической логикой приложения, которое генерирует ScreenText[], который передается обратно в ViewModel.
Как подключить это к ScreenGrid?
Спасибо за вашу помощь.
🤔 А знаете ли вы, что...
C# поддерживает множество парадигм программирования, включая процедурное, объектно-ориентированное и функциональное программирование.
Вот подход, который программно добавляет ячейки как дочерние элементы сетки, при этом каждая ячейка привязывается к элементу списка в модели представления.
Дано:
public class ScreenText
{
// I think binding to xaml requires string, not char.
public string Value {
get => value.ToString();
}
...
}
и ScreenViewModel.cs:
public class ScreenViewModel : ObservableObject
{
// List or ObservableCollection (not Array) usually used for Xamarin Forms.
public ObservableCollection<ScreenText> Texts { get; } = new ObservableCollection<ScreenText>();
...
}
Главная страница.xaml:
<?xml version = "1.0" encoding = "utf-8" ?>
<ContentPage xmlns = "http://xamarin.com/schemas/2014/forms"
xmlns:x = "http://schemas.microsoft.com/winfx/2009/xaml"
x:Class = "ManipulateGridChildren.MainPage">
<Grid x:Name = "ScreenGrid"
RowDefinitions = "*, *, *, *, *, *, *, *, *, *, *, *, *, *"
ColumnDefinitions = "*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*"/>
</ContentPage>
MainPage.xaml.cs:
using System.Collections.Generic;
using Xamarin.Forms;
namespace ManipulateGridChildren
{
public partial class MainPage : ContentPage
{
const int NRows = 14;
static int NColumns = 24;
public MainPage(ScreenViewModel vm)
{
InitializeComponent();
// So you can access from any method.
VM = vm;
AddCells(vm);
BindingContext = vm;
}
private ScreenViewModel VM;
// Used a dictionary, so can add cells in any order.
private Dictionary<int, Label> cells = new Dictionary<int, Label>();
private void AddCells(ScreenViewModel vm)
{
// Grid rows and columns are numbered from "0".
for (int row = 0; row < NRows; row++) {
for (int column = 0; column < NColumns; column++) {
var cell = AddCell(row, column, vm);
}
}
}
private Label AddCell(int row, int column, ScreenViewModel vm)
{
var index = CellKey(row, column);
var cell = new Label();
// NOTE: I think "Value" has to be a "string" not a "char".
cell.SetBinding(TextProperty, $"Texts[{index}].Value");
cell.TextColor = Color.Black;
cell.BackgroundColor = Color.White;
cell.HorizontalTextAlignment = TextAlignment.Center;
cell.VerticalTextAlignment = TextAlignment.Center;
ScreenGrid.Children.Add(cell, column, row);
// This dictionary makes it easier to access individual cells later.
cells[index] = cell;
return cell;
}
// Assign unique key to each cell.
private int CellKey(int row, int column)
{
return row * NColumns + column;
}
// Can use this to access an individual cell later.
// For example, you might change some property on that cell.
private Label GetCell(int row, int column)
{
return cell[CellKey(row, column)];
}
}
АЛЬТЕРНАТИВА:
Для тех, кто не использует модель представления, см. мой ответ здесь.
Решение для меня (минимальный пример) выглядит так:
В XAML сетка определяется следующим образом.
<Grid x:Name = "ScreenGrid"
RowDefinitions = "*, *, *, *, *, *, *, *, *, *, *, *, *, *"
ColumnDefinitions = "*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*"/>
ViewModel выглядит так (с помощью Инструментарий.Mvvm):
public partial class ScreenViewModel : ObservableObject
{
const int LINES = 14;
public ObservableCollection<ScreenText> Screen { get; } = new ObservableCollection<ScreenText>();
public ScreenViewModel()
{
for (int i = 0; i < LINES; i++)
{
Screen.Add(new ScreenText());
}
}
[ICommand]
private void ButtonPressed(AppButton button)
{
System.Diagnostics.Debug.WriteLine($"Button {button} was pressed");
//Do some business logic and manipulate the screen
OnPropertyChanged(nameof(Screen));
}
}
И, наконец, MainPage.Xaml.cs:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
//Setup Screen
AddScreenCells();
}
private void AddScreenCells()
{
for (int row = 0; row < ScreenGrid.RowDefinitions.Count; row++)
{
for (int col = 0; col < ScreenGrid.ColumnDefinitions.Count; col++)
{
Label label = new Label();
label.SetBinding(Label.TextProperty, $"{nameof(ScreenViewModel.Screen)}[{row}].{nameof(ScreenText.Letters)}[{col}]");
ScreenGrid.Add(label, col, row);
}
}
}
}