none
.Net MAUI: Tabelle mit Header mit gleicher Ausrichtung der Kolonnen RRS feed

  • Frage

  • Hallo,

    versuche eine Tabelle zu kreieren, welche etwa so aussehen soll:

    Dabei sollen gewisse Zellen statischen Text und die eigentlichen Einträge sowie das Total aus einer Liste mit binding dargestellt werden. Das Problem: Das Binding funktioniert offensichtlich nur mit immer den selben Objekten, z.B. Checkbox und kann nicht für die erste und letzte Zeile auf Text geändert werden. Bitte korrigiert mich wenn ich falsch liege.
    Zudem kann man in einem z.B. DataTemplate keine drei Grid erstellen, um erst den statischen Header und zuletzt den halbstatischen footer einzufügen.

    Diese Tabelle sieht hier nur so gut aus, weil ich drei Tabellen untereinander gemacht hab, welche alle die selben Breiten für die erste und letzte Kolonne haben. Die sind aber statisch definiert und je nach Gerät, Schriftart und Sprache können diese Überschriften und auch die View-Objekte verschieden gross, sein, so dass eine schöne Ausrichtung nicht mehr funktioniert.

    Hier das XAML:

            <Border Grid.Row="2" Grid.ColumnSpan="3" Padding="2">
    
                <Grid RowDefinitions="Auto, *, Auto"
                      Padding="0">
                    <Grid ColumnDefinitions="80, *, 100"
                          Grid.Row="0"
                          Padding="0"
                          Margin="2">
                        <Label Text="{x:Static lang:Strings.MPCount}" Grid.Row="0" Grid.Column="0"/>
                        <Label Text="{x:Static lang:Strings.MPArticle}" Grid.Row="0" Grid.Column="1"/>
                        <Label Text="{x:Static lang:Strings.MPPrice}" Grid.Row="0" Grid.Column="2"/>
                    </Grid>
                    <CollectionView ItemsSource="{Binding Items, Converter={StaticResource sconverter}}"
                                    Grid.Row="1"                         
                                    SelectionMode="None">
                        <CollectionView.ItemTemplate>
                            <DataTemplate>
                                <Grid ColumnDefinitions="80, *, 100"
                                    Grid.Row="1">
                                    <Grid ColumnDefinitions="80, *, 100"
                                    Grid.Row="1">
                                        <CheckBox IsChecked="{Binding isChecked}" Grid.Column="0"
                                              Margin="2"/>
                                    <Frame Grid.Column="1"
                                           Padding="0"
                                           Margin="0"
                                           BackgroundColor="LightYellow"
                                           BorderColor="LightYellow">
                                        <Frame.GestureRecognizers>
                                            <TapGestureRecognizer 
                                                Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=TapCommand}"
                                                CommandParameter="{Binding .}"/>
                                        </Frame.GestureRecognizers>
                                        <Label Text="{Binding name}"/>
                                    </Frame>
                                    <Label Text="{Binding price, Converter={StaticResource vconverter}}" 
                                               Grid.Column="2" />
                                </Grid>
                            </DataTemplate>
                        </CollectionView.ItemTemplate>
                    </CollectionView>
                    <Grid ColumnDefinitions="80, *, 100"
                              Grid.Row="3"
                              Padding="0"
                              Margin="2">
                        <Label Text="{x:Static lang:Strings.MPTotal}" Grid.Row="0" Grid.Column="0"/>
                        <Label Text="" Grid.Row="0" Grid.Column="1"/>
                        <Label Text="11.50" Grid.Row="0" Grid.Column="2"/>
                    </Grid>
                </Grid>
            </Border>

    Habe schon viele Ansätze versucht, mit CollectionView, ListView usw. aber keine dieser Ansichten kann offenbar statische und dynamische Einträge mixen. Aber (m)eine "normale" Tabelle sieht nun mal so aus und ich begreife nicht wieso das nicht möglich sein soll.

    Ich könnte mir auch vorstellen, eine Liste mit verschiedenen Objekten zu erstellen (1. Eintrag nur mit Strings) aber das geht ja dann mit dem XAML-Syntax nicht auf, weil dort schon entschieden werden muss, welcher Typ das angezeigt wird.

    Zudem hab ich beim googlen ein SharedSizeGroup gefunden, mit dem man die Kolonnenausrichtung zusammenbasteln könnte, dieses scheint aber nur im WPF vorhanden zu sein.

    Wer sieht da einen brauchbaren Lösungsansatz?


    • Bearbeitet Javango Donnerstag, 2. Februar 2023 12:58 Präzisierung
    Donnerstag, 2. Februar 2023 12:51

Antworten

  • Hi,

    schau dir mal dieses Projekt an DataGrid

    oder auch die Controls von Syncfusion. Syncfusion bietet ein ähnliches Lizenzmodel wie Visual Studio Community.

    Das ist aber mit ein Grund warum ich XAML nicht mehr benutze.

    In Blazor/Razor ist das einfacher

    <table>
        <thead>
            <tr>
                <td>Anzahl</td>
                <td>Artikel</td>
                <td>Preis</td>
            </tr>
        </thead>
        <tbody>
            @for each (var item in articles)
            {
                <tr>
                    <td><input type="checkbox" @bind="item.Check" /></td>
                    <td>@item.Name</td>
                    <td>@item.Price €</td>
                </tr>
            }
            <tr>
                <td>Total</td>
                <td></td>
                <td>
                    @articles.Where(x => x.Check).Select(x => x.Price).Sum();
                </td>
            </tr>
        </tbody>
    </table>
    
    @code {
    
        List<Article> articles = new();
    
        public class Article
        {
            public bool Check { get; set; }
            public string Name { get; set; }
            public double Price { get; set; }
        }
    
        protected override void OnParametersSet()
        {
            if (articles.Any() == false)
            {
                articles.Add(new() { Name = "1. Artikel", Price = 4.10d });
                articles.Add(new() { Name = "2. Artikel", Price = 1.10d });
                articles.Add(new() { Name = "3. Artikel", Price = 2.10d });
                articles.Add(new() { Name = "4. Artikel", Price = 5.10d });
            }
        }
    }

    CSS Vorlage gibt es im Internet zu Table viele


    Gruß Thomas
    Der Junior lernt täglich, um Senior zu werden.
    Der Senior lernt täglich, da er weiß, dass er immer Junior bleiben wird.
    Github

    • Als Antwort markiert Javango Freitag, 10. Februar 2023 11:58
    Freitag, 3. Februar 2023 12:57

Alle Antworten

  • Hallo Javango,

    Würdest Du es mit einer OneWayToSource-Bindung versuchen und die ColumnDefinition-Einträge für jede Zeile z. 
    B. in einer Liste mit den Spaltendefinitionen nachverfolgen? Einen Beispielcode für Xamarin Forms, der hoffentlich ohne viel Aufhebens auf MAUI angepasst werden kann, enthält dieser Thread:
    Share width of a Grid-Column in a ViewCell?

    Gruß,
    Dimitar

    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Donnerstag, 2. Februar 2023 20:52
    Moderator
  • Danke für Die Antwort.

    Aber ich denke, das kann's doch nicht sein! Jetzt muss man dazu Code schreiben, den man jedes Mal, wenn sich etwas in der XAML Struktur ändert, anpassen muss. Das ist doch nicht das Ziel von dem ganzen, so dass man die View vom Modell getrennt bearbeiten kann. Dann code ich meine View lieber gleich in C#, dann bin ich voll flexibel und bin auch nicht mehr von solchen Einschränkungen betroffen. 
    Ich frage mich allerdings, ob ich mit MAUI wirklich das richtige Framework gefunden habe, das ist doch eher ein Rückschritt... 

    Ausserdem hab ich das ganze ausprobiert und es scheint natürlich nicht richtig zu funktionieren, weil es eher ein Gebastel ist. Ich hab nur mal die Kollonnen 0 und 1 zum einfacheren Test vertauscht und nur mal die Einträge der mittleren Tabelle ausgerichtet. Der letzte Buchstage wird dann zum Teil auf einer neuen Zeile dargestellt (3.Eintrag). Da scheint die Width-Element-Abfrage nicht alles abzudecken:

    Dass das ganze dann auf allen Plattformen funktionieren soll, denk ich zudem auch nicht. Da hab ich schon einiges an schlechten Erfahrungen gemacht.

    • Bearbeitet Javango Freitag, 3. Februar 2023 12:06
    Freitag, 3. Februar 2023 11:51
  • Hi,

    schau dir mal dieses Projekt an DataGrid

    oder auch die Controls von Syncfusion. Syncfusion bietet ein ähnliches Lizenzmodel wie Visual Studio Community.

    Das ist aber mit ein Grund warum ich XAML nicht mehr benutze.

    In Blazor/Razor ist das einfacher

    <table>
        <thead>
            <tr>
                <td>Anzahl</td>
                <td>Artikel</td>
                <td>Preis</td>
            </tr>
        </thead>
        <tbody>
            @for each (var item in articles)
            {
                <tr>
                    <td><input type="checkbox" @bind="item.Check" /></td>
                    <td>@item.Name</td>
                    <td>@item.Price €</td>
                </tr>
            }
            <tr>
                <td>Total</td>
                <td></td>
                <td>
                    @articles.Where(x => x.Check).Select(x => x.Price).Sum();
                </td>
            </tr>
        </tbody>
    </table>
    
    @code {
    
        List<Article> articles = new();
    
        public class Article
        {
            public bool Check { get; set; }
            public string Name { get; set; }
            public double Price { get; set; }
        }
    
        protected override void OnParametersSet()
        {
            if (articles.Any() == false)
            {
                articles.Add(new() { Name = "1. Artikel", Price = 4.10d });
                articles.Add(new() { Name = "2. Artikel", Price = 1.10d });
                articles.Add(new() { Name = "3. Artikel", Price = 2.10d });
                articles.Add(new() { Name = "4. Artikel", Price = 5.10d });
            }
        }
    }

    CSS Vorlage gibt es im Internet zu Table viele


    Gruß Thomas
    Der Junior lernt täglich, um Senior zu werden.
    Der Senior lernt täglich, da er weiß, dass er immer Junior bleiben wird.
    Github

    • Als Antwort markiert Javango Freitag, 10. Februar 2023 11:58
    Freitag, 3. Februar 2023 12:57
  • Hallo Thomas,

    danke für Deine Antwort. Ich möchte dein Beispiel in einem einfachen Programm testen, allerdings hab ich noch mühe mit der Programmstruktur und weiss noch nicht genau, was ich in welche Datei setzen muss. Gehe von einer neuen ".NET MAUI Blazor App" aus. Die hat dieses Menu mit den drei Seiten Counter, FetchData und Index.
    Hab den HTML-Code mal in die Index.razor kopiert. Den Code hab ich aber nicht ohne Compiler-Fehler hingekriegt. Muss mir wohl mal ein paar Tutorials dazu anschauen.

    Für mich ist das Ganze noch ein bisschen verwirrend mit Maui, Blazor usw. Hab zwar C# - Erfahrung, bin aber absoluter Einsteiger in diesen neuen .NET Frameworks.
    Will eine Multiplattform App für Handys schreiben. Da kommen dann fragen auf wie:
    - Was ist eine Maui Blazor Hybrid-App?
    - Ist eine ".NET MAUI Blazor App" das richtige für "nur" Handy-Apps?
    - Was heisst Blazor in Maui App hosten?
    - Was ist eine Web-App?
    => Brauche (noch) keinen Server für die App.

    - Wo/mit welchen Tutorials soll ich am besten mit Einarbeiten anfangen?

    Danke und Gruss, Thom





    • Bearbeitet Javango Samstag, 4. Februar 2023 12:27
    Samstag, 4. Februar 2023 10:53
  • Hallo Thom,

    das Forum hat aktuell Probleme mit C# Code wie z.B. for_each. Deswegen mache ich meist ein Leerzeichen dazwischen. 

    Ich habe eine .NET MAUI Blazor App erstellt (Framework 7) und den Code in die Index.razor kopiert. for each angepasst und es läuft wie es soll. Die App wollte zwar erstmal auf Windows nicht starten da unter Win 10 und der AppSDK ein Bug ist. Ich musste die App einmal als Admin ausführen dann lief es.

    Der unterschied zu XAML ist das HTML/CSS über eine Browser-Engine an die Grafikkarte übergeben wird. Je nach Plattform ist das Chrom oder Safari. Bei XAML kommen unterschiedliche Engine zu Einsatz um am Ende in der Grafikkarte zu landen. Mit Blazor tauscht man also nur die Seitenbeschreibungssprache aus. Zudem hat man eine etwas andere Projektstruktur.

    Deswegen würde ich mir nicht so viele Gedanken darüber machen was eine „Web-App“ ist. Die eine richtige Antwort gibt es hier eh nicht.

    Wichtig ist nur das die UI mit HTML/CSS beschrieben wird und die Logik mit C#.

    Als .NET MAUI Blazor Entwickler ist man noch lange nicht Webentwickler. Eine .NET MAUI App nutzt je Gerät ein User. Eine Web-App könnten tausende User gleichzeitig verwenden. Aber man ist schon recht nah dran.

    Man kann alle Tutorials zu Blazor verwenden. Wichtig ist nur zu wissen das WebAssembly nicht verwendet wird. Der C# Code läuft also nicht im Browser.

    ASP.NET Core Blazor Hybrid

    Erstellen einer Blazor-App mit einer Aufgabenliste


    Gruß Thomas
    Der Junior lernt täglich, um Senior zu werden.
    Der Senior lernt täglich, da er weiß, dass er immer Junior bleiben wird.
    Github

    Samstag, 4. Februar 2023 23:09
  • Hallo Thomas,

    danke für die Kurzanleitung. So hat's geklappt. Du hast ja in diesen paar Zeilen eine Menge Funktion reingepackt. Das sieht ja schon sehr überschaubar aus, gegenüber dem recht komplizierten XAML Layout, was überhaupt nicht so flexibel ist, aber hier scheint sehr viel mit wenig aufwand möglich zu sein.
    Was mich wundert, dass hier die Ansicht (view) und der Code in die selbe Datei reingepackt wird (oder hast du das nur für die Demo so gemacht?) und selbst in der View ist auch noch logik drin (foreach).

    Muss mich da in den Foren noch ein bisschen schlauer machten.

    Was mit aber sehr stutzig macht: So viel ich gesehen hab, ist ja MauiBlazor eine Web-App die "normalerweise" mit einem Server kommuniziert. Entweder mit dauerndem Datentransfer mit dem Server, oder man hat eben diese WebAssembly direkt auf dem Device, um keinen ständigen Datentransfer aufrechterhalten zu müssen. Dies wäre für mich eigentlich die richtige Lösung da die App dann auch online funktioniert, und nur um gewisse Dinge auf einem Server zu speichern online sein zu müssen. Dies müsste ja aber dann beim Kreieren des Projektes irgendwo einstellbar sein, welches System man verwenden will?

    Danke nochmals für das sehr aufschlussreiche Beispiel!

    Sonntag, 5. Februar 2023 09:51
  • Das ist das besondere an Razor man kann HTML/CSS mit Logik mischen. Razor bedeute Rasiermesser und das ist eben die Art wie ins Dokument eingegriffen wird.

    Man kann aber auch eine Codebehind Datei erstellen. Z.B. Index.razor und Index.razor.cs. Ich persönlich nutze das gar nicht. Für Background Code hat man die Microservices und Dependencies Injection.

    Theoretisch könnte man auch WebAssembly nutzen aber WSAM ist komplexer für den Browser. Zudem hat man dann nicht mehr den einfachen Zugriff auf die Framework Funktionen wie z.B. FilePicker. Ich persönlich würde das nicht machen. Es gibt auch keine Vorlage dafür

    Es ist ein Art Blazor Server Projekt nur das der Server eben die App ist also der native Code.


    Gruß Thomas
    Der Junior lernt täglich, um Senior zu werden.
    Der Senior lernt täglich, da er weiß, dass er immer Junior bleiben wird.
    Github


    Sonntag, 5. Februar 2023 14:41
  • Hier noch ein Beispiel mit einem CSS Grid anstatt einer Table und etwas mehr Farbe

    <div style="display: grid; grid-auto-rows: 25px; row-gap: 4px; grid-template-columns: auto 1fr auto; width: 100%;">
        <p>Anzahl</p>
        <p>Artikel</p>
        <p>Preis</p>
        @for each (var item in articles)
        {
            string color = "white";
            @if (item.Check)
            {
                color = "dodgerblue";
            }
            
            <input type="checkbox" @bind="item.Check" />
            <label style="background-color: @color;">@item.Name</label>
            <label style="background-color: @color;">@item.Price €</label>
        }
        <p>Total</p>
        <p></p>
        <p>@articles.Where(x => x.Check).Select(x => x.Price).Sum()</p>
    </div>


    Gruß Thomas
    Der Junior lernt täglich, um Senior zu werden.
    Der Senior lernt täglich, da er weiß, dass er immer Junior bleiben wird.
    Github

    Sonntag, 5. Februar 2023 16:05
  • Die Zeile 

    @articles.Where(x => x.Check).Select(x => x.Price).Sum()</p>

    Sieht ziemlich nach einer Datenbank-Abfrage aus. Ist das noch C# oder was Blazor spezifisches? Wo kann man diese Syntax in der Dok finden?
    Ich möchte nämlich anstelle die Checkbox durch ein Input mit der Anzahl Artikel ersetzen und das Total mit den durch Anzahl Multiplizierten Artikeln zusammenrechnen.


    • Bearbeitet Javango Sonntag, 5. Februar 2023 16:47 Präzisierung
    Sonntag, 5. Februar 2023 16:46
  • Das ist Linq und bestandteil von .NET. Linq kann man auf alle Auflistungen anwenden und ist sehr mächtig

    Gruß Thomas
    Der Junior lernt täglich, um Senior zu werden.
    Der Senior lernt täglich, da er weiß, dass er immer Junior bleiben wird.
    Github

    Sonntag, 5. Februar 2023 16:56