Zum Inhalt
Kuonen.UI

TreeDataGrid

Ein DataGrid mit Baumzeilen: die erste Spalte rückt nach der Tiefe ein und zeigt das Aufklappzeichen. Auf- und Zuklappen fügt nur die Nachkommen ein; ein Klick auf einen Spaltenkopf sortiert Geschwister. Tasten wie im Explorer: Rechts und Links, Plus, Minus und Stern.

Vorführung: TreeDataGridVorführung: TreeDataGrid

KXAML

TreeDataGridSeite.kxaml
<StackPanel xmlns="https://ui.kuonen.org/kxaml"
            xmlns:x="https://ui.kuonen.org/kxaml/x"
            xmlns:local="using:Kuonen.UI.Galerie.Seiten"
            x:Class="Kuonen.UI.Galerie.Seiten.TreeDataGridVorfuehrung"
            Spacing="12">
  <StackPanel Orientation="Horizontal" Spacing="8">
    <Button Content="Alles aufklappen" Click="Alle_Click" />
    <Button Content="Alles zuklappen" Click="Keine_Click" />
  </StackPanel>
  <!-- Wurzeln und Baumdaten kommen aus dem Code-Behind; die Baumspalte umhüllt eine Textspalte. -->
  <TreeDataGrid x:Name="Gitter" Width="440" Height="260">
    <DataGridExpanderColumn Header="Name" Width="220">
      <DataGridTextColumn x:DataType="local:Ablage" Binding="{Binding Name, Mode=OneWay}" />
    </DataGridExpanderColumn>
    <DataGridTextColumn x:DataType="local:Ablage" Header="Typ" Width="80" Binding="{Binding Typ, Mode=OneWay}" />
    <DataGridTextColumn x:DataType="local:Ablage" Header="Grösse" Width="80" Binding="{Binding Groesse, Mode=OneWay}" />
  </TreeDataGrid>
</StackPanel>
TreeDataGridSeite.kxaml.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Ereignisse;

namespace Kuonen.UI.Galerie.Seiten;

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

/// <summary>KXAML-Fassung der TreeDataGrid-Seite: der Baum steht in TreeDataGridSeite.kxaml, hier Wurzeln, Baumdaten und die Knöpfe.</summary>
public sealed partial class TreeDataGridVorfuehrung
{
    public TreeDataGridVorfuehrung()
    {
        InitializeComponent();
        Gitter.Roots = TreeDataGridSeite.Wurzeln();
        Gitter.Baum = new Baumdaten<Ablage>(a => a.Kinder);
    }

    private void Alle_Click(object sender, RoutedEventArgs e) => Gitter.ExpandAll();

    private void Keine_Click(object sender, RoutedEventArgs e) => Gitter.CollapseAll();
}

C#

TreeDataGridSeite.cs
using Kuonen.UI;
using Kuonen.UI.Bindung;
using Kuonen.UI.Controls;
using Kuonen.UI.Layout;

namespace Kuonen.UI.Galerie.Seiten;

/// <summary>Galerie-Seite: <see cref="TreeDataGrid"/>, Baumzeilen im DataGrid.</summary>
public sealed partial class TreeDataGridSeite : GalerieSeite
{
    public override string Titel => "TreeDataGrid";

    public override string Beschreibung =>
        "Ein DataGrid mit Baumzeilen: die erste Spalte rückt nach der Tiefe ein und zeigt das Aufklappzeichen. Auf- und Zuklappen fügt nur die "
        + "Nachkommen ein; ein Klick auf einen Spaltenkopf sortiert Geschwister. Tasten wie im Explorer: Rechts und Links, Plus, Minus und Stern.";

    public override string Stichworte => "Baum Tabelle Grid Ordner Explorer DataGridExpanderColumn Baumdaten aufklappen einrücken TreeListView";

    public override string Datei => nameof(TreeDataGridSeite);

    /// <summary>Ein kleiner Dateibaum: drei Ordner mit Unterordnern und Dateien.</summary>
    public static Ablage[] Wurzeln()
    {
        var wurzeln = new List<Ablage>();
        foreach (var ordner in new[] { "Bilder", "Dokumente", "Musik" })
        {
            var oben = new Ablage(ordner, "Ordner", 0);
            for (int u = 1; u <= 3; u++)
            {
                var unter = new Ablage($"{ordner} {u}", "Ordner", 0);
                for (int d = 1; d <= 4; d++) unter.Kinder.Add(new Ablage($"Datei {u}.{d}", "Datei", (d * 137 % 900) + (u * 10)));
                oben.Kinder.Add(unter);
            }
            wurzeln.Add(oben);
        }
        return [.. wurzeln];
    }

    public override UIElement Baue()
    {
        var grid = new TreeDataGrid { Width = 440, Height = 260, Roots = Wurzeln(), Baum = new Baumdaten<Ablage>(a => a.Kinder) };
        grid.Columns.Add(new DataGridExpanderColumn(new DataGridTextColumn { Binding = Zellbindung.Aus(BindingPath.Stufe<Ablage, string>("Name", a => a.Name), schreibbar: false) })
        {
            Header = "Name",
            Width = GridLength.Pixel(220),
        });
        grid.Columns.Add(new DataGridTextColumn { Header = "Typ", Width = GridLength.Pixel(80), Binding = Zellbindung.Aus(BindingPath.Stufe<Ablage, string>("Typ", a => a.Typ), schreibbar: false) });
        grid.Columns.Add(new DataGridTextColumn { Header = "Grösse", Width = GridLength.Pixel(80), Binding = Zellbindung.Aus(BindingPath.Stufe<Ablage, int>("Groesse", a => a.Groesse), schreibbar: false) });

        var alle = new Button { Content = "Alles aufklappen" };
        alle.Click += (_, _) => grid.ExpandAll();
        var keine = new Button { Content = "Alles zuklappen" };
        keine.Click += (_, _) => grid.CollapseAll();
        var knoepfe = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8 };
        knoepfe.Children.Add(alle);
        knoepfe.Children.Add(keine);

        var seite = new StackPanel { Spacing = 12 };
        seite.Children.Add(knoepfe);
        seite.Children.Add(grid);
        return seite;
    }
}

/// <summary>Ein Ordner oder eine Datei der Vorführung.</summary>
public sealed class Ablage(string name, string typ, int groesse)
{
    public string Name { get; } = name;

    public string Typ { get; } = typ;

    public int Groesse { get; } = groesse;

    public List<Ablage> Kinder { get; } = [];
}