Zum Inhalt
Kuonen.UI

ButtonSpinner

Drehfelder: ein ButtonSpinner um einen Text mit eigener Logik (Grösse S bis XL, die Knöpfe sperren an den Enden, wahlweise links), und DomainUpDown über Einträgen: Wochentage reihum (Wrap), Monate zum Tippen (ein Anfang wählt den ersten passenden), nur lesbar mit Buchstabensprung.

Vorführung: ButtonSpinnerVorführung: ButtonSpinner

KXAML

ButtonSpinnerSeite.kxaml
<StackPanel xmlns="https://ui.kuonen.org/kxaml"
            xmlns:x="https://ui.kuonen.org/kxaml/x"
            x:Class="Kuonen.UI.Galerie.Seiten.ButtonSpinnerVorfuehrung"
            Spacing="8">
  <TextBlock Text="ButtonSpinner (Grösse)" FontWeight="SemiBold" />
  <ButtonSpinner x:Name="Spinner" Spin="Spinner_Spin">
    <TextBlock x:Name="Groesse" Text="M" Width="40" VerticalAlignment="Center" />
  </ButtonSpinner>
  <CheckBox Content="Knöpfe links" Checked="Links_Checked" Unchecked="Links_Unchecked" />
  <TextBlock Text="Wochentag (reihum)" FontWeight="SemiBold" />
  <!-- Die Einträge setzt das Code-Behind (dieselben Listen wie die C#-Fassung). -->
  <DomainUpDown x:Name="Wochentag" Width="200" HorizontalAlignment="Left" Wrap="True" SelectedItemChanged="Wochentag_SelectedItemChanged" />
  <TextBlock Text="Monat (tippen)" FontWeight="SemiBold" />
  <DomainUpDown x:Name="Monat" Width="200" HorizontalAlignment="Left" SelectedItemChanged="Monat_SelectedItemChanged" />
  <TextBlock Text="Nur lesbar (Buchstabe springt)" FontWeight="SemiBold" />
  <DomainUpDown x:Name="Fest" Width="200" HorizontalAlignment="Left" ReadOnly="True" SelectedItemChanged="Fest_SelectedItemChanged" />
  <TextBlock x:Name="Meldung" Text="Bereit." />
</StackPanel>
ButtonSpinnerSeite.kxaml.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Ereignisse;

namespace Kuonen.UI.Galerie.Seiten;

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

/// <summary>KXAML-Fassung der ButtonSpinner-Seite: der Baum steht in ButtonSpinnerSeite.kxaml, hier die Einträge und das Verhalten.</summary>
public sealed partial class ButtonSpinnerVorfuehrung
{
    private bool _bereit;

    public ButtonSpinnerVorfuehrung()
    {
        InitializeComponent();
        ButtonSpinnerSeite.Sperre(Spinner, 1);
        ButtonSpinnerSeite.Fuelle(Wochentag, ButtonSpinnerSeite.Wochentage);
        Wochentag.SelectedIndex = 0;
        ButtonSpinnerSeite.Fuelle(Monat, ButtonSpinnerSeite.Monate);
        ButtonSpinnerSeite.Fuelle(Fest, ButtonSpinnerSeite.Wochentage);
        Fest.SelectedIndex = 0;
        _bereit = true; // die Startwahl meldet nicht (wie in der C#-Fassung, die erst danach abonniert)
    }

    private void Melde(string text)
    {
        if (_bereit) Meldung.Text = text;
    }

    private void Spinner_Spin(object sender, SpinEventArgs e)
    {
        ButtonSpinnerSeite.Drehe(Spinner, Groesse, e);
        Melde($"Grösse: {Groesse.Text}");
    }

    private void Links_Checked(object sender, RoutedEventArgs e) => Spinner.ButtonSpinnerLocation = Location.Left;

    private void Links_Unchecked(object sender, RoutedEventArgs e) => Spinner.ButtonSpinnerLocation = Location.Right;

    private void Wochentag_SelectedItemChanged(object? sender, EventArgs e) => Melde($"Tag: {Wochentag.SelectedItem}");

    private void Monat_SelectedItemChanged(object? sender, EventArgs e) => Melde($"Monat: {Monat.SelectedItem}");

    private void Fest_SelectedItemChanged(object? sender, EventArgs e) => Melde($"Nur lesbar: {Fest.SelectedItem}");
}

C#

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

namespace Kuonen.UI.Galerie.Seiten;

/// <summary>Galerie-Seite: <see cref="ButtonSpinner"/> und <see cref="DomainUpDown"/>.</summary>
public sealed partial class ButtonSpinnerSeite : GalerieSeite
{
    public override string Titel => "ButtonSpinner";

    public override string Beschreibung =>
        "Drehfelder: ein ButtonSpinner um einen Text mit eigener Logik (Grösse S bis XL, die Knöpfe sperren an den Enden, wahlweise links), und DomainUpDown "
        + "über Einträgen: Wochentage reihum (Wrap), Monate zum Tippen (ein Anfang wählt den ersten passenden), nur lesbar mit Buchstabensprung.";

    public override string Stichworte => "Drehfeld Spinner Auf Ab Stepper Wochentag Liste ButtonSpinner DomainUpDown Spin ValidSpinDirection Wrap ReadOnly";

    public override string Datei => nameof(ButtonSpinnerSeite);

    internal static readonly string[] Groessen = ["S", "M", "L", "XL"];

    internal static readonly string[] Wochentage = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"];

    internal static readonly string[] Monate =
        ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];

    // Die Grösse dreht im Text und sperrt die Richtung an den Enden.
    internal static void Drehe(ButtonSpinner spinner, TextBlock text, SpinEventArgs e)
    {
        int i = Math.Max(Array.IndexOf(Groessen, text.Text), 0);
        i = Math.Clamp(i + (e.Direction == SpinDirection.Increase ? 1 : -1), 0, Groessen.Length - 1);
        text.Text = Groessen[i];
        Sperre(spinner, i);
    }

    internal static void Sperre(ButtonSpinner spinner, int i) =>
        spinner.ValidSpinDirection = (i < Groessen.Length - 1 ? ValidSpinDirections.Increase : ValidSpinDirections.None)
            | (i > 0 ? ValidSpinDirections.Decrease : ValidSpinDirections.None);

    internal static void Fuelle(DomainUpDown feld, string[] eintraege)
    {
        foreach (var e in eintraege) feld.Items.Add(e);
    }

    public override UIElement Baue()
    {
        var meldung = new TextBlock { Text = "Bereit." };
        var groesse = new TextBlock { Text = "M", Width = 40, VerticalAlignment = VerticalAlignment.Center };
        var spinner = new ButtonSpinner { Content = groesse };
        Sperre(spinner, 1);
        spinner.Spin += (_, e) =>
        {
            Drehe(spinner, groesse, e);
            meldung.Text = $"Grösse: {groesse.Text}";
        };
        var links = new CheckBox { Content = "Knöpfe links" };
        links.Checked += (_, _) => spinner.ButtonSpinnerLocation = Location.Left;
        links.Unchecked += (_, _) => spinner.ButtonSpinnerLocation = Location.Right;
        var tag = new DomainUpDown { Width = 200, HorizontalAlignment = HorizontalAlignment.Left, Wrap = true };
        Fuelle(tag, Wochentage);
        tag.SelectedIndex = 0;
        tag.SelectedItemChanged += (_, _) => meldung.Text = $"Tag: {tag.SelectedItem}";
        var monat = new DomainUpDown { Width = 200, HorizontalAlignment = HorizontalAlignment.Left };
        Fuelle(monat, Monate);
        monat.SelectedItemChanged += (_, _) => meldung.Text = $"Monat: {monat.SelectedItem}";
        var fest = new DomainUpDown { Width = 200, HorizontalAlignment = HorizontalAlignment.Left, ReadOnly = true };
        Fuelle(fest, Wochentage);
        fest.SelectedIndex = 0;
        fest.SelectedItemChanged += (_, _) => meldung.Text = $"Nur lesbar: {fest.SelectedItem}";
        return new StackPanel
        {
            Spacing = 8,
            Children =
            {
                new TextBlock { Text = "ButtonSpinner (Grösse)", FontWeight = 600 },
                spinner,
                links,
                new TextBlock { Text = "Wochentag (reihum)", FontWeight = 600 },
                tag,
                new TextBlock { Text = "Monat (tippen)", FontWeight = 600 },
                monat,
                new TextBlock { Text = "Nur lesbar (Buchstabe springt)", FontWeight = 600 },
                fest,
                meldung,
            },
        };
    }
}