Zum Inhalt
Kuonen.UI

Ergebnisraster

Das DataGrid wie das Ergebnisraster von SSMS: Zellbereiche wählen (Klick, Umschalt, Strg, Ziehen), Strg+C kopiert, Strg+Umschalt+C mit Köpfen, Strg+V fügt ein. Die Filterzeile filtert je Spalte („enthält“ oder mit = < > vergleichen), Entf löscht Zeilen, Pfeil runter in der letzten Zeile legt eine an. Unten der Export als CSV oder JSON.

Vorführung: ErgebnisrasterVorführung: Ergebnisraster

KXAML

ErgebnisrasterSeite.kxaml
<StackPanel xmlns="https://ui.kuonen.org/kxaml"
            xmlns:x="https://ui.kuonen.org/kxaml/x"
            x:Class="Kuonen.UI.Galerie.Seiten.ErgebnisrasterVorfuehrung"
            Spacing="12">
  <StackPanel Orientation="Horizontal" Spacing="12">
    <CheckBox x:Name="Zellen" Content="Zellen wählen" />
    <CheckBox x:Name="Filter" Content="Filterzeile" />
    <Button x:Name="Neu" Content="Neue Zeile" />
    <Button x:Name="Loeschen" Content="Löschen" />
    <Button x:Name="Csv" Content="Als CSV" />
    <Button x:Name="Json" Content="Als JSON" />
    <TextBlock x:Name="Stand" VerticalAlignment="Center" />
  </StackPanel>
  <!-- Die Spalten (darunter eine Auswahlliste mit Anzeigefunktion) setzt das Code-Behind; so teilen sich beide Fassungen das Verhalten. -->
  <DataGrid x:Name="Raster" Width="640" Height="300" />
  <TextBox x:Name="Ausgabe" Width="640" Height="140" AcceptsReturn="True" IsReadOnly="True" FontFamily="Consolas" />
</StackPanel>
ErgebnisrasterSeite.kxaml.cs
using Kuonen.UI;

namespace Kuonen.UI.Galerie.Seiten;

public sealed partial class ErgebnisrasterSeite
{
    protected override Func<UIElement>? KxamlFabrik => () => new ErgebnisrasterVorfuehrung();
}

/// <summary>KXAML-Fassung der Seite Ergebnisraster: der Aufbau steht in ErgebnisrasterSeite.kxaml, hier das Verhalten.</summary>
public sealed partial class ErgebnisrasterVorfuehrung
{
    public ErgebnisrasterVorfuehrung()
    {
        InitializeComponent();
        ErgebnisrasterSeite.Verdrahte(Raster, Zellen, Filter, Neu, Loeschen, Csv, Json, Ausgabe, Stand);
    }
}

C#

ErgebnisrasterSeite.cs
using System.Collections.ObjectModel;
using System.Globalization;
using Kuonen.UI;
using Kuonen.UI.Bindung;
using Kuonen.UI.Controls;
using Kuonen.UI.Layout;
using Kuonen.UI.Text;

namespace Kuonen.UI.Galerie.Seiten;

/// <summary>Galerie-Seite: das DataGrid als Ergebnisraster eines SQL-Managers (Plan T): Zellbereiche, Filter, Zeilen anlegen und löschen, Export.</summary>
public sealed partial class ErgebnisrasterSeite : GalerieSeite
{
    public override string Titel => "Ergebnisraster";

    public override string Beschreibung =>
        "Das DataGrid wie das Ergebnisraster von SSMS: Zellbereiche wählen (Klick, Umschalt, Strg, Ziehen), Strg+C kopiert, Strg+Umschalt+C mit Köpfen, "
        + "Strg+V fügt ein. Die Filterzeile filtert je Spalte („enthält“ oder mit = < > vergleichen), Entf löscht Zeilen, Pfeil runter in der letzten "
        + "Zeile legt eine an. Unten der Export als CSV oder JSON.";

    public override string Stichworte => "DataGrid Ergebnisraster SQL SSMS Zellen Auswahl Filter Export CSV JSON Kopieren Einfügen Zwischenablage Löschen";

    public override string Datei => nameof(ErgebnisrasterSeite);

    internal static readonly string[] Status = ["offen", "geliefert", "storniert"];

    /// <summary>Eine Bestellung (eine Zeile des Ergebnisses).</summary>
    public sealed class Bestellung
    {
        public string Kunde { get; set; } = "";
        public int Menge { get; set; }
        public decimal Betrag { get; set; }
        public string? Stand { get; set; }
        public bool Bezahlt { get; set; }
    }

    internal static ObservableCollection<Bestellung> Daten()
    {
        string[] kunden = ["Muster AG", "Beispiel GmbH", "Kuonen", "Alpenblick", "Seeblick", "Talstation"];
        var liste = new ObservableCollection<Bestellung>();
        for (int i = 0; i < 40; i++)
            liste.Add(new Bestellung { Kunde = kunden[i % kunden.Length], Menge = 1 + (i * 7 % 12), Betrag = 12.5m * (1 + (i % 9)), Stand = Status[i % 3], Bezahlt = i % 4 == 0 });
        return liste;
    }

    /// <summary>Das Verhalten beider Fassungen: Spalten, Schalter und Export.</summary>
    internal static void Verdrahte(DataGrid raster, CheckBox zellen, CheckBox filter, Button neu, Button loeschen, Button csv, Button json, TextBox ausgabe, TextBlock stand)
    {
        raster.Columns.Add(new DataGridTextColumn
        {
            Header = "Kunde", Width = GridLength.Star(), SortMemberPath = "Kunde",
            Binding = Zellbindung.Aus(BindingPath.Stufe<Bestellung, string>("Kunde", static b => b.Kunde, static (b, w) => b.Kunde = w ?? "")),
        });
        raster.Columns.Add(new DataGridTextColumn
        {
            Header = "Menge", Width = GridLength.Pixel(80), SortMemberPath = "Menge", TextAlignment = TextAusrichtung.Ende,
            Binding = Zellbindung.Aus(BindingPath.Stufe<Bestellung, int>("Menge", static b => b.Menge, static (b, w) => b.Menge = w)),
        });
        raster.Columns.Add(new DataGridTextColumn
        {
            Header = "Betrag", Width = GridLength.Pixel(100), SortMemberPath = "Betrag", Format = "N2", TextAlignment = TextAusrichtung.Ende,
            Binding = Zellbindung.Aus(BindingPath.Stufe<Bestellung, decimal>("Betrag", static b => b.Betrag, static (b, w) => b.Betrag = w)),
        });
        raster.Columns.Add(new DataGridComboBoxColumn
        {
            Header = "Stand", Width = GridLength.Pixel(110), SortMemberPath = "Stand", ItemsSource = Status,
            Binding = Zellbindung.Aus(BindingPath.Stufe<Bestellung, string?>("Stand", static b => b.Stand, static (b, w) => b.Stand = w)),
        });
        raster.Columns.Add(new DataGridCheckBoxColumn
        {
            Header = "Bezahlt", Width = GridLength.Pixel(80), SortMemberPath = "Bezahlt",
            Binding = Zellbindung.Aus(BindingPath.Stufe<Bestellung, bool>("Bezahlt", static b => b.Bezahlt, static (b, w) => b.Bezahlt = w)),
        });
        raster.ItemsSource = Daten();
        raster.CanUserAddRows = true;
        raster.NewItemFactory = static () => new Bestellung { Kunde = "Neu", Stand = Status[0] };
        raster.SelectionUnit = DataGridSelectionUnit.Cell;
        zellen.IsChecked = true;
        zellen.Checked += (_, _) => raster.SelectionUnit = DataGridSelectionUnit.Cell;
        zellen.Unchecked += (_, _) => raster.SelectionUnit = DataGridSelectionUnit.FullRow;
        filter.Checked += (_, _) => raster.ShowFilterRow = true;
        filter.Unchecked += (_, _) => raster.ShowFilterRow = false;
        neu.Click += (_, _) => raster.AddNewRow();
        loeschen.Click += (_, _) => raster.DeleteSelectedRows();
        csv.Click += (_, _) => Exportiere(raster, DataGridExportFormat.Csv, ausgabe);
        json.Click += (_, _) => Exportiere(raster, DataGridExportFormat.Json, ausgabe);
        void Zaehle() => stand.Text = $"{raster.ZeilenAnzahl.ToString(CultureInfo.CurrentCulture)} Zeilen";
        raster.SelectionChanged += (_, _) => Zaehle();
        Zaehle();
    }

    // Exportiert die Auswahl (ohne Auswahl alles) in das Ausgabefeld.
    private static void Exportiere(DataGrid raster, DataGridExportFormat format, TextBox ausgabe)
    {
        var text = new StringWriter();
        DataGridExport.Schreibe(raster, text, format, nurAuswahl: raster.Auswahl.Anzahl > 0);
        ausgabe.Text = text.ToString();
    }

    public override UIElement Baue()
    {
        var zellen = new CheckBox { Content = "Zellen wählen" };
        var filter = new CheckBox { Content = "Filterzeile" };
        var neu = new Button { Content = "Neue Zeile" };
        var loeschen = new Button { Content = "Löschen" };
        var csv = new Button { Content = "Als CSV" };
        var json = new Button { Content = "Als JSON" };
        var stand = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
        var raster = new DataGrid { Width = 640, Height = 300 };
        var ausgabe = new TextBox { Width = 640, Height = 140, AcceptsReturn = true, IsReadOnly = true, FontFamily = new("Consolas") };
        Verdrahte(raster, zellen, filter, neu, loeschen, csv, json, ausgabe, stand);
        var leiste = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 12, Children = { zellen, filter, neu, loeschen, csv, json, stand } };
        return new StackPanel { Spacing = 12, Children = { leiste, raster, ausgabe } };
    }
}