Calendar
Ein Monatskalender mit Jahr- und Jahrzehntansicht (Klick auf den Titel). Gewählt wird ein Tag, ein Bereich oder mehrere; gesperrte Tage (Bereiche, Vergangenheit, Wochenenden über einen Prüfer) sind per Tastatur erreichbar, aber nicht wählbar. Das Format (Monate, Wochentage, erster Tag, Wochenzählung) gilt je Kalender; die Designsprache bestimmt Zellen, Titel und Band.


KXAML
CalendarSeite.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.CalendarVorfuehrung"
Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="24">
<!-- Ab 2026, die Weihnachtstage gesperrt (Sammlung als Kindelemente); das Format setzt das Code-Behind. -->
<Calendar x:Name="Kalender" DisplayDateStart="2026-01-01" SelectedDatesChanged="Kalender_SelectedDatesChanged">
<Calendar.BlackoutDates>
<CalendarDateRange Start="2026-12-24" End="2026-12-26" />
</Calendar.BlackoutDates>
</Calendar>
<StackPanel Spacing="8">
<TextBlock Text="Auswahlart" FontWeight="SemiBold" />
<!-- Erst die Einträge, dann die Wahl, dann die Handler: der Startzustand meldet nichts. -->
<ComboBox x:Name="Art" Width="180" ItemsSource="{x:Static local:CalendarSeiteHilfe.Auswahlarten}" SelectedIndex="0"
SelectionChanged="Art_SelectionChanged" />
<CheckBox x:Name="Wochennummern" Content="Wochennummern zeigen" Click="Wochennummern_Click" />
<CheckBox x:Name="Vergangenheit" Content="Vergangenheit sperren" Click="Vergangenheit_Click" />
<CheckBox x:Name="Wochenenden" Content="Wochenenden sperren" Click="Wochenenden_Click" />
<CheckBox x:Name="Anzeigeende" Content="Anzeige bis Ende 2026 begrenzen" Click="Anzeigeende_Click" />
<TextBlock Text="Erster Wochentag" FontWeight="SemiBold" />
<ComboBox x:Name="Erster" Width="180" ItemsSource="{x:Static local:CalendarSeiteHilfe.ErsteTage}" SelectedIndex="0"
SelectionChanged="Erster_SelectionChanged" />
<TextBlock Text="Format" FontWeight="SemiBold" />
<ComboBox x:Name="Format" Width="180" ItemsSource="{x:Static local:CalendarSeiteHilfe.Formate}" SelectedIndex="0"
SelectionChanged="Format_SelectionChanged" />
</StackPanel>
</StackPanel>
<TextBlock x:Name="Meldung" Text="Gewählt: nichts." />
</StackPanel>CalendarSeite.kxaml.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Ereignisse;
namespace Kuonen.UI.Galerie.Seiten;
public sealed partial class CalendarSeite
{
protected override Func<UIElement>? KxamlFabrik => () => new CalendarVorfuehrung();
}
/// <summary>KXAML-Fassung der Calendar-Seite: der Baum steht in CalendarSeite.kxaml, hier das Format und das Verhalten.</summary>
public sealed partial class CalendarVorfuehrung
{
public CalendarVorfuehrung()
{
InitializeComponent();
// Das Format gilt nur für diesen Kalender (nicht für die ganze Anwendung).
Kalender.Kalenderformat = Kalenderformat.Fuer("de-CH");
}
private void Kalender_SelectedDatesChanged(object sender, SelectionChangedEventArgs e) => Meldung.Text = CalendarSeiteHilfe.Meldung(Kalender);
private void Art_SelectionChanged(object sender, SelectionChangedEventArgs e) => Kalender.SelectionMode = CalendarSeiteHilfe.Auswahlart(Art.SelectedIndex);
private void Wochennummern_Click(object sender, RoutedEventArgs e) => Kalender.ShowWeekNumbers = Wochennummern.IsChecked == true;
private void Vergangenheit_Click(object sender, RoutedEventArgs e) => CalendarSeiteHilfe.SperreVergangenheit(Kalender, Vergangenheit.IsChecked == true);
private void Wochenenden_Click(object sender, RoutedEventArgs e) => CalendarSeiteHilfe.SperreWochenenden(Kalender, Wochenenden.IsChecked == true);
private void Anzeigeende_Click(object sender, RoutedEventArgs e) => Kalender.DisplayDateEnd = Anzeigeende.IsChecked == true ? new DateTime(2026, 12, 31) : null;
private void Erster_SelectionChanged(object sender, SelectionChangedEventArgs e) => CalendarSeiteHilfe.WaehleErstenTag(Kalender, Erster.SelectedIndex);
private void Format_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Kalender.Kalenderformat = Kalenderformat.Fuer(CalendarSeiteHilfe.Formate[Format.SelectedIndex]);
Meldung.Text = CalendarSeiteHilfe.Meldung(Kalender);
}
}C#
CalendarSeite.cs
using Kuonen.UI;
using Kuonen.UI.Controls;
using Kuonen.UI.Layout;
namespace Kuonen.UI.Galerie.Seiten;
/// <summary>Galerie-Seite: <see cref="Calendar"/>.</summary>
public sealed partial class CalendarSeite : GalerieSeite
{
public override string Titel => "Calendar";
public override string Beschreibung =>
"Ein Monatskalender mit Jahr- und Jahrzehntansicht (Klick auf den Titel). Gewählt wird ein Tag, ein Bereich oder mehrere; "
+ "gesperrte Tage (Bereiche, Vergangenheit, Wochenenden über einen Prüfer) sind per Tastatur erreichbar, aber nicht wählbar. "
+ "Das Format (Monate, Wochentage, erster Tag, Wochenzählung) gilt je Kalender; die Designsprache bestimmt Zellen, Titel und Band.";
public override string Stichworte => "Kalender Datum Monat Jahr Auswahl BlackoutDates Wochennummern DisplayDate SelectedDates FirstDayOfWeek";
public override string Datei => nameof(CalendarSeite);
public override UIElement Baue()
{
// Ab 2026, die Weihnachtstage gesperrt; das Format gilt nur für diesen Kalender (nicht für die ganze Anwendung).
var kalender = new Calendar { DisplayDateStart = new DateTime(2026, 1, 1), Kalenderformat = Kalenderformat.Fuer("de-CH") };
kalender.BlackoutDates.Add(new CalendarDateRange(new DateTime(2026, 12, 24), new DateTime(2026, 12, 26)));
var meldung = new TextBlock { Text = "Gewählt: nichts." };
kalender.SelectedDatesChanged += (_, _) => meldung.Text = CalendarSeiteHilfe.Meldung(kalender);
var art = new ComboBox { Width = 180, ItemsSource = CalendarSeiteHilfe.Auswahlarten, SelectedIndex = 0 };
art.SelectionChanged += (_, _) => kalender.SelectionMode = CalendarSeiteHilfe.Auswahlart(art.SelectedIndex);
var wochennummern = new CheckBox { Content = "Wochennummern zeigen" };
wochennummern.Click += (_, _) => kalender.ShowWeekNumbers = wochennummern.IsChecked == true;
var vergangenheit = new CheckBox { Content = "Vergangenheit sperren" };
vergangenheit.Click += (_, _) => CalendarSeiteHilfe.SperreVergangenheit(kalender, vergangenheit.IsChecked == true);
var wochenenden = new CheckBox { Content = "Wochenenden sperren" };
wochenenden.Click += (_, _) => CalendarSeiteHilfe.SperreWochenenden(kalender, wochenenden.IsChecked == true);
var anzeigeende = new CheckBox { Content = "Anzeige bis Ende 2026 begrenzen" };
anzeigeende.Click += (_, _) => kalender.DisplayDateEnd = anzeigeende.IsChecked == true ? new DateTime(2026, 12, 31) : null;
var erster = new ComboBox { Width = 180, ItemsSource = CalendarSeiteHilfe.ErsteTage, SelectedIndex = 0 };
erster.SelectionChanged += (_, _) => CalendarSeiteHilfe.WaehleErstenTag(kalender, erster.SelectedIndex);
var format = new ComboBox { Width = 180, ItemsSource = CalendarSeiteHilfe.Formate, SelectedIndex = 0 };
format.SelectionChanged += (_, _) =>
{
kalender.Kalenderformat = Kalenderformat.Fuer(CalendarSeiteHilfe.Formate[format.SelectedIndex]);
meldung.Text = CalendarSeiteHilfe.Meldung(kalender);
};
var optionen = new StackPanel { Spacing = 8 };
optionen.Children.Add(new TextBlock { Text = "Auswahlart", FontWeight = 600 });
optionen.Children.Add(art);
optionen.Children.Add(wochennummern);
optionen.Children.Add(vergangenheit);
optionen.Children.Add(wochenenden);
optionen.Children.Add(anzeigeende);
optionen.Children.Add(new TextBlock { Text = "Erster Wochentag", FontWeight = 600 });
optionen.Children.Add(erster);
optionen.Children.Add(new TextBlock { Text = "Format", FontWeight = 600 });
optionen.Children.Add(format);
var oben = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 24 };
oben.Children.Add(kalender);
oben.Children.Add(optionen);
var seite = new StackPanel { Spacing = 12 };
seite.Children.Add(oben);
seite.Children.Add(meldung);
return seite;
}
}
/// <summary>Die Wahlmöglichkeiten und Handgriffe der Seite (beide Fassungen nutzen sie).</summary>
public static class CalendarSeiteHilfe
{
/// <summary>Die Auswahlarten in der Reihenfolge der Liste.</summary>
public static readonly string[] Auswahlarten = ["Ein Tag", "Ein Bereich", "Mehrere Bereiche", "Keine"];
/// <summary>Der erste Wochentag: wie das Format oder fest.</summary>
public static readonly string[] ErsteTage = ["Wie das Format", "Montag", "Sonntag"];
/// <summary>Die Formate (Eigenschaft <c>Kalenderformat</c> des Kalenders).</summary>
public static readonly string[] Formate = ["de-CH", "fr-FR", "en-US"];
private static readonly Func<DateTime, bool> Wochenende = tag => tag.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
public static CalendarSelectionMode Auswahlart(int index) => index switch
{
1 => CalendarSelectionMode.SingleRange,
2 => CalendarSelectionMode.MultipleRange,
3 => CalendarSelectionMode.None,
_ => CalendarSelectionMode.SingleDate,
};
public static void SperreVergangenheit(Calendar kalender, bool sperren)
{
if (sperren)
{
kalender.SelectedDates.Clear(); // ein gewählter Tag in der Vergangenheit liesse sich nicht sperren
kalender.BlackoutDates.AddDatesInPast();
return;
}
for (int i = kalender.BlackoutDates.Count - 1; i >= 0; i--)
if (kalender.BlackoutDates[i].Start == DateTime.MinValue) kalender.BlackoutDates.RemoveAt(i);
}
public static void SperreWochenenden(Calendar kalender, bool sperren) => kalender.IstGesperrt = sperren ? Wochenende : null;
public static void WaehleErstenTag(Calendar kalender, int index)
{
if (index == 1) kalender.FirstDayOfWeek = DayOfWeek.Monday;
else if (index == 2) kalender.FirstDayOfWeek = DayOfWeek.Sunday;
else kalender.ClearValue(Calendar.FirstDayOfWeekProperty);
}
/// <summary>Die Meldungszeile: nichts, ein Tag oder die Zahl der Tage von … bis ….</summary>
public static string Meldung(Calendar kalender)
{
var format = kalender.Kalenderformat ?? Kalenderformat.Aktuell;
var tage = kalender.SelectedDates;
if (tage.Count == 0) return "Gewählt: nichts.";
if (tage.Count == 1) return $"Gewählt: {format.Formatiere(tage[0], format.KurzesDatum)}.";
var erster = tage[0];
var letzter = tage[0];
foreach (var tag in tage)
{
if (tag < erster) erster = tag;
if (tag > letzter) letzter = tag;
}
return $"Gewählt: {tage.Count} Tage, {format.Formatiere(erster, format.KurzesDatum)} bis {format.Formatiere(letzter, format.KurzesDatum)}.";
}
}