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,7 +23,7 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty] private string _newName = "";
[ObservableProperty] private string _newShort = "";
[ObservableProperty] private string _validationMessage = "";
[ObservableProperty] private string _newNameError = "";
public ObservableCollection<SubjectListItem> Subjects { get; } = [];
@@ -41,7 +41,7 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty] private string _newTemplateName = "";
[ObservableProperty] private string _newTemplateGradingSystemName = "Noten 16";
[ObservableProperty] private string _templateValidationMessage = "";
[ObservableProperty] private string _newTemplateNameError = "";
public List<string> GradingSystemOptions { get; } = ["Noten 16", "Punkte 015"];
public ObservableCollection<GradingKeyTemplateEditItem> GradingKeyTemplateList { get; } = [];
@@ -93,7 +93,7 @@ public partial class SettingsViewModel : ObservableObject
[RelayCommand]
private void AddGradingKeyTemplate()
{
if (string.IsNullOrWhiteSpace(NewTemplateName)) { TemplateValidationMessage = "Vorlagenname erforderlich."; return; }
if (string.IsNullOrWhiteSpace(NewTemplateName)) { NewTemplateNameError = "Vorlagenname erforderlich."; return; }
var system = NewTemplateGradingSystemName == "Punkte 015"
? GradingSystem.Points0To15 : GradingSystem.Grades1To6;
@@ -109,7 +109,7 @@ public partial class SettingsViewModel : ObservableObject
_gradingKeyTemplates.Save(template);
GradingKeyTemplateList.Add(new GradingKeyTemplateEditItem(template, _gradingKeyTemplates, _grading));
NewTemplateName = "";
TemplateValidationMessage = "";
NewTemplateNameError = "";
}
[RelayCommand]
@@ -132,17 +132,17 @@ public partial class SettingsViewModel : ObservableObject
[RelayCommand]
private void AddSubject()
{
if (string.IsNullOrWhiteSpace(NewName)) { ValidationMessage = "Name erforderlich."; return; }
if (string.IsNullOrWhiteSpace(NewName)) { NewNameError = "Name erforderlich."; return; }
try
{
_subjects.Save(new Subject { Name = NewName.Trim(), ShortName = NewShort.Trim() });
}
catch (InvalidOperationException ex)
{
ValidationMessage = ex.Message;
NewNameError = ex.Message;
return;
}
NewName = ""; NewShort = ""; ValidationMessage = "";
NewName = ""; NewShort = ""; NewNameError = "";
LoadSubjects();
}
@@ -156,10 +156,10 @@ public partial class SettingsViewModel : ObservableObject
}
catch (InvalidOperationException ex)
{
ValidationMessage = ex.Message;
NewNameError = ex.Message;
return;
}
ValidationMessage = "";
NewNameError = "";
if (CatalogSubject?.Id == item.Id) CatalogSubject = null;
LoadSubjects();
}
@@ -362,7 +362,8 @@ public partial class GradingKeyTemplateEditItem : ObservableObject
[ObservableProperty] private string _newGrade = "";
[ObservableProperty] private double _newMinPercent;
[ObservableProperty] private string _validation = "";
[ObservableProperty] private string _newGradeError = "";
[ObservableProperty] private string _newMinPercentError = "";
[ObservableProperty] private string _completenessWarning = "";
public ObservableCollection<GradingKeyEntryVm> Entries { get; } = [];
@@ -384,10 +385,15 @@ public partial class GradingKeyTemplateEditItem : ObservableObject
[RelayCommand]
private void AddEntry()
{
if (string.IsNullOrWhiteSpace(NewGrade)) { Validation = "Bezeichnung erforderlich."; return; }
if (NewMinPercent is < 0 or > 100) { Validation = "Prozentgrenze muss zwischen 0 und 100 liegen."; return; }
if (_template.Entries.Any(e => Math.Abs(e.MinPercent - NewMinPercent) < 0.0001))
{ Validation = "Diese Prozentgrenze existiert bereits."; return; }
NewGradeError = ""; NewMinPercentError = "";
var valid = true;
if (string.IsNullOrWhiteSpace(NewGrade)) { NewGradeError = "Bezeichnung erforderlich."; valid = false; }
if (NewMinPercent is < 0 or > 100) { NewMinPercentError = "Muss zwischen 0 und 100 liegen."; valid = false; }
else if (_template.Entries.Any(e => Math.Abs(e.MinPercent - NewMinPercent) < 0.0001))
{ NewMinPercentError = "Diese Prozentgrenze existiert bereits."; valid = false; }
if (!valid) return;
var entry = new GradingKeyEntry { Grade = NewGrade.Trim(), MinPercent = NewMinPercent };
_template.Entries.Add(entry);
@@ -396,7 +402,7 @@ public partial class GradingKeyTemplateEditItem : ObservableObject
Entries.Clear();
foreach (var e in _template.Entries) Entries.Add(new GradingKeyEntryVm(e, DeleteEntry));
NewGrade = ""; NewMinPercent = 0; Validation = "";
NewGrade = ""; NewMinPercent = 0;
RecomputeCompleteness();
}