Fehlerbehandlung, Logging und feldbezogene Validierung (Kapitel 13.2)

Zentrale Exception-Behandlung (Dispatcher.UIThread.UnhandledException,
AppDomain, TaskScheduler) verhindert Abstürze und protokolliert Fehler
über AppLogger in eine rotierende Log-Datei im App-Datenverzeichnis.
Toast-Benachrichtigungen zeigen Erfolg/Fehler global an. Alle Dialoge
mit Formularfeldern zeigen Validierungsmeldungen jetzt direkt am
betroffenen Feld statt in einem Sammel-Label.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 12:38:30 +02:00
co-authored by Claude Sonnet 5
parent 273f459119
commit db1afff7e4
27 changed files with 578 additions and 102 deletions
@@ -23,6 +23,8 @@
<StackPanel Spacing="4">
<TextBlock Text="{Binding NameLabel}" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding Name}" PlaceholderText="{Binding NameHint}"/>
<TextBlock Text="{Binding NameError}" Foreground="Red" FontSize="11"
IsVisible="{Binding NameError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<!-- Fach AutoComplete aus bekannten Fächern; auch bei Klasse, da eine
@@ -41,6 +43,8 @@
<StackPanel Grid.Column="0" Spacing="4">
<TextBlock Text="Klassenstufe *" FontSize="12" Opacity="0.7"/>
<NumericUpDown Value="{Binding GradeLevel}" Minimum="1" Maximum="13" FormatString="0"/>
<TextBlock Text="{Binding GradeLevelError}" Foreground="Red" FontSize="11"
IsVisible="{Binding GradeLevelError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="4">
<TextBlock Text="Schuljahr" FontSize="12" Opacity="0.7"/>
@@ -70,8 +74,6 @@
<CheckBox Content="Niveaudifferenzierung (E/G/Förder)" IsChecked="{Binding IsDifferentiated}"
ToolTip.Tip="Blendet im Schüler-Tab eine Niveau-Zuordnung je Schüler ein und erlaubt in Klausuren die Zuordnung zu einem Niveau."/>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<Grid Grid.Row="1" ColumnDefinitions="*,8,*" Margin="0,20,0,0">
@@ -14,15 +14,14 @@
<StackPanel Spacing="4">
<TextBlock Text="Datum *" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ" x:Name="DateBox"/>
<TextBlock Text="{Binding DateTextError}" Foreground="Red" FontSize="11"
IsVisible="{Binding DateTextError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Kommentar (optional)" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding Comment}" PlaceholderText="z. B. Stunde 12 Säure-Base-Reaktion"/>
</StackPanel>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<Grid Grid.Row="1" ColumnDefinitions="*,8,*" Margin="0,20,0,0">
@@ -15,14 +15,16 @@
<Grid ColumnDefinitions="120,*,90" ColumnSpacing="6">
<ComboBox Grid.Column="0" ItemsSource="{x:Static vm:GradeCategoryDisplay.Options}"
SelectedItem="{Binding CategoryName}"/>
<TextBox Grid.Column="1" Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<StackPanel Grid.Column="1" Spacing="2">
<TextBox Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<TextBlock Text="{Binding DateTextError}" Foreground="Red" FontSize="11"
IsVisible="{Binding DateTextError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<NumericUpDown Grid.Column="2" Value="{Binding Weight}" Minimum="0" Maximum="10"
Increment="0.1" FormatString="0.#" ShowButtonSpinner="False"
ToolTip.Tip="Gewichtung"/>
</Grid>
<TextBox Text="{Binding Note}" PlaceholderText="Notiz (z.B. Hausaufgabenkontrolle)"/>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Text="Werte pro Schüler unten eintragen. Leere Felder werden nicht gespeichert."
FontSize="11" Opacity="0.5" TextWrapping="Wrap"/>
</StackPanel>
@@ -19,6 +19,8 @@
<StackPanel Spacing="4">
<TextBlock Text="Titel *" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding Title}" PlaceholderText="z.B. 1. Klausur Kinetik"/>
<TextBlock Text="{Binding TitleError}" Foreground="Red" FontSize="11"
IsVisible="{Binding TitleError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Spacing="4" IsVisible="{Binding IsDifferentiated}">
@@ -32,6 +34,8 @@
<StackPanel Grid.Column="0" Spacing="4">
<TextBlock Text="Datum *" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<TextBlock Text="{Binding DateTextError}" Foreground="Red" FontSize="11"
IsVisible="{Binding DateTextError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="4">
<TextBlock Text="Klausurnummer" FontSize="12" Opacity="0.7"/>
@@ -41,6 +45,8 @@
<StackPanel Grid.Column="4" Spacing="4">
<TextBlock Text="Rückgabedatum" FontSize="12" Opacity="0.7"/>
<TextBox Text="{Binding ReturnedAtText}" PlaceholderText="TT.MM.JJJJ"/>
<TextBlock Text="{Binding ReturnedAtTextError}" Foreground="Red" FontSize="11"
IsVisible="{Binding ReturnedAtTextError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Grid>
@@ -118,9 +124,6 @@
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="12"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Separator Margin="0,4"/>
<Grid ColumnDefinitions="*,Auto">
@@ -128,11 +128,17 @@
<StackPanel Spacing="6">
<TextBlock Text="Abschnitt abschließen" FontSize="12" FontWeight="SemiBold"/>
<Grid ColumnDefinitions="*,100" ColumnSpacing="6">
<TextBox Grid.Column="0" Text="{Binding NewSectionLabel}" PlaceholderText="Bezeichnung"/>
<TextBox Grid.Column="1" Text="{Binding NewSectionEndDateText}" PlaceholderText="TT.MM.JJJJ"/>
<StackPanel Grid.Column="0" Spacing="2">
<TextBox Text="{Binding NewSectionLabel}" PlaceholderText="Bezeichnung"/>
<TextBlock Text="{Binding NewSectionLabelError}" Foreground="Red" FontSize="11"
IsVisible="{Binding NewSectionLabelError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Grid.Column="1" Spacing="2">
<TextBox Text="{Binding NewSectionEndDateText}" PlaceholderText="TT.MM.JJJJ"/>
<TextBlock Text="{Binding NewSectionEndDateTextError}" Foreground="Red" FontSize="11"
IsVisible="{Binding NewSectionEndDateTextError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Grid>
<TextBlock Text="{Binding SectionValidationMessage}" Foreground="Red" FontSize="11"
IsVisible="{Binding SectionValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="Abschnitt für alle Schüler abschließen" Command="{Binding CloseSectionCommand}"
HorizontalAlignment="Stretch"/>
</StackPanel>
@@ -24,15 +24,21 @@
<Grid ColumnDefinitions="120,*,90,90" ColumnSpacing="6">
<ComboBox Grid.Column="0" ItemsSource="{x:Static vm:GradeCategoryDisplay.Options}"
SelectedItem="{Binding CategoryName}"/>
<TextBox Grid.Column="1" Text="{Binding Value}" PlaceholderText="Wert (z.B. 2 oder 11)"/>
<TextBox Grid.Column="2" Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<StackPanel Grid.Column="1" Spacing="2">
<TextBox Text="{Binding Value}" PlaceholderText="Wert (z.B. 2 oder 11)"/>
<TextBlock Text="{Binding ValueError}" Foreground="Red" FontSize="11"
IsVisible="{Binding ValueError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<StackPanel Grid.Column="2" Spacing="2">
<TextBox Text="{Binding DateText}" PlaceholderText="TT.MM.JJJJ"/>
<TextBlock Text="{Binding DateTextError}" Foreground="Red" FontSize="11"
IsVisible="{Binding DateTextError, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
<NumericUpDown Grid.Column="3" Value="{Binding Weight}" Minimum="0" Maximum="10"
Increment="0.1" FormatString="0.#" ShowButtonSpinner="False"
ToolTip.Tip="Gewichtung"/>
</Grid>
<TextBox Text="{Binding Note}" PlaceholderText="Notiz (optional)" FontSize="12"/>
<TextBlock Text="{Binding ValidationMessage}" Foreground="Red" FontSize="11"
IsVisible="{Binding ValidationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBlock Grid.Column="0" Text="{Binding CreatedAtDisplay}" FontSize="11" Opacity="0.5"
VerticalAlignment="Center"/>