Zum Inhalt
Kuonen.UI

Eigenschaftsraster

Eigenschaften nach Kategorien wie im Eigenschaftenfenster von Visual Studio: die einer Schaltfläche (direkt aus ihren registrierten Eigenschaften, ohne Reflexion) oder einer Tabellenspalte wie im Tabellen-Designer von SSMS. Enter übernimmt, Esc verwirft, ein ungültiger Wert wird rot gemeldet; oben Kategorien, alphabetisch und Suche, unten die Beschreibung.

Vorführung: EigenschaftsrasterVorführung: Eigenschaftsraster

KXAML

EigenschaftsrasterSeite.kxaml
<StackPanel xmlns="https://ui.kuonen.org/kxaml"
            xmlns:x="https://ui.kuonen.org/kxaml/x"
            x:Class="Kuonen.UI.Galerie.Seiten.EigenschaftsrasterVorfuehrung"
            Orientation="Horizontal" Spacing="16">
  <StackPanel Spacing="8">
    <ComboBox x:Name="Objekt" MinWidth="160" />
    <PropertyGrid x:Name="Raster" Width="360" Height="420" />
    <TextBlock x:Name="Status" Text="Noch nichts geändert." />
  </StackPanel>
  <Border Padding="24" VerticalAlignment="Top">
    <Button x:Name="Vorschau" Content="Vorschau" />
  </Border>
</StackPanel>
EigenschaftsrasterSeite.kxaml.cs
using Kuonen.UI;

namespace Kuonen.UI.Galerie.Seiten;

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

/// <summary>KXAML-Fassung der Seite Eigenschaftsraster: der Aufbau steht in EigenschaftsrasterSeite.kxaml, hier das Verhalten.</summary>
public sealed partial class EigenschaftsrasterVorfuehrung
{
    public EigenschaftsrasterVorfuehrung()
    {
        InitializeComponent();
        EigenschaftsrasterSeite.Verdrahte(Raster, Objekt, Vorschau, Status);
    }
}

C#

EigenschaftsrasterSeite.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Layout;

namespace Kuonen.UI.Galerie.Seiten;

/// <summary>Galerie-Seite: das Eigenschaftsraster (<see cref="PropertyGrid"/>) mit einer Schaltfläche und einer Tabellenspalte.</summary>
public sealed partial class EigenschaftsrasterSeite : GalerieSeite
{
    public override string Titel => "Eigenschaftsraster";

    public override string Beschreibung =>
        "Eigenschaften nach Kategorien wie im Eigenschaftenfenster von Visual Studio: die einer Schaltfläche (direkt aus ihren registrierten "
        + "Eigenschaften, ohne Reflexion) oder einer Tabellenspalte wie im Tabellen-Designer von SSMS. Enter übernimmt, Esc verwirft, ein ungültiger "
        + "Wert wird rot gemeldet; oben Kategorien, alphabetisch und Suche, unten die Beschreibung.";

    public override string Stichworte => "PropertyGrid Eigenschaften Eigenschaftenfenster Inspektor Editor Kategorien Tabellen-Designer Spalte";

    public override string Datei => nameof(EigenschaftsrasterSeite);

    internal static readonly string[] Objekte = ["Schaltfläche", "Tabellenspalte"];

    /// <summary>Eine Spalte im Tabellen-Designer: beschreibt ihre Eigenschaften selbst.</summary>
    public sealed class Tabellenspalte : IPropertyGridSource
    {
        public string Name { get; set; } = "KundeId";
        public string Datentyp { get; set; } = "int";
        public int Laenge { get; set; } = 4;
        public bool NullZulassen { get; set; }
        public string? Standardwert { get; set; }
        public bool Identitaet { get; set; } = true;

        public IReadOnlyList<PropertyGridProperty> GetProperties() =>
        [
            new("Name", typeof(string), () => Name, v => Name = (string)v!) { Category = "Spalte", Description = "Der Name der Spalte." },
            new("Datentyp", typeof(string), () => Datentyp, v => Datentyp = (string)v!)
            {
                Category = "Spalte", Description = "Der SQL-Datentyp.", Options = ["int", "bigint", "decimal", "nvarchar", "datetime2", "bit"],
            },
            new("Laenge", typeof(int), () => Laenge, v => Laenge = (int)v!) { DisplayName = "Länge", Category = "Spalte", Description = "Länge in Bytes oder Zeichen." },
            new("NullZulassen", typeof(bool), () => NullZulassen, v => NullZulassen = (bool)v!)
            {
                DisplayName = "NULL zulassen", Category = "Spalte", Description = "Die Spalte darf leer bleiben.",
            },
            new("Standardwert", typeof(string), () => Standardwert, v => Standardwert = (string?)v) { Category = "Werte", Description = "Ausdruck für neue Zeilen." },
            new("Identitaet", typeof(bool), () => Identitaet, v => Identitaet = (bool)v!) { DisplayName = "Identität", Category = "Werte" },
        ];
    }

    /// <summary>Das Verhalten beider Fassungen: Objekt wählen, Änderungen melden.</summary>
    internal static void Verdrahte(PropertyGrid raster, ComboBox objekt, Button vorschau, TextBlock status)
    {
        var spalte = new Tabellenspalte();
        objekt.ItemsSource = Objekte;
        objekt.SelectionChanged += (_, _) => raster.SelectedObject = objekt.SelectedIndex == 1 ? spalte : vorschau;
        objekt.SelectedIndex = 0;
        raster.PropertyValueChanged += (_, e) =>
            status.Text = $"{e.Property.DisplayName}: {PropertyGrid.Format(e.OldValue)} → {PropertyGrid.Format(e.NewValue)}";
    }

    public override UIElement Baue()
    {
        var objekt = new ComboBox { MinWidth = 160 };
        var vorschau = new Button { Content = "Vorschau" };
        var status = new TextBlock { Text = "Noch nichts geändert." };
        var raster = new PropertyGrid { Width = 360, Height = 420 };
        Verdrahte(raster, objekt, vorschau, status);
        var links = new StackPanel { Spacing = 8, Children = { objekt, raster, status } };
        var flaeche = new Border { Padding = new Thickness(24), Child = vorschau, VerticalAlignment = VerticalAlignment.Top };
        return new StackPanel { Orientation = Orientation.Horizontal, Spacing = 16, Children = { links, flaeche } };
    }
}