XAML/MVVM-Hilfe zum Implementieren von Bildschirmraster

Als absoluter Anfänger in XAML/MVVM möchte ich um Hilfe bei der Implementierung eines 24x14-Buchstabenbildschirms bitten.
Die XAML-Implementierung ist ziemlich einfach, denke ich, mit so etwas wie

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

Womit ich jedoch zu kämpfen habe, ist die Datenbindung.
Ich würde gerne so etwas wie eine "UpdateScreen()"-Methode schreiben, die dieses Raster mit Daten füllt wo

  • Jede Gitterzelle hat 1 Buchstaben
  • Jeder Buchstabe hat eine Farbe (rot, grün, ...)
  • Jeder Buchstabe ist entweder groß oder klein

My current ViewModel looks like this (with the help of 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()
    {
        [.?.]
    }
}

Welches mit der eigentlichen Logik der App kommunizieren soll, die einen ScreenText[] generiert, der an das ViewModel.
zurückgereicht wird Wie verdrahte ich das mit dem ScreenGrid?
Danke für Ihre Hilfe.

🤔 А знаете ли вы, что...
C# поддерживает интероперабельность с кодом, написанным на C++, что позволяет использовать существующие библиотеки.


1
49
2

Antworten:

Hier ist ein Ansatz, der programmgesteuert Zellen als untergeordnete Elemente des Rasters hinzufügt, wobei jede Zelle an ein Element einer Liste im Ansichtsmodell gebunden ist.

Gegeben:

public class ScreenText
{
    // I think binding to xaml requires string, not char.
    public string Value {
        get => value.ToString();
    }
    ...
}

und 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)];
    }
}

ALTERNATIVE:

Für alle, die kein Ansichtsmodell verwenden, siehe meine Antwort hier.


Gelöst

Die Lösung für mich (Minimalbeispiel) sieht so aus:

Im XAML wird das Raster so definiert

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

Das ViewModel sieht so aus (mit Hilfe von 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));
    }
}

Und schließlich die 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);
            }
        }
    }
}