Table of Contents

Seiten mit BpPage

Mit BpPage definieren Sie Listen, Detailansichten und Dashboards in C#. Studio und Portal stellen die zurückgegebenen UI-Bausteine dar.

Seite definieren

Eine Seite erbt von BpPage und liefert in BuildAsync eine Liste von Bausteinen:

public sealed class UebersichtSeite(IBpSession session) : BpPage
{
    public override string Title => "Übersicht";

    public override Task<Page> BuildAsync(PageRequest request, CancellationToken cancellationToken)
    {
        return Task.FromResult(new Page(
        [
            new Header("Adressen") { Subtitle = "Stand von heute" },
            new Note("Hier steht später eine Tabelle.")
        ]));
    }
}

Diese Properties steuern Titel, Zielgruppe und Navigation:

Override Bedeutung
Title Titel in Menü und Brotkrumen
Audience Portal für Endanwender unter /apps (Voreinstellung), Admin für einen Reiter auf der Modulseite in Studio
ShowInMenu false nimmt die Seite aus dem Menü, erreichbar bleibt sie über Links
Order Sortierung im Menü

Bei Interaktionen wird BuildAsync erneut aufgerufen. Halten Sie Seiten zustandslos und lesend; begrenzen Sie Abfragen mit includes: und einem Seitenlimit.

Beispiel: Adressbuch mit Suche

Das Adressbuch zeigt 20 Adressen pro Seite und verlinkt auf die nachfolgende Detailseite:

public sealed class AdressbuchSeite(IBpSession session) : BpPage
{
    public override string Title => "Adressbuch";

    public override Task<Page> BuildAsync(
        PageRequest request,
        CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();

        const int pageSize = 20;
        var search = request.Text("search");
        var page = Math.Clamp(request.Int("page", 1), 1, 10000);
        var query = session.QueryDataSet<Adressen>(includes: ["AdrNr", "SuchBeg"]);

        if (!string.IsNullOrWhiteSpace(search))
            query = query.Where(adresse => adresse.SuchBeg!.Contains(search));

        var rows = query
            .OrderBy(adresse => adresse.AdrNr)
            .Skip((page - 1) * pageSize)
            .Take(pageSize + 1)
            .ToList();

        return Task.FromResult(new Page(
        [
            new Header("Adressbuch"),
            new SearchBox("search") { Placeholder = "Suchbegriff" },
            new Table
            {
                Columns = [new TableColumn("Nr.", Mono: true), new TableColumn("Name")],
                Rows = rows.Take(pageSize).Select(adresse => new TableRow(
                    [adresse.AdrNr ?? "", adresse.SuchBeg ?? ""])
                {
                    Opens = Link.To<AdresseSeite>(("id", adresse.AdrNr!))
                }).ToList()
            },
            new Pager("page") { HasMore = rows.Count > pageSize }
        ]));
    }
}

Suchwert und Seitenzahl stehen unter den Baustein-IDs im PageRequest bereit:

Zugriff Verhalten
request.Text("search") Suchwert oder null, wenn er fehlt oder leer ist
request.Int("page", 1) Seitenzahl oder der Rückfallwert
request.Required("id") Pflichtwert; fehlt er, wird eine fehlerhafte Anfrage gemeldet

Pager.HasMore aktiviert die nächste Seite. Eine geänderte Suche setzt den Pager zurück.

Seiten miteinander verknüpfen

Link.To<T> öffnet eine Seite mit den angegebenen Parametern:

new TableRow([adr.AdrNr!, adr.SuchBeg ?? string.Empty])
{
    Opens = Link.To<AdresseSeite>(("id", adr.AdrNr!))
}

Ein Klick auf die Zeile öffnet AdresseSeite, und dort steht das Argument im PageRequest bereit:

public sealed class AdresseSeite(IBpSession session) : BpPage
{
    public override string Title => "Adresse";

    // Detailseite: über Links erreichbar, aber nicht im Menü.
    public override bool ShowInMenu => false;

    public override Task<Page> BuildAsync(PageRequest request, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        var adrNr = request.Required("id");
        var adresse = session
                .QueryDataSet<Adressen>(includes: ["AdrNr", "SuchBeg", "Status", "Branche"])
                .FirstOrDefault(adr => adr.AdrNr == adrNr)
            ?? throw new PageArgumentException($"Keine Adresse mit Nummer '{adrNr}' gefunden.");

        return Task.FromResult(new Page(
        [
            new Header(adresse.SuchBeg ?? adrNr)
            {
                Subtitle = adrNr,
                Chips = [new KeyValueRow("Status", adresse.Status ?? "-")]
            }
        ]));
    }
}

Mit PageArgumentException melden Sie einen ungültigen Parameter als fehlerhafte Anfrage.

Seiten aufrufen

Nach dem erneuten Laden des Moduls ist Adressbuch im Menü der freigeschalteten App erreichbar.