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
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++, что позволяет использовать существующие библиотеки.
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.
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);
}
}
}
}