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
@@ -361,7 +361,9 @@ public partial class AddStudentDialogViewModel : ObservableObject
[ObservableProperty] private string _dateOfBirthText = "";
[ObservableProperty] private string _selectedGender = "";
[ObservableProperty] private string _notes = "";
[ObservableProperty] private string _validationMessage = "";
[ObservableProperty] private string _lastNameError = "";
[ObservableProperty] private string _firstNameError = "";
[ObservableProperty] private string _dateOfBirthError = "";
public List<string> GenderOptions { get; } = ["", "M männlich", "W weiblich", "D divers"];
public List<string> RelationPresets { get; } = ["Schüler/in", "Mutter", "Vater", "Elternteil", "Erziehungsberechtigte/r", "Sonstige"];
@@ -380,8 +382,11 @@ public partial class AddStudentDialogViewModel : ObservableObject
[RelayCommand]
private void Save()
{
if (string.IsNullOrWhiteSpace(LastName)) { ValidationMessage = "Nachname erforderlich."; return; }
if (string.IsNullOrWhiteSpace(FirstName)) { ValidationMessage = "Vorname erforderlich."; return; }
LastNameError = ""; FirstNameError = ""; DateOfBirthError = "";
var valid = true;
if (string.IsNullOrWhiteSpace(LastName)) { LastNameError = "Nachname erforderlich."; valid = false; }
if (string.IsNullOrWhiteSpace(FirstName)) { FirstNameError = "Vorname erforderlich."; valid = false; }
DateOnly? dob = null;
if (!string.IsNullOrWhiteSpace(DateOfBirthText))
@@ -389,11 +394,13 @@ public partial class AddStudentDialogViewModel : ObservableObject
if (!DateOnly.TryParseExact(DateOfBirthText, "dd.MM.yyyy", null,
System.Globalization.DateTimeStyles.None, out var d))
{
ValidationMessage = "Geburtsdatum im Format TT.MM.JJJJ."; return;
DateOfBirthError = "Format TT.MM.JJJJ."; valid = false;
}
dob = d;
else dob = d;
}
if (!valid) return;
Result = new Student
{
FirstName = FirstName.Trim(),
@@ -456,7 +463,8 @@ public partial class ContactEditDialogViewModel : ObservableObject
[ObservableProperty] private string _invalidSinceText = "";
[ObservableProperty] private string _selectedInvalidReason = "";
[ObservableProperty] private string _invalidReasonDetails = "";
[ObservableProperty] private string _validationMessage = "";
[ObservableProperty] private string _nameError = "";
[ObservableProperty] private string _invalidSinceError = "";
public string DialogTitle => _source is null ? "Kontakt anlegen" : "Kontakt bearbeiten";
public List<string> RelationPresets { get; } =
@@ -494,10 +502,13 @@ public partial class ContactEditDialogViewModel : ObservableObject
[RelayCommand]
private void Save()
{
NameError = ""; InvalidSinceError = "";
var valid = true;
if (string.IsNullOrWhiteSpace(Name))
{
ValidationMessage = "Name erforderlich.";
return;
NameError = "Name erforderlich.";
valid = false;
}
DateOnly? invalidSince = null;
@@ -506,12 +517,14 @@ public partial class ContactEditDialogViewModel : ObservableObject
if (!DateOnly.TryParseExact(InvalidSinceText, "dd.MM.yyyy", null,
System.Globalization.DateTimeStyles.None, out var parsedDate))
{
ValidationMessage = "Ungültig seit im Format TT.MM.JJJJ angeben.";
return;
InvalidSinceError = "Format TT.MM.JJJJ.";
valid = false;
}
invalidSince = parsedDate;
else invalidSince = parsedDate;
}
if (!valid) return;
Result = new Contact
{
Id = _source?.Id ?? Guid.NewGuid(),