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
@@ -28,7 +28,9 @@ public partial class ExamDialogViewModel : ObservableObject
[ObservableProperty] private int? _examNumber;
[ObservableProperty] private string _notes = "";
[ObservableProperty] private string _returnedAtText = "";
[ObservableProperty] private string _validationMessage = "";
[ObservableProperty] private string _titleError = "";
[ObservableProperty] private string _dateTextError = "";
[ObservableProperty] private string _returnedAtTextError = "";
[ObservableProperty] private double _totalPoints;
[ObservableProperty] private bool _hasCompetencyCatalog;
[ObservableProperty] private bool _useWeighting;
@@ -264,27 +266,34 @@ public partial class ExamDialogViewModel : ObservableObject
[RelayCommand]
private void Save()
{
if (string.IsNullOrWhiteSpace(Title)) { ValidationMessage = "Titel erforderlich."; return; }
if (!DateOnly.TryParseExact(DateText, "dd.MM.yyyy", null, DateTimeStyles.None, out var date))
TitleError = ""; DateTextError = ""; ReturnedAtTextError = ""; GradingKeyValidation = "";
var valid = true;
if (string.IsNullOrWhiteSpace(Title)) { TitleError = "Titel erforderlich."; valid = false; }
DateOnly date = default;
if (!DateOnly.TryParseExact(DateText, "dd.MM.yyyy", null, DateTimeStyles.None, out date))
{
ValidationMessage = "Datum im Format TT.MM.JJJJ eingeben.";
return;
DateTextError = "Format TT.MM.JJJJ.";
valid = false;
}
DateOnly? returnedAt = null;
if (!string.IsNullOrWhiteSpace(ReturnedAtText))
{
if (!DateOnly.TryParseExact(ReturnedAtText, "dd.MM.yyyy", null, DateTimeStyles.None, out var r))
{
ValidationMessage = "Rückgabedatum im Format TT.MM.JJJJ eingeben.";
return;
ReturnedAtTextError = "Format TT.MM.JJJJ.";
valid = false;
}
returnedAt = r;
else returnedAt = r;
}
var gradingKey = BuildGradingKeyEntries();
var gradingKeyError = _grading.ValidateGradingKey(gradingKey);
if (gradingKeyError is not null) { GradingKeyValidation = gradingKeyError; return; }
GradingKeyValidation = "";
if (gradingKeyError is not null) { GradingKeyValidation = gradingKeyError; valid = false; }
if (!valid) return;
Result = _editingExam ?? new Exam { GroupId = _groupId };
Result.Title = Title.Trim();