XAML/MVVM 帮助实施屏幕网格

XAML/MVVM help for implementing screen grid

作为 XAML/MVVM 的完全初学者,我想请求一些帮助来实现 24x14 字母屏幕。
XAML 的实现非常简单,我认为

<Grid x:Name="ScreenGrid"
      RowDefinitions="*, *, *, *, *, *, *, *, *, *, *, *, *, *"                  
      ColumnDefinitions="*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*"/>

然而,我正在努力解决的是数据绑定。
我想写一些类似“UpdateScreen()”方法的东西,用数据填充那个网格,其中


我当前的 ViewModel 看起来像这样(在 Microsoft.Toolkit.Mvvm 的帮助下):
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?
感谢您的帮助。

这是一种以编程方式将单元格添加为网格子级的方法,每个单元格都绑定到视图模型中列表的一个元素。

鉴于:

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>();
    ...
}

MainPage.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 看起来像这样(在 Toolkit.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);
            }
        }
    }
}