TextBox
Ein Eingabefeld: Caret, Auswahl mit Maus und Umschalt, Wortsprünge mit Strg, Zwischenablage (Strg+C/X/V), Rückgängig und Wiederholen (Strg+Z/Y), IME-Eingabe. Mehrzeilig mit AcceptsReturn und Umbruch; dazu IsReadOnly, MaxLength und Validierung über die Bindung.


KXAML
TextBoxSeite.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.TextBoxVorfuehrung"
Spacing="8">
<StackPanel.Resources>
<!-- Das Datenmodell der Validierung; die Bindung unten hat es als Quelle. -->
<local:TextBoxPerson x:Key="Person" />
</StackPanel.Resources>
<TextBlock Text="Einzeilig" FontWeight="SemiBold" />
<!-- Einzeilig, mit Rückmeldung bei jeder Änderung (auch bei Rückgängig oder Einfügen). -->
<TextBox x:Name="Einzeilig" Text="Hier tippen" Width="320" HorizontalAlignment="Left" TextChanged="Einzeilig_TextChanged" />
<TextBlock x:Name="Zaehler" Text="11 Zeichen" />
<TextBlock Text="Mehrzeilig" FontWeight="SemiBold" />
<!-- Mehrzeilig mit Umbruch; Eingabe fügt Zeilen ein. Ein Zeilenumbruch im Attribut steht als da. -->
<TextBox Text="Mehrzeilig mit Umbruch. Die Eingabetaste fügt eine Zeile ein, Bild auf/ab blättert."
AcceptsReturn="True" TextWrapping="Wrap" Width="320" Height="96" HorizontalAlignment="Left" />
<TextBlock Text="Nur lesen, höchstens 5 Zeichen, deaktiviert" FontWeight="SemiBold" />
<TextBox Text="Nur lesen: auswählen und kopieren geht" IsReadOnly="True" Width="320" HorizontalAlignment="Left" />
<TextBox MaxLength="5" Width="120" HorizontalAlignment="Left" />
<TextBox Text="Deaktiviert" IsEnabled="False" Width="320" HorizontalAlignment="Left" />
<TextBlock Text="Validierung (Name darf nicht leer sein)" FontWeight="SemiBold" />
<!-- Validierung über die Bindung: ein leerer Name erreicht das Modell nicht, das Feld zeigt den Fehler. -->
<!-- Die Bindung vor TextChanged: der erste Text meldet sich so nicht, bevor „Fehler“ steht. -->
<TextBox x:Name="NameFeld" Width="320" HorizontalAlignment="Left"
Text="{Binding Name, Mode=TwoWay, Source={StaticResource Person}, Validierung=PruefeName}" TextChanged="Name_TextChanged" />
<TextBlock x:Name="Fehler" Text="Im Modell: Ada" />
</StackPanel>TextBoxSeite.kxaml.cs
using Kuonen.UI;
using Kuonen.UI.Bindung;
using Kuonen.UI.Ereignisse;
namespace Kuonen.UI.Galerie.Seiten;
public sealed partial class TextBoxSeite
{
protected override Func<UIElement>? KxamlFabrik => () => new TextBoxVorfuehrung();
}
/// <summary>Das Datenmodell der Validierung in der KXAML-Fassung.</summary>
public sealed class TextBoxPerson
{
public string Name { get; set; } = "Ada";
}
/// <summary>KXAML-Fassung der TextBox-Seite: der Baum samt Bindung steht in TextBoxSeite.kxaml, hier nur das Verhalten.</summary>
public sealed partial class TextBoxVorfuehrung
{
private readonly TextBoxPerson _person;
public TextBoxVorfuehrung()
{
InitializeComponent();
if (!Resources.TryGet(Schluessel.Person, out var person) || person is null)
throw new InvalidOperationException("Die Ressource \"Person\" fehlt in TextBoxSeite.kxaml.");
_person = person;
// Beide Auslöser sind nötig: Der Fehler ändert sich nur beim Wechsel zwischen gültig und ungültig, das Modell
// aber bei jedem angenommenen Text, ohne dass sich der Fehler ändert.
NameFeld.Abonniere(Validation.FehlerProperty, ZeigeStand);
}
private string? PruefeName(string text) => string.IsNullOrWhiteSpace(text) ? "Bitte einen Namen eingeben." : null;
private void ZeigeStand() => Fehler.Text = Validation.GetFehler(NameFeld) ?? "Im Modell: " + _person.Name;
private void Einzeilig_TextChanged(object sender, RoutedEventArgs e) => Zaehler.Text = $"{Einzeilig.Text.Length} Zeichen";
private void Name_TextChanged(object sender, RoutedEventArgs e) => ZeigeStand();
}C#
TextBoxSeite.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="TextBox"/>.</summary>
public sealed partial class TextBoxSeite : GalerieSeite
{
public override string Titel => "TextBox";
public override string Beschreibung =>
"Ein Eingabefeld: Caret, Auswahl mit Maus und Umschalt, Wortsprünge mit Strg, Zwischenablage (Strg+C/X/V), "
+ "Rückgängig und Wiederholen (Strg+Z/Y), IME-Eingabe. Mehrzeilig mit AcceptsReturn und Umbruch; dazu "
+ "IsReadOnly, MaxLength und Validierung über die Bindung.";
public override string Stichworte => "Eingabefeld Textfeld Eingabe tippen mehrzeilig AcceptsReturn MaxLength IsReadOnly Validierung Validation Zwischenablage Rückgängig IME";
public override string Datei => nameof(TextBoxSeite);
/// <summary>Das Datenmodell des Beispiels für die Validierung.</summary>
private sealed class Person
{
public string Name { get; set; } = "Ada";
}
private static readonly IBindingPath<Person, string> NamePfad = BindingPath.Stufe<Person, string>("Name", p => p.Name, (p, w) => p.Name = w);
public override UIElement Baue()
{
TextBlock Beschriftung(string text) => new() { Text = text, FontWeight = 600 };
// Einzeilig, mit Rückmeldung bei jeder Änderung.
var einzeilig = new TextBox { Text = "Hier tippen", Width = 320, HorizontalAlignment = HorizontalAlignment.Left };
var zaehler = new TextBlock { Text = "11 Zeichen" };
// Der Zähler stimmt nach jeder Änderung des Textes, auch bei Rückgängig oder Einfügen.
einzeilig.TextChanged += (_, _) => zaehler.Text = $"{einzeilig.Text.Length} Zeichen";
// Mehrzeilig mit Umbruch; Eingabe fügt Zeilen ein.
var mehrzeilig = new TextBox
{
Text = "Mehrzeilig mit Umbruch.\nDie Eingabetaste fügt eine Zeile ein, Bild auf/ab blättert.",
AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, Width = 320, Height = 96, HorizontalAlignment = HorizontalAlignment.Left,
};
// Nur lesen, höchstens 5 Zeichen, deaktiviert.
var nurLesen = new TextBox { Text = "Nur lesen: auswählen und kopieren geht", IsReadOnly = true, Width = 320, HorizontalAlignment = HorizontalAlignment.Left };
var kurz = new TextBox { MaxLength = 5, Width = 120, HorizontalAlignment = HorizontalAlignment.Left };
var aus = new TextBox { Text = "Deaktiviert", IsEnabled = false, Width = 320, HorizontalAlignment = HorizontalAlignment.Left };
// Validierung über die Bindung: ein leerer Name erreicht das Modell nicht, das Feld zeigt den Fehler.
var person = new Person();
var name = new TextBox { Width = 320, HorizontalAlignment = HorizontalAlignment.Left };
name.SetBinding(TextBox.TextProperty, new Binding<Person, string>(NamePfad)
{
Source = person, Mode = BindingMode.TwoWay,
Validierung = text => string.IsNullOrWhiteSpace(text) ? "Bitte einen Namen eingeben." : null,
});
var fehler = new TextBlock { Text = "Im Modell: Ada" };
// Beide Auslöser sind nötig: Der Fehler ändert sich nur beim Wechsel zwischen gültig und ungültig, das Modell
// aber bei jedem angenommenen Text, ohne dass sich der Fehler ändert.
void ZeigeStand() => fehler.Text = Validation.GetFehler(name) ?? "Im Modell: " + person.Name;
name.Abonniere(Validation.FehlerProperty, ZeigeStand);
name.TextChanged += (_, _) => ZeigeStand();
var seite = new StackPanel { Spacing = 8 };
seite.Children.Add(Beschriftung("Einzeilig"));
seite.Children.Add(einzeilig);
seite.Children.Add(zaehler);
seite.Children.Add(Beschriftung("Mehrzeilig"));
seite.Children.Add(mehrzeilig);
seite.Children.Add(Beschriftung("Nur lesen, höchstens 5 Zeichen, deaktiviert"));
seite.Children.Add(nurLesen);
seite.Children.Add(kurz);
seite.Children.Add(aus);
seite.Children.Add(Beschriftung("Validierung (Name darf nicht leer sein)"));
seite.Children.Add(name);
seite.Children.Add(fehler);
return seite;
}
}