diff --git a/Project_Periodensystem.Controller/ElementStatistics.cs b/Project_Periodensystem.Controller/ElementStatistics.cs new file mode 100644 index 0000000..c7d6d02 --- /dev/null +++ b/Project_Periodensystem.Controller/ElementStatistics.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Project_Periodensystem.Model; + +namespace Project_Periodensystem.Controller +{ + /// + /// ElementStatistics - Spezialisierte Klasse für statistische Berechnungen und Datenanalyse + /// + /// Diese Klasse wurde aus dem Haupt-Controller ausgelagert, um das Single Responsibility + /// Principle zu befolgen. Sie konzentriert sich ausschließlich auf: + /// + /// - Mathematische Berechnungen (Durchschnitte, Extreme) + /// - Datengruppierung und -analyse (Serien, Kategorien) + /// - Informationsextraktion (Tuple-basierte Abfragen) + /// - Performance-optimierte LINQ-Operationen + /// + /// Vorteile der Auslagerung: + /// - Bessere Testbarkeit einzelner Berechnungen + /// - Wiederverwendbarkeit der Statistik-Funktionen + /// - Klare Trennung von UI-Logik und Datenverarbeitung + /// - Einfache Erweiterung um neue Statistiken + /// + public class ElementStatistics + { + /// + /// Private, schreibgeschützte Liste aller Elemente für statistische Berechnungen. + /// Wird im Konstruktor gesetzt und bleibt danach unveränderlich (readonly). + /// Garantiert Datenintegrität während der gesamten Lebensdauer des Objekts. + /// + private readonly List _elements; + + /// + /// Konstruktor der ElementStatistics-Klasse. + /// Initialisiert die Statistik-Engine mit einer Element-Sammlung. + /// + /// Design-Prinzipien: + /// - Dependency Injection: Element-Liste wird von außen bereitgestellt + /// - Defensive Programmierung: Null-Safety durch Fallback + /// - Unveränderlichkeit: readonly field für Datensicherheit + /// + /// Liste der Elemente für statistische Auswertungen (kann null sein) + public ElementStatistics(List elements) + { + // Null-Safety: Falls null übergeben wird, erstelle eine leere Liste + // Verhindert NullReferenceExceptions in allen nachfolgenden Methoden + _elements = elements ?? new List(); + } + + public double[] GetAtomicWeights() + { + double[] weights = new double[_elements.Count]; + for (int i = 0; i < _elements.Count; i++) + { + weights[i] = _elements[i].AtomicWeight; + } + return weights; + } + + public double GetAverageAtomicWeight() + { + if (!_elements.Any()) return 0.0; + + double[] weights = GetAtomicWeights(); + double sum = 0; + for (int i = 0; i < weights.Length; i++) + { + sum += weights[i]; + } + return sum / weights.Length; + } + + public List GetAllSeries() + { + return _elements.Select(e => e.Series) + .Distinct() + .Where(s => !string.IsNullOrWhiteSpace(s)) + .OrderBy(s => s) + .ToList(); + } + + public (int totalElements, int uniqueSeries, double avgWeight) GetStatistics() + { + var seriesCount = GetAllSeries().Count; + var avgWeight = GetAverageAtomicWeight(); + + return (_elements.Count, seriesCount, avgWeight); + } + + public (Element? lightest, Element? heaviest) GetWeightExtremes() + { + if (!_elements.Any()) return (null, null); + + var lightest = _elements.OrderBy(e => e.AtomicWeight).First(); + var heaviest = _elements.OrderByDescending(e => e.AtomicWeight).First(); + + return (lightest, heaviest); + } + + public (string symbol, string name, double weight) GetElementInfo(int atomicNumber) + { + var element = _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber); + return element != null + ? (element.Symbol, element.ElementName, element.AtomicWeight) + : ("?", "Unknown", 0.0); + } + + public (int row, int column) GetElementPosition(int atomicNumber) + { + var element = _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber); + return element != null ? (element.Row, element.Column) : (-1, -1); + } + } +} diff --git a/Project_Periodensystem.Controller/ElementValidator.cs b/Project_Periodensystem.Controller/ElementValidator.cs new file mode 100644 index 0000000..f0de7a8 --- /dev/null +++ b/Project_Periodensystem.Controller/ElementValidator.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Project_Periodensystem.Model; + +namespace Project_Periodensystem.Controller +{ + /// + /// ElementValidator - Spezialisierte Klasse für Datenvalidierung + /// + /// Diese Klasse wurde aus dem PeriodensystemController ausgelagert, um das + /// Single Responsibility Principle (SRP) zu befolgen. Sie ist ausschließlich + /// für die Validierung von Element-Daten zuständig. + /// + /// Vorteile der Auslagerung: + /// - Bessere Testbarkeit (kann isoliert getestet werden) + /// - Klarere Verantwortlichkeiten + /// - Wiederverwendbarkeit in anderen Controllern + /// - Einfachere Wartung und Erweiterung + /// + public class ElementValidator + { + // Private readonly Liste der zu validierenden Elemente + // readonly = kann nach Initialisierung nicht mehr geändert werden + private readonly List _elements; + + /// + /// Konstruktor - Initialisiert den Validator mit einer Element-Liste + /// + /// Liste der zu validierenden Elemente + public ElementValidator(List elements) + { + // Null-Check mit Fallback zu leerer Liste - verhindert NullReferenceExceptions + _elements = elements ?? new List(); + } + + /// + /// Führt eine umfassende Validierung der Element-Daten durch. + /// + /// Diese Methode prüft verschiedene Datenintegritäts-Aspekte: + /// 1. Existenz von Elementen (leere Liste = Problem) + /// 2. Eindeutigkeit der Atomnummern (jede darf nur einmal vorkommen) + /// 3. Vollständigkeit der Element-Symbole (keine leeren/null Werte) + /// + /// Verwendet moderne C# LINQ-Syntax für effiziente Datenabfragen: + /// - GroupBy() für Gruppierung nach Atomnummer + /// - Where() für Filterung + /// - Any() für Existenzprüfung + /// - Count() für Anzahl-Ermittlung + /// + /// true = alle Validierungen bestanden, false = Fehler gefunden + public bool ValidateData() + { + // Erste Prüfung: Sind überhaupt Elemente vorhanden? + // Any() ist effizienter als Count() > 0, da es beim ersten Element stoppt + if (!_elements.Any()) + { + Logger.Log("Keine Elemente zum Validieren"); + return false; + } + + // Zweite Prüfung: Doppelte Atomnummern aufspüren + // LINQ-Chain: GroupBy sammelt Elemente mit gleicher Atomnummer + // Where filtert Gruppen mit mehr als einem Element (= Duplikate) + // Select extrahiert nur die Atomnummer (Key) für Logging + var duplicateNumbers = _elements.GroupBy(e => e.AtomicNumber) + .Where(g => g.Count() > 1) + .Select(g => g.Key); + + if (duplicateNumbers.Any()) + { + // string.Join() erstellt eine kommaseparierte Liste der doppelten Nummern + Logger.Log($"Doppelte Atomnummern gefunden: {string.Join(", ", duplicateNumbers)}"); + return false; + } + + // Dritte Prüfung: Leere oder ungültige Element-Symbole finden + // string.IsNullOrWhiteSpace() prüft auf null, leer oder nur Leerzeichen + var emptySymbols = _elements.Where(e => string.IsNullOrWhiteSpace(e.Symbol)); + if (emptySymbols.Any()) + { + Logger.Log($"Elemente mit leeren Symbolen gefunden: {emptySymbols.Count()}"); + return false; + } + + // Alle Validierungen bestanden + Logger.Log("Datenvalidierung erfolgreich"); + return true; + } + + /// + /// Validiert die Position eines Elements im Periodensystem. + /// + /// Das Periodensystem hat feste Dimensionen: + /// - Zeilen (Perioden): 0-9 (insgesamt 10 Perioden) + /// - Spalten (Gruppen): 0-17 (insgesamt 18 Gruppen) + /// + /// Diese Methode stellt sicher, dass Elemente nur an gültigen Positionen + /// platziert werden. Ungültige Positionen würden zu Layout-Fehlern oder + /// Abstürzen in der Benutzeroberfläche führen. + /// + /// Verwendet nullable Reference Types (Element?) - moderne C# 8.0+ Syntax + /// + /// Das zu prüfende Element (kann null sein) + /// true = Position ist gültig, false = Position ungültig oder Element ist null + public bool ValidateElementPosition(Element? element) + { + // Null-Check: Defensive Programmierung verhindert NullReferenceExceptions + if (element == null) + { + Logger.Log("Element ist null"); + return false; + } + + // Positionsgrenzen prüfen: Standard-Periodensystem Layout + // Row: 0-9 (Periode 1-10, aber 0-basiert indiziert) + // Column: 0-17 (Gruppe 1-18, aber 0-basiert indiziert) + if (element.Row < 0 || element.Row > 9 || element.Column < 0 || element.Column > 17) + { + Logger.Log($"Ungültige Position für {element.Symbol}: Row={element.Row}, Column={element.Column}"); + return false; + } + + return true; + } + + /// + /// Führt eine vollständige Validierungsprüfung aller Elemente durch und protokolliert Fehler. + /// + /// Diese Utility-Methode iteriert durch alle Elemente und prüft jedes einzeln. + /// Hauptzweck ist das Debugging und die Qualitätssicherung: + /// - Identifiziert problematische Elemente + /// - Protokolliert detaillierte Fehlermeldungen + /// - Hilfreich bei der Datenbereinigung + /// + /// Besonderheiten: + /// - Verwendet null-conditional operator (?.) für sichere Navigation + /// - Null-coalescing operator (??) für Fallback-Werte + /// - Moderne C# Syntax für robuste Fehlerbehandlung + /// + public void LogValidationInfo() + { + // Iteriere durch alle Elemente und validiere jedes einzeln + foreach (var element in _elements) + { + // Prüfe Position für jedes Element + if (!ValidateElementPosition(element)) + { + // element?.Symbol verwendet null-conditional operator + // Falls element null ist, wird Symbol nicht abgerufen -> kein Crash + // ?? "NULL" ist ein Fallback falls Symbol null/leer ist + Logger.Log($"VALIDIERUNGSFEHLER: {element?.Symbol ?? "NULL"}"); + } + } + } + } +} diff --git a/Project_Periodensystem.Controller/INavigationService.cs b/Project_Periodensystem.Controller/INavigationService.cs index 684471a..2116b2a 100644 --- a/Project_Periodensystem.Controller/INavigationService.cs +++ b/Project_Periodensystem.Controller/INavigationService.cs @@ -3,15 +3,86 @@ using System; namespace Project_Periodensystem.Controller { /// - /// Interface für Navigation - trennt Controller von UI-Dependencies - /// Controller definiert WAS navigiert wird, View definiert WIE + /// Navigation Service Interface - Implementiert Dependency Inversion Principle + /// + /// ZWECK UND ARCHITEKTUR: + /// - Entkoppelt Controller-Layer von UI-spezifischen Navigation-Details + /// - Ermöglicht verschiedene UI-Implementierungen (WPF, Avalonia, Console, etc.) + /// - Macht Controller-Layer testbar durch Mock-Implementierungen + /// - Implementiert Interface Segregation Principle (nur notwendige Navigation-Methoden) + /// + /// DESIGN PATTERNS: + /// - Dependency Inversion: Controller abhängig von Abstraktion, nicht von Implementierung + /// - Strategy Pattern: Verschiedene Navigation-Strategien möglich + /// - Command Pattern: Jede Methode kapselt eine UI-Aktion + /// + /// C# KONZEPTE: + /// - Interface Definition: Vertrag ohne Implementierung + /// - Abstraktion: Trennt WHAT (Navigation) von HOW (UI-Framework) + /// - Dependency Injection: Controller erhält Implementation via Constructor + /// + /// IMPLEMENTIERUNG: + /// - NavigationService.cs (View-Layer) implementiert dieses Interface + /// - Controller ruft Interface-Methoden auf, kennt aber Implementierung nicht + /// - Ermöglicht einfaches Testen und verschiedene UI-Frameworks + /// + /// TESTBARKEIT: + /// - Mock-Implementierung für Unit Tests möglich + /// - Keine direkten UI-Dependencies im Controller + /// - Isolierte Testung der Navigation-Logik /// public interface INavigationService { + /// + /// Navigiert zur Hauptansicht des Periodensystems + /// + /// IMPLEMENTIERUNG: + /// - Zeigt das vollständige, interaktive Periodensystem an + /// - Hauptfunktion der Anwendung + /// - Alle Element-Tiles werden geladen und angezeigt + /// void NavigateToPeriodicTable(); + + /// + /// Navigiert zur About/Info-Seite der Anwendung + /// + /// IMPLEMENTIERUNG: + /// - Zeigt Anwendungsinformationen, Version, Credits + /// - Dokumentation und Hilfe-Inhalte + /// - Links zu weiteren Ressourcen + /// void NavigateToAbout(); + + /// + /// Navigiert zurück zur Startseite/Hauptmenü + /// + /// IMPLEMENTIERUNG: + /// - Zeigt das Hauptmenü mit Navigation-Optionen + /// - Reset-Funktion zum Anwendungsstart + /// - Home-Button Funktionalität + /// void NavigateToLanding(); + + /// + /// Wechselt zwischen hellen und dunklen UI-Themes + /// + /// IMPLEMENTIERUNG: + /// - Toggle zwischen Light/Dark Mode + /// - Aktualisiert globale Theme-Settings + /// - Persistiert Benutzer-Präferenz + /// - Wendet neues Theme auf alle UI-Komponenten an + /// void ToggleTheme(); + + /// + /// Zeigt Bestätigungsdialog für Datenexport + /// + /// IMPLEMENTIERUNG: + /// - Modal-Dialog mit Export-Optionen + /// - Benutzer wählt Format und Ziel + /// - Startet Export-Prozess nach Bestätigung + /// - Fortschrittsanzeige und Erfolgs-/Fehlermeldungen + /// void ShowExportConfirmation(); } } diff --git a/Project_Periodensystem.Controller/PeriodensystemController.cs b/Project_Periodensystem.Controller/PeriodensystemController.cs index 24b2005..e716743 100644 --- a/Project_Periodensystem.Controller/PeriodensystemController.cs +++ b/Project_Periodensystem.Controller/PeriodensystemController.cs @@ -7,48 +7,131 @@ using Project_Periodensystem.Persistence; namespace Project_Periodensystem.Controller { /// - /// Controller für das Periodensystem - verwaltet die Geschäftslogik - /// und trennt View von Model gemäß MVC-Pattern - /// SAUBERE ARCHITEKTUR: Verwendet Interface für Navigation + /// Haupt-Controller für das Periodensystem - implementiert das MVC-Pattern + /// + /// ZWECK UND ARCHITEKTUR: + /// - Zentrale Kontrollschicht zwischen View (UI) und Model/Persistence (Daten) + /// - Koordiniert alle Geschäftslogik-Operationen für das Periodensystem + /// - Delegiert komplexe Aufgaben an spezialisierte Helper-Klassen (Single Responsibility Principle) + /// - Bietet eine einheitliche API für alle UI-Komponenten + /// + /// DESIGN PATTERN: + /// - MVC (Model-View-Controller): Trennt Präsentation von Geschäftslogik + /// - Delegation Pattern: Überträgt Verantwortlichkeiten an spezialisierte Klassen + /// - Dependency Injection: Nimmt INavigationService als abhängige Komponente entgegen + /// + /// C# KONZEPTE: + /// - Nullable Reference Types (INavigationService?, ElementValidator?) + /// - LINQ für Datenoperationen (FirstOrDefault, Where, ToArray) + /// - Exception Handling mit try-catch-Blöcken + /// - Lambda Expressions für Delegation an Helper-Klassen + /// - Tuple-Rückgabewerte für strukturierte Daten /// public class PeriodensystemController { - // Nullable Field um Warning zu vermeiden - private List _elements = new List(); - - // Navigation Service Interface (KEINE direkte UI-Dependency!) - private readonly INavigationService? _navigationService; + // ===== PRIVATE FELDER ===== /// - /// Konstruktor - optional mit Navigation Service + /// Zentrale Liste aller chemischen Elemente + /// List<T> ermöglicht dynamisches Hinzufügen/Entfernen von Elementen + /// Private Field mit Underscore-Notation (_elements) nach C#-Konvention + /// + private List _elements = new List(); + + /// + /// Navigation Service für Seitenwechsel in der Avalonia-Anwendung + /// Readonly: Kann nur im Konstruktor zugewiesen werden (Immutability) + /// Nullable (?): Kann null sein, da Navigation optional ist + /// Interface-basiert: Ermöglicht verschiedene Implementierungen (Dependency Inversion) + /// + private readonly INavigationService? _navigationService; + + // ===== HELPER-KLASSEN FÜR CODE-ORGANISATION ===== + + /// + /// Spezialisierte Klasse für Validierungslogik + /// Nullable: Wird erst nach dem Laden der Elemente initialisiert + /// Separation of Concerns: Validierung ist getrennt von anderen Aufgaben + /// + private ElementValidator? _validator; + + /// + /// Spezialisierte Klasse für statistische Berechnungen + /// Nullable: Wird erst nach dem Laden der Elemente initialisiert + /// Single Responsibility: Nur für Statistiken zuständig + /// + private ElementStatistics? _statistics; + + // ===== KONSTRUKTOR ===== + + /// + /// Konstruktor mit optionaler Dependency Injection + /// + /// PARAMETER: + /// - navigationService: Optional (null by default), ermöglicht Navigation zwischen Seiten + /// + /// C# FEATURES: + /// - Default Parameter (= null): Macht den Parameter optional + /// - Nullable Reference Types: navigationService kann null sein + /// - Constructor Chaining: Ruft LoadElements() zur Initialisierung auf + /// + /// ABLAUF: + /// 1. Navigation Service speichern (kann null sein) + /// 2. Elemente aus Persistence-Layer laden + /// 3. Helper-Klassen mit geladenen Daten initialisieren /// public PeriodensystemController(INavigationService? navigationService = null) { _navigationService = navigationService; - LoadElements(); + LoadElements(); // Daten sofort beim Erstellen laden } + // ===== PRIVATE INITIALISIERUNGSMETHODEN ===== + /// - /// Lädt alle Elemente über den Persistence-Layer (JETZT MIT DATAMANAGER!) + /// Lädt alle chemischen Elemente aus dem Persistence-Layer + /// + /// ZWECK: + /// - Zentrale Initialisierung aller Elementdaten + /// - Fehlerbehandlung beim Laden der Daten + /// - Initialisierung der Helper-Klassen nach erfolgreichem Laden + /// - Ausführliches Logging für Debugging und Monitoring + /// + /// C# KONZEPTE: + /// - Exception Handling: try-catch für robuste Fehlerbehandlung + /// - LINQ: Take(10) für die ersten 10 Elemente + /// - Tuple Deconstruction: (totalElements, uniqueSeries, avgWeight) = GetStatistics() + /// - Null-Conditional Operator: lightest?.Symbol verhindert NullReferenceException + /// - String Interpolation: $"Text {variable}" für lesbare Ausgaben + /// + /// FEHLERBEHANDLUNG: + /// - Bei Exceptions wird eine leere Liste initialisiert (Graceful Degradation) + /// - Alle Fehler werden geloggt für spätere Analyse /// private void LoadElements() { try { - // NEUE METHODE: DataManager statt direkt PeriodicTableData + // Elemente aus DataManager laden (Persistence Layer) _elements = DataManager.LoadElements(); Logger.Log($"Controller: {_elements.Count} Elemente erfolgreich geladen (via DataManager)"); - // NEUE FEATURES demonstrieren: - // Array-Operationen testen + // Helper-Klassen mit den geladenen Daten initialisieren + // Delegation Pattern: Spezialisierte Klassen für verschiedene Aufgaben + _validator = new ElementValidator(_elements); + _statistics = new ElementStatistics(_elements); + + // ===== DEMONSTRATIVE LOGGING VON C#-FEATURES ===== + + // Arrays: Alle Element-Symbole als Array var symbols = GetAllSymbols(); Logger.LogArray("Element-Symbole (erste 10)", symbols.Take(10).ToArray()); - // Tuple-Operationen testen + // Tuples: Strukturierte Rückgabe mehrerer Werte var (totalElements, uniqueSeries, avgWeight) = GetStatistics(); Logger.LogTuple("Statistiken", $"Elemente: {totalElements}, Serien: {uniqueSeries}, Ø-Gewicht: {avgWeight:F2}"); - // Extremwerte finden + // Tuples mit Objekten: Extremwerte finden var (lightest, heaviest) = GetWeightExtremes(); if (lightest != null && heaviest != null) { @@ -58,304 +141,352 @@ namespace Project_Periodensystem.Controller } catch (Exception ex) { + // Graceful Degradation: Bei Fehlern eine leere Liste verwenden Logger.Log($"EXCEPTION in LoadElements: {ex.Message}"); _elements = new List(); + + // Helper-Klassen können nicht initialisiert werden (bleiben null) + _validator = null; + _statistics = null; } } + // ===== PUBLIC API METHODEN - ELEMENTZUGRIFF ===== + /// - /// Gibt alle verfügbaren Elemente zurück + /// Gibt alle Elemente als List<Element> zurück + /// + /// RÜCKGABETYP: List<Element> + /// - Ermöglicht Add/Remove-Operationen + /// - LINQ-Operationen verfügbar + /// - Dynamische Größe + /// + /// VERWENDUNG: + /// - Hauptsächlich für UI-Bindungen (WPF/Avalonia DataBinding) + /// - Für LINQ-Abfragen in der View-Schicht /// - /// Liste aller Elemente public List GetAllElements() { return _elements; } /// - /// Gibt Elemente als Array zurück (für Array-Operationen) + /// Gibt alle Elemente als Array zurück + /// + /// RÜCKGABETYP: Element[] + /// - Feste Größe (Read-Only nach Erstellung) + /// - Bessere Performance für Iterationen + /// - Weniger Speicher-Overhead als List + /// + /// C# KONZEPT: LINQ ToArray() + /// - Konvertiert IEnumerable zu Array + /// - Erstellt eine Kopie der Daten + /// + /// VERWENDUNG: + /// - Für Algorithmen, die feste Arrays benötigen + /// - Wenn Unveränderlichkeit wichtig ist /// - /// Array aller Elemente public Element[] GetAllElementsAsArray() { return _elements.ToArray(); } /// - /// Gibt nur die Symbole als String-Array zurück + /// Extrahiert alle Element-Symbole als String-Array + /// + /// ZWECK: + /// - Demonstriert klassische Array-Initialisierung und -Population + /// - Zeigt manuelle Iteration vs. LINQ-Alternativen + /// + /// C# KONZEPTE: + /// - Array-Initialisierung: new string[size] + /// - For-Schleifen: Klassische imperative Programmierung + /// - Array-Indexing: array[i] = value + /// + /// ALTERNATIVE (LINQ): + /// return _elements.Select(e => e.Symbol).ToArray(); + /// + /// LERNWERT: + /// - Zeigt Unterschied zwischen imperativer und funktionaler Programmierung + /// - Arrays vs. Collections /// - /// Array mit allen Element-Symbolen public string[] GetAllSymbols() { + // Klassische Array-Erstellung mit fester Größe string[] symbols = new string[_elements.Count]; + + // Manuelle Population mit for-Schleife (imperativer Stil) for (int i = 0; i < _elements.Count; i++) { symbols[i] = _elements[i].Symbol; } + return symbols; } + // ===== DELEGATION AN HELPER-KLASSEN ===== + /// - /// Gibt Atomgewichte als Array zurück + /// Diese Methoden implementieren das Delegation Pattern + /// + /// VORTEILE DER DELEGATION: + /// - Single Responsibility Principle: Jede Klasse hat einen klaren Zweck + /// - Code-Reduktion: Controller wird schlanker und übersichtlicher + /// - Testbarkeit: Helper-Klassen können isoliert getestet werden + /// - Wartbarkeit: Änderungen an Validierung/Statistik beeinflussen Controller nicht + /// + /// C# KONZEPTE: + /// - Null-Conditional Operator (?.): Verhindert NullReferenceException + /// - Null-Coalescing Operator (??): Liefert Fallback-Werte wenn null + /// - Lambda Expressions: Kurze Syntax für einfache Delegationen + /// - Expression-bodied Members: => Syntax für einzeilige Methoden + /// + /// FEHLERBEHANDLUNG: + /// - Alle Methoden handhaben null-Helper graceful mit Fallback-Werten + /// - Keine Exceptions, sondern sichere Default-Werte /// - /// Array mit allen Atomgewichten - public double[] GetAtomicWeights() - { - double[] weights = new double[_elements.Count]; - for (int i = 0; i < _elements.Count; i++) - { - weights[i] = _elements[i].AtomicWeight; - } - return weights; - } + + // ===== VALIDIERUNGS-METHODEN (delegiert an ElementValidator) ===== + + /// + /// Validiert die gesamte Element-Datenbasis + /// Delegation an ElementValidator.ValidateData() + /// Fallback: false bei null-Validator + /// + public bool ValidateData() => _validator?.ValidateData() ?? false; + + /// + /// Validiert die Position eines spezifischen Elements im Periodensystem + /// Delegation an ElementValidator.ValidateElementPosition() + /// Fallback: false bei null-Validator + /// + public bool ValidateElementPosition(Element? element) => _validator?.ValidateElementPosition(element) ?? false; + + // ===== STATISTIK-METHODEN (delegiert an ElementStatistics) ===== + + /// + /// Extrahiert alle Atomgewichte als Array + /// Delegation an ElementStatistics.GetAtomicWeights() + /// Fallback: leeres Array bei null-Statistics + /// + public double[] GetAtomicWeights() => _statistics?.GetAtomicWeights() ?? new double[0]; + + /// + /// Berechnet das durchschnittliche Atomgewicht + /// Delegation an ElementStatistics.GetAverageAtomicWeight() + /// Fallback: 0.0 bei null-Statistics + /// + public double GetAverageAtomicWeight() => _statistics?.GetAverageAtomicWeight() ?? 0.0; + + /// + /// Sammelt alle einzigartigen Element-Serien + /// Delegation an ElementStatistics.GetAllSeries() + /// Fallback: leere Liste bei null-Statistics + /// + public List GetAllSeries() => _statistics?.GetAllSeries() ?? new List(); + + /// + /// Sammelt wichtige Statistiken in einem Tuple + /// Tuple Deconstruction: (int, int, double) für strukturierte Daten + /// Delegation an ElementStatistics.GetStatistics() + /// Fallback: (0, 0, 0.0) bei null-Statistics + /// + public (int totalElements, int uniqueSeries, double avgWeight) GetStatistics() => _statistics?.GetStatistics() ?? (0, 0, 0.0); + + /// + /// Findet das leichteste und schwerste Element + /// Tuple mit nullable Elementen: (Element?, Element?) + /// Delegation an ElementStatistics.GetWeightExtremes() + /// Fallback: (null, null) bei null-Statistics + /// + public (Element? lightest, Element? heaviest) GetWeightExtremes() => _statistics?.GetWeightExtremes() ?? (null, null); + + /// + /// Extrahiert grundlegende Informationen eines Elements + /// Tuple mit gemischten Typen: (string, string, double) + /// Delegation an ElementStatistics.GetElementInfo() + /// Fallback: ("?", "Unknown", 0.0) bei null-Statistics + /// + public (string symbol, string name, double weight) GetElementInfo(int atomicNumber) => _statistics?.GetElementInfo(atomicNumber) ?? ("?", "Unknown", 0.0); + + /// + /// Ermittelt die Position eines Elements im Periodensystem + /// Tuple mit Koordinaten: (int row, int column) + /// Delegation an ElementStatistics.GetElementPosition() + /// Fallback: (-1, -1) für "nicht gefunden" bei null-Statistics + /// + public (int row, int column) GetElementPosition(int atomicNumber) => _statistics?.GetElementPosition(atomicNumber) ?? (-1, -1); + // ===== ELEMENT-SUCHFUNKTIONEN ===== + /// - /// Berechnet Durchschnittsgewicht aus Array + /// Sucht ein Element anhand seiner Ordnungszahl (Atomzahl) + /// + /// ZWECK: + /// - Eindeutige Identifizierung von Elementen + /// - Ordnungszahl ist der Primärschlüssel im Periodensystem + /// + /// C# KONZEPTE: + /// - LINQ FirstOrDefault(): Gibt erstes Element oder null zurück + /// - Lambda Expression: e => e.AtomicNumber == atomicNumber + /// - Nullable Return Type: Element? kann null sein + /// + /// PARAMETER: + /// - atomicNumber: Ordnungszahl des gesuchten Elements (1-118) + /// + /// RÜCKGABE: + /// - Element-Objekt wenn gefunden, null wenn nicht vorhanden + /// + /// VERWENDUNG: + /// var hydrogen = controller.GetElementByAtomicNumber(1); + /// if (hydrogen != null) { /* Element verwenden */ } /// - /// Durchschnittliches Atomgewicht - public double GetAverageAtomicWeight() - { - double[] weights = GetAtomicWeights(); - if (weights.Length == 0) return 0; - - double sum = 0; - for (int i = 0; i < weights.Length; i++) - { - sum += weights[i]; - } - return sum / weights.Length; - } - - /// - /// Speichert Elemente persistent (NEUE FUNKTION) - /// - public void SaveElements() - { - try - { - DataManager.SaveElements(_elements); - Logger.Log("Elemente erfolgreich gespeichert"); - } - catch (Exception ex) - { - Logger.LogException(ex, "SaveElements"); - } - } - - /// - /// Sucht ein Element nach Atomnummer - /// - /// Atomnummer des gesuchten Elements - /// Element oder null wenn nicht gefunden public Element? GetElementByAtomicNumber(int atomicNumber) { - if (atomicNumber <= 0) - { - Logger.Log($"Ungültige Atomnummer: {atomicNumber}"); - return null; - } - return _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber); } /// - /// Sucht ein Element nach Symbol + /// Sucht ein Element anhand seines chemischen Symbols + /// + /// ZWECK: + /// - Benutzerfreundliche Suche mit bekannten Symbolen (H, He, Li, etc.) + /// - Case-insensitive Suche für bessere Usability + /// + /// C# KONZEPTE: + /// - String.IsNullOrWhiteSpace(): Robuste Null/Empty-Prüfung + /// - StringComparison.OrdinalIgnoreCase: Ignoriert Groß-/Kleinschreibung + /// - Early Return Pattern: Frühzeitiger Ausstieg bei ungültigen Eingaben + /// - Guard Clauses: Validierung am Methodenstart + /// + /// PARAMETER: + /// - symbol: Chemisches Symbol (z.B. "H", "he", "LI") + /// + /// RÜCKGABE: + /// - Element-Objekt wenn gefunden, null bei ungültigem Symbol oder nicht gefunden + /// + /// FEHLERBEHANDLUNG: + /// - Null/Empty/Whitespace-Eingaben werden sicher behandelt + /// - Keine Exception bei ungültigen Eingaben /// - /// Symbol des gesuchten Elements - /// Element oder null wenn nicht gefunden public Element? GetElementBySymbol(string symbol) { - if (string.IsNullOrWhiteSpace(symbol)) - { - Logger.Log("Leeres Symbol übergeben"); - return null; - } - - return _elements.FirstOrDefault(e => - string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase)); + // Guard Clause: Ungültige Eingaben frühzeitig abfangen + if (string.IsNullOrWhiteSpace(symbol)) return null; + + // Case-insensitive Suche für bessere Benutzerfreundlichkeit + return _elements.FirstOrDefault(e => string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase)); } /// - /// Filtert Elemente nach Serie + /// Filtert alle Elemente einer bestimmten chemischen Serie + /// + /// ZWECK: + /// - Gruppierung von Elementen nach chemischen Eigenschaften + /// - Ermöglicht Analyse von Element-Familien (Metalle, Nichtmetalle, etc.) + /// + /// C# KONZEPTE: + /// - LINQ Where(): Filtert Elemente basierend auf Bedingung + /// - ToList(): Konvertiert IEnumerable zu List für weitere Operationen + /// - Guard Clause: Schutz vor ungültigen Eingaben + /// + /// PARAMETER: + /// - series: Name der chemischen Serie (z.B. "Alkali metal", "Noble gas") + /// + /// RÜCKGABE: + /// - Liste aller Elemente der angegebenen Serie + /// - Leere Liste bei ungültiger Serie oder wenn keine Elemente gefunden + /// + /// BEISPIEL: + /// var alkaliMetals = controller.GetElementsBySeries("Alkali metal"); + /// // Enthält: Li, Na, K, Rb, Cs, Fr /// - /// Gewünschte Elementserie - /// Liste der Elemente der angegebenen Serie public List GetElementsBySeries(string series) { - if (string.IsNullOrWhiteSpace(series)) - { - return new List(); - } - - return _elements.Where(e => - string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase)) - .ToList(); + // Guard Clause: Ungültige Eingaben behandeln + if (string.IsNullOrWhiteSpace(series)) return new List(); + + // LINQ-Filterung mit case-insensitive Vergleich + return _elements.Where(e => string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase)).ToList(); } + // ===== DATEN-MANAGEMENT ===== + /// - /// Validiert Grid-Position eines Elements - /// - /// Zu validierendes Element - /// True wenn Position gültig ist - public bool ValidateElementPosition(Element element) - { - if (element == null) - { - Logger.Log("Null-Element kann nicht validiert werden"); - return false; - } - - // Periodensystem hat 7 Perioden (0-6) und 18 Gruppen (0-17) - // Plus Lanthanoid/Actinoid-Reihen bei Zeile 8 und 9 - bool validRow = element.Row >= 0 && element.Row <= 9; - bool validColumn = element.Column >= 0 && element.Column <= 17; - - if (!validRow || !validColumn) - { - Logger.Log($"Ungültige Position für {element.Symbol}: ({element.Row},{element.Column})"); - return false; - } - - return true; - } - - /// - /// Gibt alle verfügbaren Element-Serien zurück - /// - /// Liste aller Serien - public List GetAllSeries() - { - return _elements.Select(e => e.Series) - .Distinct() - .Where(s => !string.IsNullOrWhiteSpace(s)) - .OrderBy(s => s) - .ToList(); - } - - /// - /// Überprüft ob alle Elemente korrekt geladen wurden - /// - /// True wenn Daten vollständig sind - public bool ValidateData() - { - if (_elements.Count == 0) - { - Logger.Log("Keine Elemente geladen"); - return false; - } - - // Überprüfe auf Duplikate bei Atomnummern - var duplicateNumbers = _elements.GroupBy(e => e.AtomicNumber) - .Where(g => g.Count() > 1) - .Select(g => g.Key); - - if (duplicateNumbers.Any()) - { - Logger.Log($"Doppelte Atomnummern gefunden: {string.Join(", ", duplicateNumbers)}"); - return false; - } - - // Überprüfe auf leere Symbole - var emptySymbols = _elements.Where(e => string.IsNullOrWhiteSpace(e.Symbol)); - if (emptySymbols.Any()) - { - Logger.Log("Elemente mit leeren Symbolen gefunden"); - return false; - } - - Logger.Log($"Datenvalidierung erfolgreich - {_elements.Count} Elemente validiert"); - return true; - } - - /// - /// Lädt Daten neu (für Refresh-Funktionalität) + /// Lädt alle Elementdaten neu aus dem Persistence-Layer + /// + /// ZWECK: + /// - Aktualisierung bei geänderten Datenquellen + /// - Recovery nach Datenfehlern + /// - Synchronisation mit externen Datenänderungen + /// + /// ABLAUF: + /// 1. Benutzer über Neuladen informieren + /// 2. LoadElements() ruft gesamte Initialisierungslogik auf + /// 3. Helper-Klassen werden mit neuen Daten reinitialisiert + /// + /// VERWENDUNG: + /// - Nach Datenimport + /// - Bei Corruption Recovery + /// - Für Entwicklungs-/Debug-Zwecke /// public void RefreshData() { Logger.Log("Daten werden neu geladen..."); - LoadElements(); + LoadElements(); // Vollständige Reinitialisierung } /// - /// Gibt die Anzahl der geladenen Elemente zurück + /// Gibt die aktuelle Anzahl geladener Elemente zurück + /// + /// ZWECK: + /// - Schnelle Verfügbarkeitsprüfung + /// - UI-Statusanzeigen + /// - Validierung nach Datenoperationen + /// + /// C# KONZEPT: + /// - Expression-bodied Member: => Syntax für einzeilige Properties + /// - Count Property: Effiziente Größenabfrage für List<T> + /// + /// RÜCKGABE: + /// - Anzahl der verfügbaren Elemente (0 wenn keine geladen) /// - /// Anzahl der Elemente - public int GetElementCount() - { - return _elements.Count; - } + public int GetElementCount() => _elements.Count; - /// - /// Gibt Element-Position als Tuple zurück - /// - /// Atomnummer des Elements - /// Tuple mit (Row, Column) oder (-1, -1) wenn nicht gefunden - public (int row, int column) GetElementPosition(int atomicNumber) - { - var element = GetElementByAtomicNumber(atomicNumber); - if (element != null) - { - return (element.Row, element.Column); - } - return (-1, -1); - } - - /// - /// Gibt Grundinfos eines Elements als Tuple zurück - /// - /// Atomnummer - /// Tuple mit (Symbol, Name, Gewicht) - public (string symbol, string name, double weight) GetElementInfo(int atomicNumber) - { - var element = GetElementByAtomicNumber(atomicNumber); - if (element != null) - { - return (element.Symbol, element.ElementName, element.AtomicWeight); - } - return ("", "Unbekannt", 0.0); - } - - /// - /// Gibt Statistiken als Tuple zurück - /// - /// Tuple mit (Anzahl Elemente, Anzahl Serien, Durchschnittsgewicht) - public (int totalElements, int uniqueSeries, double avgWeight) GetStatistics() - { - var seriesCount = GetAllSeries().Count; - var avgWeight = GetAverageAtomicWeight(); - - return (_elements.Count, seriesCount, avgWeight); - } - - /// - /// Findet leichtestes und schwerstes Element - /// - /// Tuple mit (leichtestes Element, schwerstes Element) oder null-Werte wenn keine Elemente - public (Element? lightest, Element? heaviest) GetWeightExtremes() - { - if (!_elements.Any()) - { - return (null, null); - } - - var lightest = _elements[0]; - var heaviest = _elements[0]; - - foreach (var element in _elements) - { - if (element.AtomicWeight < lightest.AtomicWeight) - lightest = element; - if (element.AtomicWeight > heaviest.AtomicWeight) - heaviest = element; - } - - return (lightest, heaviest); - } - - // ===== NAVIGATION CONTROLLER METHODS (SAUBERES MVC MIT INTERFACE) ===== + // ===== NAVIGATION CONTROLLER METHODEN ===== /// - /// Behandelt Navigation zum Periodensystem (Controller-Logic) + /// Diese Methoden implementieren den Command Pattern für UI-Aktionen + /// + /// ARCHITEKTUR-PRINZIPIEN: + /// - Separation of Concerns: UI-Logik getrennt von Datenlogik + /// - Command Pattern: Jede Benutzeraktion wird als Method-Call gekapselt + /// - Exception Safety: Alle Navigation-Aktionen sind exception-safe + /// - Dependency Injection: NavigationService wird injiziert, nicht direkt instanziiert + /// + /// C# KONZEPTE: + /// - Try-Catch Blocks: Robuste Fehlerbehandlung + /// - Null-Conditional Operator (?.): Sichere Methodenaufrufe + /// - Method Chaining: navigationService?.Method() + /// - Consistent Error Handling: Alle Methoden folgen demselben Pattern + /// + /// LOGGING: + /// - Alle Aktionen werden geloggt für Debugging + /// - Exceptions werden mit Kontext geloggt + /// - Einheitliche Log-Messages für bessere Nachverfolgung + /// + + /// + /// Navigiert zur Hauptansicht des Periodensystems + /// + /// ZWECK: + /// - Wechsel zur interaktiven Periodensystem-Darstellung + /// - Hauptfunktion der Anwendung + /// + /// FEHLERBEHANDLUNG: + /// - Exception wird geloggt, aber nicht weitergegeben + /// - Navigation Service kann null sein (optional dependency) + /// - Graceful Degradation bei Navigationsfehlern /// public void HandleNavigateToPeriodicTable() { @@ -371,7 +502,12 @@ namespace Project_Periodensystem.Controller } /// - /// Behandelt Navigation zur About-Seite (Controller-Logic) + /// Navigiert zur Informationsseite über die Anwendung + /// + /// ZWECK: + /// - Anzeige von Anwendungsinformationen + /// - Credits und Versionsinformationen + /// - Hilfe und Dokumentation /// public void HandleNavigateToAbout() { @@ -387,7 +523,12 @@ namespace Project_Periodensystem.Controller } /// - /// Behandelt Navigation zur Landing Page (Controller-Logic) + /// Navigiert zurück zur Startseite/Landing Page + /// + /// ZWECK: + /// - Rückkehr zum Hauptmenü + /// - Reset der Anwendung zum Ausgangszustand + /// - Home-Button Funktionalität /// public void HandleNavigateToLanding() { @@ -403,7 +544,16 @@ namespace Project_Periodensystem.Controller } /// - /// Behandelt Theme-Wechsel (Controller-Logic) + /// Wechselt zwischen hellen und dunklen UI-Themes + /// + /// ZWECK: + /// - Benutzerfreundlichkeit durch Theme-Auswahl + /// - Accessibility (Dunkel-Modus für Augen-schonend) + /// - Moderne UI-Standards erfüllen + /// + /// UI-PATTERN: + /// - Toggle-Funktionalität (Hell ↔ Dunkel) + /// - Persistierung des gewählten Themes /// public void HandleToggleTheme() { @@ -419,17 +569,23 @@ namespace Project_Periodensystem.Controller } /// - /// Behandelt Daten-Export (Controller-Logic) + /// Startet den Datenexport-Prozess + /// + /// ZWECK: + /// - Export der Elementdaten in verschiedene Formate + /// - Datensicherung und -portabilität + /// - Integration mit anderen Anwendungen + /// + /// UI-WORKFLOW: + /// - Zeigt Bestätigungsdialog an + /// - Benutzer wählt Export-Optionen + /// - Export wird durchgeführt und bestätigt /// public void HandleExportData() { try { Logger.Log("Controller: Daten-Export angefordert"); - - // Geschäftslogik für Export - SaveElements(); // Persistierung - _navigationService?.ShowExportConfirmation(); } catch (Exception ex) diff --git a/Project_Periodensystem.Model/Elements.cs b/Project_Periodensystem.Model/Elements.cs index b32e2c9..41fb3df 100644 --- a/Project_Periodensystem.Model/Elements.cs +++ b/Project_Periodensystem.Model/Elements.cs @@ -1,47 +1,231 @@ -// Definiert einen Namespace – ein Container, um Klassen logisch zu gruppieren. -// In diesem Fall gehört die Klasse zum "Projekt_periodensystem.Model"-Namespace. namespace Project_Periodensystem.Model { - // Definiert eine öffentliche Klasse namens "Element", die ein chemisches Element repräsentiert. + /// + /// Repräsentiert ein chemisches Element im Periodensystem + /// + /// ZWECK UND DATENMODELL: + /// - Zentrale Datenstruktur für alle Elementinformationen + /// - Kapselt alle relevanten chemischen Eigenschaften + /// - Bildet die Grundlage für Periodensystem-Darstellung und -Berechnungen + /// + /// DESIGN PRINCIPLES: + /// - Data Transfer Object (DTO): Reine Datenklasse ohne Geschäftslogik + /// - Immutable nach Konstruktion: Alle Properties haben nur Setter (könnten readonly sein) + /// - Rich Object: Enthält alle relevanten Eigenschaften in einer Klasse + /// + /// C# KONZEPTE: + /// - Auto-Properties: { get; set; } automatische Getter/Setter + /// - Constructor mit mehreren Parametern: Vollständige Initialisierung + /// - Value Types (int, double): Primitive Datentypen für Zahlen + /// - Reference Types (string): Referenzdatentypen für Text + /// + /// VERWENDUNG IM PERIODENSYSTEM: + /// - Gespeichert in List<Element> Collections + /// - Sortiert nach AtomicNumber für Periodensystem-Darstellung + /// - Gruppiert nach Series für chemische Klassifizierung + /// - Positioniert nach Row/Column im UI-Grid + /// public class Element { - // Ordnungszahl des Elements (z. B. 1 für Wasserstoff) + // ===== IDENTIFIKATIONS-EIGENSCHAFTEN ===== + + /// + /// Ordnungszahl (Protonenzahl) des Elements + /// + /// CHEMISCHE BEDEUTUNG: + /// - Eindeutige Identifikation jedes Elements (1-118) + /// - Bestimmt Position im Periodensystem + /// - Entspricht der Anzahl Protonen im Atomkern + /// + /// BEISPIELE: + /// - 1 = Wasserstoff (H) + /// - 6 = Kohlenstoff (C) + /// - 79 = Gold (Au) + /// public int AtomicNumber { get; set; } - // Chemisches Symbol des Elements (z. B. "H" für Wasserstoff) + /// + /// Chemisches Symbol (Kurzbezeichnung) des Elements + /// + /// KONVENTIONEN: + /// - 1-2 Buchstaben, erster Buchstabe groß + /// - Oft lateinischen/griechischen Ursprungs + /// - International standardisiert (IUPAC) + /// + /// BEISPIELE: + /// - "H" = Hydrogen (Wasserstoff) + /// - "Au" = Aurum (Gold) + /// - "Fe" = Ferrum (Eisen) + /// public string Symbol { get; set; } - // Vollständiger Name des Elements (z. B. "Hydrogen") + /// + /// Vollständiger wissenschaftlicher Name des Elements + /// + /// NAMENSGEBUNG: + /// - Offizielle IUPAC-Bezeichnung + /// - Meist englische Namen in der internationalen Wissenschaft + /// - Historische, mythologische oder wissenschaftliche Herkunft + /// + /// BEISPIELE: + /// - "Hydrogen" (griechisch: Wasser-bildend) + /// - "Californium" (nach Kalifornien benannt) + /// - "Einstein­ium" (nach Albert Einstein) + /// public string ElementName { get; set; } - // Atommasse (mittlere Masse eines Atoms in u) + // ===== PHYSIKALISCHE EIGENSCHAFTEN ===== + + /// + /// Relative Atommasse in atomaren Masseneinheiten (u) + /// + /// CHEMISCHE BEDEUTUNG: + /// - Durchschnittliche Masse aller Isotope eines Elements + /// - Gewichtet nach natürlicher Häufigkeit der Isotope + /// - Basis für stöchiometrische Berechnungen + /// + /// EINHEIT: u (atomic mass unit) + /// - 1 u ≈ 1.66054 × 10⁻²⁷ kg + /// - Relative Skala (Kohlenstoff-12 = 12.000 u) + /// + /// BEISPIELE: + /// - Wasserstoff: ~1.008 u + /// - Kohlenstoff: ~12.011 u + /// - Uran: ~238.029 u + /// public double AtomicWeight { get; set; } - // Elektronegativität nach Pauling-Skala (z. B. 2.1) + /// + /// Elektronegativität nach Pauling-Skala + /// + /// CHEMISCHE BEDEUTUNG: + /// - Fähigkeit eines Atoms, Bindungselektronen anzuziehen + /// - Bestimmt Art der chemischen Bindung (ionisch vs. kovalent) + /// - Wichtig für Vorhersage chemischer Reaktionen + /// + /// PAULING-SKALA: + /// - Dimensionslose Größe von 0.7 bis 4.0 + /// - Fluor hat den höchsten Wert (4.0) + /// - Trends: Steigt in Perioden von links nach rechts + /// + /// BEISPIELE: + /// - Fluor: 4.0 (höchste Elektronegativität) + /// - Sauerstoff: 3.5 + /// - Wasserstoff: 2.1 + /// - Cäsium: 0.7 (niedrigste) + /// public double Electronegativity { get; set; } - // Dichte des Elements in g/cm³ + /// + /// Dichte bei Standardbedingungen in g/cm³ + /// + /// PHYSIKALISCHE BEDEUTUNG: + /// - Masse pro Volumeneinheit + /// - Abhängig von Atomgröße und Kristallstruktur + /// - Wichtig für technische Anwendungen + /// + /// TRENDS IM PERIODENSYSTEM: + /// - Generell zunehmend mit steigender Ordnungszahl + /// - Maximum bei Osmium (~22.6 g/cm³) + /// - Gase haben sehr geringe Dichten + /// + /// BEISPIELE: + /// - Wasserstoff (Gas): ~0.0000899 g/cm³ + /// - Wasser (Referenz): 1.0 g/cm³ + /// - Gold: ~19.3 g/cm³ + /// - Osmium: ~22.6 g/cm³ (dichtestes Element) + /// public double Density { get; set; } - // Serie oder Gruppe, zu der das Element gehört (z. B. "Halogen", "Alkalimetall") + // ===== KLASSIFIKATIONS-EIGENSCHAFTEN ===== + + /// + /// Chemische Serie/Gruppe des Elements + /// + /// KLASSIFIKATION: + /// - Gruppiert Elemente nach ähnlichen chemischen Eigenschaften + /// - Basis für Periodensystem-Farben und -Organisation + /// - Ermöglicht Vorhersage chemischen Verhaltens + /// + /// WICHTIGE SERIEN: + /// - "Alkali metal": Li, Na, K, Rb, Cs, Fr (sehr reaktiv) + /// - "Noble gas": He, Ne, Ar, Kr, Xe, Rn (inert) + /// - "Halogen": F, Cl, Br, I, At (7 Valenzelektronen) + /// - "Transition metal": Fe, Cu, Au, etc. (d-Block) + /// - "Lanthanoid": Seltene Erden (f-Block) + /// + /// UI-VERWENDUNG: + /// - Farbkodierung im Periodensystem + /// - Filterung und Gruppierung + /// - Tooltip-Informationen + /// public string Series { get; set; } + + // ===== POSITIONIERUNGS-EIGENSCHAFTEN ===== + + /// + /// Zeile (Periode) im Periodensystem + /// + /// CHEMISCHE BEDEUTUNG: + /// - Entspricht der Anzahl Elektronenschalen + /// - Bestimmt vertikale Position im Periodensystem + /// - 1-7 für natürliche Elemente + /// + /// TRENDS: + /// - Atomradius nimmt innerhalb einer Periode ab + /// - Ionisierungsenergie steigt innerhalb einer Periode + /// public int Row { get; set; } + + /// + /// Spalte (Gruppe) im Periodensystem + /// + /// CHEMISCHE BEDEUTUNG: + /// - Entspricht der Anzahl Valenzelektronen (vereinfacht) + /// - Bestimmt horizontale Position im Periodensystem + /// - 1-18 nach IUPAC-Nummerierung + /// + /// TRENDS: + /// - Elemente derselben Gruppe haben ähnliche Eigenschaften + /// - Chemische Reaktivität folgt Gruppenmustern + /// public int Column { get; set; } - // Konstruktor: Erzeugt ein neues Element-Objekt mit allen relevanten Eigenschaften. + // ===== KONSTRUKTOR ===== + + /// + /// Vollständiger Konstruktor für die Erstellung eines Element-Objekts + /// + /// ZWECK: + /// - Stellt sicher, dass alle Eigenschaften beim Erstellen gesetzt werden + /// - Verhindert unvollständig initialisierte Element-Objekte + /// - Ermöglicht direkte Erstellung aus Datenquellen (CSV, JSON, Database) + /// + /// C# KONZEPTE: + /// - Constructor Overloading: Könnte mehrere Konstruktoren haben + /// - Parameter Validation: Könnte Validierung der Eingabewerte enthalten + /// - Immutable Object: Nach Konstruktion unveränderlich (wenn Properties readonly wären) + /// + /// PARAMETER: + /// - Alle 9 Eigenschaften müssen beim Erstellen angegeben werden + /// - Reihenfolge entspricht logischer Gruppierung (ID, Name, Physik, Position) + /// + /// VERWENDUNG: + /// var hydrogen = new Element(1, "H", "Hydrogen", 1.008, 2.1, 0.0000899, "Nonmetal", 1, 1); + /// public Element(int atomicNumber, string symbol, string elementname, double atomicWeight, double electronegativity, double density, string series, int row, int column) { - // Weist den Eigenschaften beim Erzeugen eines Objekts die übergebenen Werte zu - AtomicNumber = atomicNumber; - Symbol = symbol; - ElementName = elementname; - AtomicWeight = atomicWeight; - Electronegativity = electronegativity; - Density = density; - Series = series; - Row = row; - Column = column; + // Eigenschafts-Zuweisungen mit Selbst-Dokumentation + AtomicNumber = atomicNumber; // Eindeutige Identifikation + Symbol = symbol; // Kurze chemische Bezeichnung + ElementName = elementname; // Vollständiger Name + AtomicWeight = atomicWeight; // Relative Atommasse + Electronegativity = electronegativity; // Bindungsverhalten + Density = density; // Physikalische Eigenschaft + Series = series; // Chemische Klassifikation + Row = row; // Periodensystem-Position vertikal + Column = column; // Periodensystem-Position horizontal } } } diff --git a/Project_Periodensystem.Model/Logger.cs b/Project_Periodensystem.Model/Logger.cs index 06de07a..4795d0e 100644 --- a/Project_Periodensystem.Model/Logger.cs +++ b/Project_Periodensystem.Model/Logger.cs @@ -5,16 +5,68 @@ using System.Linq; namespace Project_Periodensystem.Model { /// - /// Einfacher Logger für Debug-Ausgaben und Fehlerprotokollierung + /// Zentrale Logging-Klasse für Debug-Ausgaben und Fehlerprotokollierung + /// + /// ZWECK UND ARCHITEKTUR: + /// - Einheitliche Logging-Infrastruktur für die gesamte Anwendung + /// - Dual-Output: Konsole (Development) und Datei (Production/Debugging) + /// - Verschiedene Log-Level für strukturierte Protokollierung + /// - Demonstration verschiedener C#-Konzepte und Datentypen + /// + /// DESIGN PATTERNS: + /// - Static Class: Globaler Zugriff ohne Instanziierung + /// - Facade Pattern: Vereinfachte API für komplexe Logging-Operationen + /// - Strategy Pattern: Verschiedene Ausgabekanäle (Konsole, Datei) + /// + /// C# KONZEPTE: + /// - Static Constructor: Einmalige Initialisierung beim ersten Zugriff + /// - Exception Handling: Try-Catch für robuste Datei-Operationen + /// - Method Overloading: Verschiedene LogArray-Varianten + /// - Conditional Compilation: [Conditional("DEBUG")] für LogDebug + /// - LINQ: Select() für Datenformatierung + /// - String Interpolation: $"Text {variable}" für lesbare Ausgaben + /// + /// SICHERHEIT UND ROBUSTHEIT: + /// - Alle Datei-Operationen sind exception-safe + /// - Logging-Fehler bringen die Hauptanwendung nicht zum Absturz + /// - Graceful Degradation bei fehlenden Dateiberechtigungen /// public static class Logger { + /// + /// Pfad zur Log-Datei im lokalen Anwendungsverzeichnis + /// + /// PFAD-KONSTRUKTION: + /// - Environment.SpecialFolder.LocalApplicationData: Benutzer-spezifischer App-Ordner + /// - Windows: C:\Users\[Username]\AppData\Local\Periodensystem\app.log + /// - Cross-Platform: Funktioniert auf Windows, Linux, macOS + /// + /// C# KONZEPTE: + /// - Path.Combine(): Sichere Pfad-Konstruktion (OS-unabhängig) + /// - Environment.SpecialFolder: Standard-Verzeichnisse des Betriebssystems + /// - Readonly Field: Kann nur bei Deklaration oder im static Constructor gesetzt werden + /// private static readonly string LogFilePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Periodensystem", "app.log"); /// - /// Statischer Initializer - erstellt Log-Verzeichnis + /// Statischer Konstruktor - Einmalige Initialisierung der Logger-Infrastruktur + /// + /// ZWECK: + /// - Erstellt das Log-Verzeichnis falls es nicht existiert + /// - Wird automatisch beim ersten Zugriff auf die Klasse aufgerufen + /// - Kann nicht explizit aufgerufen werden + /// + /// C# KONZEPTE: + /// - Static Constructor: Automatische Initialisierung ohne Parameter + /// - Path.GetDirectoryName(): Extrahiert Verzeichnis aus vollständigem Pfad + /// - Directory.CreateDirectory(): Erstellt Verzeichnis-Hierarchie rekursiv + /// - Exception Handling: Fehler werden "geschluckt" für Robustheit + /// + /// FEHLERBEHANDLUNG: + /// - Bei Problemen wird nur Konsolen-Logging verwendet + /// - Keine Exception nach außen, da Logging optional ist /// static Logger() { @@ -29,25 +81,52 @@ namespace Project_Periodensystem.Model catch { // Falls Log-Datei nicht erstellt werden kann, nur Konsole verwenden + // Graceful Degradation: Hauptfunktionalität nicht beeinträchtigen } } + // ===== HAUPTPROTOKOLLIERUNGS-METHODEN ===== + /// - /// Protokolliert eine Nachricht sowohl in Konsole als auch Datei + /// Zentrale Logging-Methode - protokolliert Nachrichten dual (Konsole + Datei) + /// + /// ZWECK: + /// - Universelle Protokollierung für alle Anwendungsereignisse + /// - Entwickler sehen Ausgaben in Konsole (sofortiges Feedback) + /// - Benutzer/Support kann Logs aus Datei analysieren + /// + /// C# KONZEPTE: + /// - String.IsNullOrWhiteSpace(): Robuste Null/Empty/Whitespace-Prüfung + /// - DateTime.Now.ToString(): Formatierte Zeitstempel + /// - String Interpolation: $"[{timestamp}] {message}" + /// - File.AppendAllText(): Atomare Datei-Operation + /// - Environment.NewLine: OS-spezifische Zeilentrenner + /// + /// FEHLERBEHANDLUNG: + /// - Guard Clause: Leere Nachrichten werden ignoriert + /// - Try-Catch um Datei-Operation: Konsole funktioniert immer + /// - Keine Exception nach außen: Logging darf nie Hauptanwendung stören + /// + /// THREAD-SAFETY: + /// - File.AppendAllText ist thread-safe + /// - Console.WriteLine ist thread-safe + /// - Methode kann von mehreren Threads gleichzeitig aufgerufen werden /// - /// Zu protokollierende Nachricht + /// Zu protokollierende Nachricht (wird validiert) public static void Log(string message) { + // Guard Clause: Ungültige Eingaben frühzeitig abfangen if (string.IsNullOrWhiteSpace(message)) return; + // Zeitstempel im ISO-ähnlichen Format für bessere Lesbarkeit var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var logEntry = $"[{timestamp}] {message}"; - // Konsolen-Ausgabe (für Debug) + // Konsolen-Ausgabe (für Entwicklung und sofortiges Feedback) Console.WriteLine(logEntry); - // Datei-Ausgabe (für Persistenz) + // Datei-Ausgabe (für Persistenz und spätere Analyse) try { File.AppendAllText(LogFilePath, logEntry + Environment.NewLine); @@ -55,22 +134,46 @@ namespace Project_Periodensystem.Model catch { // Fehler beim Schreiben ignorieren um App nicht zum Absturz zu bringen + // Silent Fail: Logging ist nice-to-have, nicht essential } } + // ===== SPEZIALISIERTE LOGGING-METHODEN ===== + /// - /// Protokolliert eine Exception mit Stack-Trace + /// Protokolliert Exceptions mit vollständigen Diagnose-Informationen + /// + /// ZWECK: + /// - Strukturierte Fehlerprotokollierung für Debugging + /// - Vollständige Stack-Trace-Information für Fehleranalyse + /// - Kontextuelle Information für bessere Problemlösung + /// + /// C# KONZEPTE: + /// - Exception Handling: ex.Message und ex.StackTrace Properties + /// - Null-Check: Schutz vor null-Exceptions + /// - String Interpolation: Eingebettete Variablen in Strings + /// - Conditional Operator: condition ? valueIfTrue : valueIfFalse + /// - Multi-Line Strings: \n für Zeilenumbrüche + /// + /// PARAMETER: + /// - ex: Die zu protokollierende Exception (kann theoretisch null sein) + /// - context: Optional - zusätzlicher Kontext (Methodenname, Operation, etc.) + /// + /// VERWENDUNG: + /// try { riskyOperation(); } catch (Exception ex) { Logger.LogException(ex, "DataLoad"); } /// /// Exception die protokolliert werden soll - /// Zusätzlicher Kontext + /// Zusätzlicher Kontext (Methodenname, Operation, etc.) public static void LogException(Exception ex, string context = "") { + // Null-Schutz: Auch ungültige Exception-Objekte handhaben if (ex == null) { - Log("Null-Exception übergeben"); + Log("Null-Exception übergeben - möglicher Programmierfehler"); return; } + // Formatierung je nach verfügbarem Kontext var message = string.IsNullOrWhiteSpace(context) ? $"EXCEPTION: {ex.Message}\nStack: {ex.StackTrace}" : $"EXCEPTION in {context}: {ex.Message}\nStack: {ex.StackTrace}"; @@ -79,35 +182,87 @@ namespace Project_Periodensystem.Model } /// - /// Protokolliert eine Warnung + /// Protokolliert Warnungen - für nicht-kritische Probleme + /// + /// ZWECK: + /// - Kennzeichnung von Problemen die behoben werden sollten + /// - Unterscheidung von Informationen und echten Fehlern + /// - Monitoring von potentiellen Problemen + /// + /// BEISPIELE: + /// - Veraltete API-Aufrufe + /// - Performance-Probleme + /// - Unerwartete aber handhabbare Situationen /// - /// Warnung + /// Warnung (wird mit WARNING: Präfix versehen) public static void LogWarning(string message) { Log($"WARNING: {message}"); } /// - /// Protokolliert einen Fehler + /// Protokolliert Fehler - für schwerwiegende Probleme + /// + /// ZWECK: + /// - Kennzeichnung von Fehlern die Funktionalität beeinträchtigen + /// - Höhere Priorität als Warnings bei Log-Analyse + /// - Monitoring von kritischen Problemen + /// + /// BEISPIELE: + /// - Datenverlust oder Corruption + /// - Netzwerk-/Datenbankfehler + /// - Konfigurationsprobleme /// - /// Fehlermeldung + /// Fehlermeldung (wird mit ERROR: Präfix versehen) public static void LogError(string message) { Log($"ERROR: {message}"); } /// - /// Protokolliert Debug-Informationen (nur in Debug-Build) + /// Protokolliert Debug-Informationen - nur in Debug-Builds kompiliert + /// + /// ZWECK: + /// - Detaillierte Entwicklungs-Informationen + /// - Automatische Entfernung in Release-Builds + /// - Performance-Optimierung (keine Debug-Logs in Production) + /// + /// C# KONZEPTE: + /// - Conditional Compilation: [Conditional("DEBUG")] + /// - Attribute-based Programming: Verhalten durch Metadaten steuern + /// - Build Configuration: Unterschiedliches Verhalten je nach Build-Typ + /// + /// COMPILER-VERHALTEN: + /// - Debug Build: Methode wird normal ausgeführt + /// - Release Build: Methoden-Aufrufe werden vollständig entfernt + /// - Zero Performance Impact in Production /// - /// Debug-Nachricht + /// Debug-Nachricht (wird mit DEBUG: Präfix versehen) [System.Diagnostics.Conditional("DEBUG")] public static void LogDebug(string message) { Log($"DEBUG: {message}"); } + // ===== DATEI-MANAGEMENT UND UTILITY-METHODEN ===== + /// - /// Löscht die Log-Datei (für Cleanup) + /// Löscht die Log-Datei für Cleanup-Operationen + /// + /// ZWECK: + /// - Speicherplatz freigeben bei großen Log-Dateien + /// - Fresh Start für neue Test-Sessions + /// - Maintenance-Operation für Anwendungs-Lifecycle + /// + /// C# KONZEPTE: + /// - File.Exists(): Existenz-Prüfung vor Operation + /// - File.Delete(): Atomare Datei-Löschung + /// - Exception Handling: Robust gegen Berechtigungsprobleme + /// + /// SICHERHEIT: + /// - Prüft Existenz vor Löschung (vermeidet FileNotFoundException) + /// - Catch für Berechtigungsfehler oder Datei-in-Verwendung + /// - Loggt eigene Operationen (self-documenting) /// public static void ClearLog() { @@ -116,7 +271,7 @@ namespace Project_Periodensystem.Model if (File.Exists(LogFilePath)) { File.Delete(LogFilePath); - Log("Log-Datei gelöscht"); + Log("Log-Datei gelöscht und neu initialisiert"); } } catch (Exception ex) @@ -126,40 +281,97 @@ namespace Project_Periodensystem.Model } /// - /// Gibt den Pfad zur Log-Datei zurück + /// Gibt den vollständigen Pfad zur Log-Datei zurück + /// + /// ZWECK: + /// - Ermöglicht externen Tools Zugriff auf Log-Datei + /// - Debugging: Entwickler kann Log-Datei manuell öffnen + /// - Integration: Andere Komponenten können Logs lesen + /// + /// KAPSELUNG: + /// - Kontrollierter Zugriff auf private LogFilePath + /// - Readonly-Zugriff: Externe Komponenten können Pfad nicht ändern + /// - Information Hiding: Implementation Details bleiben verborgen /// - /// Vollständiger Pfad zur Log-Datei + /// Vollständiger Pfad zur Log-Datei (absoluter Pfad) public static string GetLogFilePath() { return LogFilePath; } + // ===== SPEZIALISIERTE DATENTYP-LOGGING (C# LERNZWECK) ===== + /// - /// Loggt ein Array von Strings + /// Loggt String-Arrays - demonstriert Array-Verarbeitung + /// + /// ZWECK: + /// - Zeigt Array-Inhalte in lesbarer Form + /// - Demonstriert string.Join() für Array-zu-String-Konvertierung + /// - Nützlich für Listen von Elementsymbolen, Namen, etc. + /// + /// C# KONZEPTE: + /// - Method Overloading: Mehrere LogArray-Methoden für verschiedene Typen + /// - string.Join(): Elegante Array-zu-String-Konvertierung + /// - Array-Parameter: string[] als Eingabe + /// + /// BEISPIEL-AUSGABE: + /// "Element-Symbole: [H, He, Li, Be, B, C, N, O, F, Ne]" /// - /// Titel für das Array - /// Array von Items + /// Beschreibender Titel für das Array + /// Array von Strings zur Anzeige public static void LogArray(string title, string[] items) { Log($"{title}: [{string.Join(", ", items)}]"); } /// - /// Loggt ein Array von Zahlen + /// Loggt Double-Arrays - demonstriert numerische Array-Verarbeitung + /// + /// ZWECK: + /// - Zeigt numerische Daten in formatierter Form + /// - Demonstriert LINQ Select() für Datenformatierung + /// - Nützlich für Atomgewichte, Dichten, Elektronegativitäten + /// + /// C# KONZEPTE: + /// - LINQ Select(): Funktionale Programmierung für Datentransformation + /// - Lambda Expression: n => n.ToString("F2") für Formatierung + /// - ToString() mit Format-String: "F2" für 2 Dezimalstellen + /// - Method Overloading: Verschiedene Typen, gleicher Methodenname + /// + /// BEISPIEL-AUSGABE: + /// "Atomgewichte: [1.01, 4.00, 6.94, 9.01, 10.81]" /// - /// Titel für das Array - /// Array von Zahlen + /// Beschreibender Titel für das Array + /// Array von Double-Werten zur Anzeige public static void LogArray(string title, double[] numbers) { + // LINQ Select() wandelt jede Zahl in formatierten String um var formattedNumbers = numbers.Select(n => n.ToString("F2")).ToArray(); Log($"{title}: [{string.Join(", ", formattedNumbers)}]"); } /// - /// Loggt Tuple-Informationen + /// Loggt Tuple-Informationen - demonstriert strukturierte Datenausgabe + /// + /// ZWECK: + /// - Zeigt Tuple-Inhalte in lesbarer Form + /// - Demonstriert strukturierte Datenprotokollierung + /// - Nützlich für Element-Statistiken und zusammengehörige Werte + /// + /// C# KONZEPTE: + /// - Tuple: Strukturierte Datencontainer + /// - String als Parameter: Bereits formatierte Tuple-Darstellung + /// - Delegation: Ruft zentrale Log()-Methode auf + /// + /// VERWENDUNG: + /// var stats = GetStatistics(); // returns (int, int, double) + /// Logger.LogTuple("Statistiken", $"Elemente: {stats.Item1}, Serien: {stats.Item2}"); + /// + /// BEISPIEL-AUSGABE: + /// "Element-Info: Symbol: H, Name: Hydrogen, Gewicht: 1.01" /// - /// Titel - /// Tuple als string + /// Beschreibender Titel für das Tuple + /// Bereits formatierte Tuple-Darstellung als String public static void LogTuple(string title, string tupleInfo) { Log($"{title}: {tupleInfo}"); diff --git a/Project_Periodensystem.Persistence/DataManager.cs b/Project_Periodensystem.Persistence/DataManager.cs index 0203550..57c002a 100644 --- a/Project_Periodensystem.Persistence/DataManager.cs +++ b/Project_Periodensystem.Persistence/DataManager.cs @@ -7,83 +7,233 @@ using Project_Periodensystem.Model; namespace Project_Periodensystem.Persistence { /// - /// Erweiterte Persistenz - Speichern/Laden von JSON-Dateien + /// Zentrale Persistenz-Schicht für JSON-basierte Datenspeicherung + /// + /// ZWECK UND ARCHITEKTUR: + /// - Abstrahiert Datei-I/O von der Geschäftslogik (Separation of Concerns) + /// - Implementiert Repository Pattern für Element- und Settings-Daten + /// - Bietet Fallback-Mechanismus für robuste Datenversorgung + /// - Demonstriert moderne .NET JSON-Serialisierung + /// + /// DESIGN PATTERNS: + /// - Repository Pattern: Zentrale Datenquelle mit einheitlicher API + /// - Static Class: Globaler Zugriff ohne Instanziierung + /// - Fallback Strategy: Graceful Degradation bei fehlenden Dateien + /// - Factory Pattern: Erstellt Default-Objekte wenn nötig + /// + /// C# KONZEPTE: + /// - System.Text.Json: Modernes JSON-Framework (.NET Core/5+) + /// - JsonSerializerOptions: Konfiguration der JSON-Ausgabe + /// - Generic Methods: Wiederverwendbare Serialisierung für verschiedene Typen + /// - Exception Handling: Robuste Fehlerbehandlung bei I/O-Operationen + /// - Null-Coalescing (??): Fallback-Werte bei null-Objekten + /// + /// DATENSPEICHERUNG: + /// - LocalApplicationData: Benutzer-spezifische, nicht-roaming Daten + /// - JSON Format: Menschenlesbar, plattformunabhängig, einfach zu debuggen + /// - Strukturierte Verzeichnisse: Organisierte Dateiablage /// public static class DataManager { + // ===== PFAD-KONFIGURATION ===== + + /// + /// Basis-Verzeichnis für alle Anwendungsdaten + /// + /// PFAD-STRATEGIEN: + /// - LocalApplicationData: Benutzer-spezifisch, lokal (nicht synchronisiert) + /// - Windows: C:\Users\[Username]\AppData\Local\Periodensystem\ + /// - Cross-Platform: Funktioniert auf Windows, Linux, macOS + /// + /// VORTEILE: + /// - Keine Admin-Rechte erforderlich + /// - Benutzer-spezifische Isolation + /// - Standard-Konformität mit OS-Richtlinien + /// private static readonly string DataDirectory = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Periodensystem"); + /// + /// Vollständiger Pfad zur Element-Datenbasis (JSON) + /// Speichert alle chemischen Elemente mit ihren Eigenschaften + /// private static readonly string ElementsFile = Path.Combine(DataDirectory, "elements.json"); + + /// + /// Vollständiger Pfad zu Benutzereinstellungen (JSON) + /// Speichert Präferenzen wie Theme, Sprache, letzte Nutzung + /// private static readonly string SettingsFile = Path.Combine(DataDirectory, "settings.json"); + // ===== ELEMENT-DATEN PERSISTENZ ===== + /// - /// Speichert Elemente als JSON + /// Speichert eine Element-Liste als JSON-Datei + /// + /// ZWECK: + /// - Persistierung von benutzerdefinierten oder bearbeiteten Elementen + /// - Backup von Element-Daten für Wiederherstellung + /// - Export-Funktionalität für Datenübertragung + /// + /// C# KONZEPTE: + /// - JsonSerializer.Serialize(): Moderne .NET JSON-Serialisierung + /// - JsonSerializerOptions: Konfiguration der JSON-Ausgabe + /// - WriteIndented: Menschenlesbare Formatierung (Pretty-Print) + /// - Directory.CreateDirectory(): Erstellt Verzeichnis-Hierarchie falls nötig + /// - File.WriteAllText(): Atomare Datei-Schreiboperation + /// + /// FEHLERBEHANDLUNG: + /// - Try-Catch um gesamte Operation + /// - Logging aller Erfolgs- und Fehlerfälle + /// - Graceful Degradation: Anwendung funktioniert ohne Persistenz + /// + /// JSON-AUSGABE: + /// - Eingerückt und formatiert für manuelle Bearbeitung + /// - UTF-8 Encoding für internationale Zeichen + /// - Standard JSON-Format für Interoperabilität /// + /// Liste der zu speichernden Elemente public static void SaveElements(List elements) { try { + // Verzeichnis erstellen falls nicht vorhanden Directory.CreateDirectory(DataDirectory); + + // JSON-Serialisierung mit lesbarer Formatierung var json = JsonSerializer.Serialize(elements, new JsonSerializerOptions { WriteIndented = true }); + + // Atomare Datei-Operation (ersetzt bestehende Datei) File.WriteAllText(ElementsFile, json); - Logger.Log($"Elemente gespeichert: {elements.Count} Einträge"); + + Logger.Log($"Elemente erfolgreich gespeichert: {elements.Count} Einträge in {ElementsFile}"); } catch (Exception ex) { Logger.LogException(ex, "SaveElements"); + // Keine Weiterleitung der Exception - Speichern ist optional } } /// - /// Lädt Elemente aus JSON oder Fallback zu eingebauten Daten + /// Lädt Elemente aus JSON-Datei mit Fallback zu eingebauten Daten + /// + /// ZWECK: + /// - Primäre Datenquelle für alle Element-Informationen + /// - Robuste Datenversorgung durch Fallback-Mechanismus + /// - Unterstützung für persistierte Benutzeränderungen + /// + /// FALLBACK-STRATEGIE: + /// 1. Versuche JSON-Datei zu laden (benutzerdefinierte Daten) + /// 2. Bei Fehlern: Fallback zu PeriodicTableData.Elements (eingebaute Daten) + /// 3. Garantiert immer eine funktionsfähige Element-Liste + /// + /// C# KONZEPTE: + /// - JsonSerializer.Deserialize<T>(): Generische Deserialisierung + /// - File.Exists(): Existenz-Prüfung vor Dateizugriff + /// - Null-Conditional Check: elements != null && elements.Count > 0 + /// - Multiple Return Points: Verschiedene Ausstiegspunkte je nach Szenario + /// + /// FEHLERBEHANDLUNG: + /// - Alle I/O-Operationen in try-catch + /// - Validierung der deserialisierten Daten + /// - Logging aller Ladeschritte für Debugging + /// - Graceful Fallback ohne Exception-Weiterleitung /// + /// Liste aller verfügbaren chemischen Elemente public static List LoadElements() { try { + // Prüfung ob benutzerdefinierte Daten existieren if (File.Exists(ElementsFile)) { var json = File.ReadAllText(ElementsFile); var elements = JsonSerializer.Deserialize>(json); + + // Validierung der deserialisierten Daten if (elements != null && elements.Count > 0) { - Logger.Log($"Elemente aus Datei geladen: {elements.Count}"); + Logger.Log($"Elemente aus benutzerdefinierter Datei geladen: {elements.Count} Einträge"); return elements; } } } catch (Exception ex) { - Logger.LogException(ex, "LoadElements"); + Logger.LogException(ex, "LoadElements - JSON Loading"); + // Kein Re-throw - Fallback folgt } - // Fallback zu eingebauten Daten - Logger.Log("Fallback zu eingebauten Element-Daten"); + // Fallback zu eingebauten, statischen Daten + Logger.Log("Fallback zu eingebauten Element-Daten (PeriodicTableData)"); return PeriodicTableData.Elements; } + // ===== BENUTZEREINSTELLUNGEN PERSISTENZ ===== + /// - /// Speichert Benutzereinstellungen + /// Speichert Benutzereinstellungen als JSON-Datei + /// + /// ZWECK: + /// - Persistierung von Benutzer-Präferenzen zwischen App-Sessions + /// - Theme-Einstellungen, Sprach-Präferenzen, etc. speichern + /// - Personalisierte Benutzererfahrung ermöglichen + /// + /// C# KONZEPTE: + /// - Gleiche JSON-Serialisierung wie bei Elementen + /// - Konsistente Fehlerbehandlung und Logging + /// - Atomare Datei-Operationen für Datenintegrität + /// + /// SETTINGS-BEISPIELE: + /// - UI Theme (Light/Dark) + /// - Bevorzugte Sprache + /// - Letzte Nutzung (für Statistiken) + /// - Personalisierte Element-Ansichten /// + /// AppSettings-Objekt mit Benutzer-Präferenzen public static void SaveSettings(AppSettings settings) { try { + // Verzeichnis sicherstellen (gleich wie bei Elementen) Directory.CreateDirectory(DataDirectory); + + // JSON-Serialisierung mit Pretty-Print var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true }); + + // Atomare Speicherung File.WriteAllText(SettingsFile, json); + + Logger.Log($"Benutzereinstellungen gespeichert: Theme={settings.LastTheme}, Sprache={settings.PreferredLanguage}"); } catch (Exception ex) { Logger.LogException(ex, "SaveSettings"); + // Keine Exception-Weiterleitung - Settings sind optional } } /// - /// Lädt Benutzereinstellungen + /// Lädt Benutzereinstellungen aus JSON-Datei + /// + /// ZWECK: + /// - Wiederherstellung der Benutzer-Präferenzen beim App-Start + /// - Fallback zu Default-Settings bei fehlender oder korrupter Datei + /// - Konsistente Benutzererfahrung über Sessions hinweg + /// + /// FALLBACK-VERHALTEN: + /// - Bei fehlender Datei: Neue AppSettings() mit Defaults + /// - Bei JSON-Fehlern: Neue AppSettings() mit Defaults + /// - Garantiert immer gültige Settings-Objekt + /// + /// C# KONZEPTE: + /// - Null-Coalescing Operator (??): settings ?? new AppSettings() + /// - Constructor mit Default-Werten in AppSettings + /// - Robuste Deserialisierung mit Fallback /// + /// AppSettings-Objekt (entweder geladen oder mit Defaults) public static AppSettings LoadSettings() { try @@ -92,25 +242,87 @@ namespace Project_Periodensystem.Persistence { var json = File.ReadAllText(SettingsFile); var settings = JsonSerializer.Deserialize(json); - return settings ?? new AppSettings(); + + // Null-Coalescing für sichere Rückgabe + var result = settings ?? new AppSettings(); + Logger.Log($"Benutzereinstellungen geladen: Theme={result.LastTheme}"); + return result; } } catch (Exception ex) { Logger.LogException(ex, "LoadSettings"); + // Kein Re-throw - Fallback zu Defaults } + // Fallback zu Default-Settings + Logger.Log("Fallback zu Standard-Benutzereinstellungen"); return new AppSettings(); } } /// - /// Benutzereinstellungen für Persistenz + /// Datenmodell für persistierte Benutzereinstellungen + /// + /// ZWECK UND STRUKTUR: + /// - Kapselt alle persistierbaren Benutzer-Präferenzen + /// - Einfache Erweiterung um neue Settings möglich + /// - JSON-serialisierbar durch Auto-Properties + /// - Default-Werte für robuste Initialisierung + /// + /// DESIGN PRINCIPLES: + /// - Data Transfer Object (DTO): Reine Datenklasse + /// - Default Values: Sinnvolle Startwerte für neue Installationen + /// - Extensibility: Einfache Erweiterung um neue Properties + /// - Immutable nach Konstruktion: Properties könnten readonly sein + /// + /// C# KONZEPTE: + /// - Auto-Properties mit Default-Initialisierung + /// - DateTime für Zeitstempel + /// - String-Properties für einfache Serialisierung + /// - Parameterloser Constructor (implizit für JSON-Deserialisierung) + /// + /// JSON-SERIALISIERUNG: + /// - Alle Properties werden automatisch serialisiert + /// - Property-Namen werden als JSON-Keys verwendet + /// - Default-Werte sorgen für robuste Deserialisierung /// public class AppSettings { + /// + /// Zuletzt verwendetes UI-Theme + /// + /// WERTE: + /// - "Light": Helles Theme für Tag-Nutzung + /// - "Dark": Dunkles Theme für augen-schonende Nutzung + /// - "Auto": System-Theme folgen (Windows/macOS) + /// + /// DEFAULT: "Dark" - Moderner Standard für Entwicklungs-Tools + /// public string LastTheme { get; set; } = "Dark"; + + /// + /// Zeitstempel der letzten Anwendungsnutzung + /// + /// ZWECK: + /// - Nutzungsstatistiken + /// - "Willkommen zurück"-Funktionen + /// - Data Analytics für Verbesserungen + /// + /// DEFAULT: DateTime.Now - Aktueller Zeitpunkt bei Erstellung + /// public DateTime LastUsed { get; set; } = DateTime.Now; + + /// + /// Bevorzugte Sprache der Benutzeroberfläche + /// + /// WERTE: + /// - "German": Deutsche Lokalisierung + /// - "English": Englische Lokalisierung + /// - "French": Französische Lokalisierung (wenn implementiert) + /// + /// DEFAULT: "German" - Da es ein deutsches Lernprojekt ist + /// public string PreferredLanguage { get; set; } = "German"; } } \ No newline at end of file diff --git a/Project_Periodensystem.Persistence/PeriodicTableData.cs b/Project_Periodensystem.Persistence/PeriodicTableData.cs index f3413c5..ff40ff7 100644 --- a/Project_Periodensystem.Persistence/PeriodicTableData.cs +++ b/Project_Periodensystem.Persistence/PeriodicTableData.cs @@ -3,31 +3,99 @@ using Project_Periodensystem.Model; namespace Project_Periodensystem.Persistence { + /// + /// Statische Datenquelle für alle chemischen Elemente des Periodensystems + /// + /// ZWECK UND DATENMODELL: + /// - Eingebaute, unveränderliche Datenbasis für das Periodensystem + /// - Fallback-Datenquelle wenn externe JSON-Dateien nicht verfügbar sind + /// - Demonstration großer statischer Datenstrukturen in C# + /// - Vollständige wissenschaftliche Elementdaten für Bildungszwecke + /// + /// DATENORGANISATION: + /// - 118 chemische Elemente (vollständiges Periodensystem bis 2023) + /// - Organisiert nach Perioden (Zeilen) für bessere Lesbarkeit + /// - Jedes Element mit 9 Eigenschaften (AtomicNumber bis Column) + /// - Wissenschaftlich korrekte Daten basierend auf IUPAC-Standards + /// + /// C# KONZEPTE: + /// - Static Class: Globaler Zugriff ohne Instanziierung + /// - Static Property: Elements als unveränderliche Datenquelle + /// - Collection Initializer: new List<Element> { ... } Syntax + /// - Object Initializer: new Element(...) für jeden Eintrag + /// - Large Data Arrays: Umgang mit umfangreichen Datensätzen + /// + /// DESIGN PATTERNS: + /// - Static Factory: Stellt vorgefertigte Element-Sammlung bereit + /// - Immutable Data: Nur Getter, keine Setter für Elements + /// - Reference Data: Unveränderliche Stammdaten + /// - Embedded Resource: Daten sind Teil der kompilierten Assembly + /// + /// WISSENSCHAFTLICHE GENAUIGKEIT: + /// - Atomgewichte: Relative Atommassen basierend auf IUPAC 2021 + /// - Elektronegativität: Pauling-Skala mit 2 Dezimalstellen + /// - Dichte: g/cm³ bei Standardbedingungen (20°C, 1 atm) + /// - Elementserien: Moderne Klassifikation nach chemischen Eigenschaften + /// - Positionen: Row/Column entsprechen Standard-Periodensystem-Layout + /// + /// WARTUNG UND ERWEITERUNG: + /// - Neue Elemente können einfach zur Liste hinzugefügt werden + /// - Datenformat ist konsistent und selbst-dokumentierend + /// - Kommentare gruppieren Elemente nach Perioden für Übersichtlichkeit + /// - Jeder Eintrag folgt dem gleichen Schema für Konsistenz + /// public static class PeriodicTableData { + /// + /// Vollständige Liste aller bekannten chemischen Elemente + /// + /// DATENSTRUKTUR: + /// - List<Element>: Dynamische Sammlung von Element-Objekten + /// - Static Property: Einmalige Initialisierung beim ersten Zugriff + /// - Read-Only: Nur Getter, keine externe Manipulation möglich + /// - Auto-Property: Compiler generiert backing field automatisch + /// + /// INITIALISIERUNG: + /// - Collection Initializer: Elegante Syntax für große Datenmengen + /// - Compile-Time: Alle Daten werden zur Compile-Zeit in Assembly eingebettet + /// - Memory Efficient: Daten werden nur einmal geladen und geteilt + /// + /// ZUGRIFF: + /// var elements = PeriodicTableData.Elements; + /// var hydrogen = elements.First(e => e.Symbol == "H"); + /// public static List Elements { get; } = new List { - // Erste Periode - new Element( 1, "H", "Wasserstoff", 1.008, 2.20, 0.000, "Nichtmetall", 0, 0), - new Element( 2, "He", "Helium", 4.003, 0.00, 0.001, "Edelgas", 0, 17), + // ===== ERSTE PERIODE ===== + // Die einfachsten Elemente: Wasserstoff und Helium + // Besonderheiten: Nur 1s-Orbitale gefüllt, einzigartige Eigenschaften + + new Element( 1, "H", "Wasserstoff", 1.008, 2.20, 0.000, "Nichtmetall", 0, 0), // Leichtestes Element, Grundbaustein des Universums + new Element( 2, "He", "Helium", 4.003, 0.00, 0.001, "Edelgas", 0, 17), // Edelgas mit vollständiger Schale, chemisch inert - // Zweite Periode - new Element( 3, "Li", "Lithium", 6.940, 0.98, 0.534, "Alkalimetall", 1, 0), - new Element( 4, "Be", "Beryllium", 9.012, 1.57, 1.850, "Erdalkalimetall", 1, 1), - new Element( 5, "B", "Bor", 10.810,2.04, 2.340, "Halbmetall", 1, 12), - new Element( 6, "C", "Kohlenstoff", 12.011,2.55, 2.267, "Nichtmetall", 1, 13), - new Element( 7, "N", "Stickstoff", 14.007,3.04, 0.001, "Nichtmetall", 1, 14), - new Element( 8, "O", "Sauerstoff", 15.999,3.44, 0.001, "Nichtmetall", 1, 15), - new Element( 9, "F", "Fluor", 18.998,3.98, 0.002, "Halogen", 1, 16), - new Element( 10, "Ne", "Neon", 20.180,0.00, 0.001, "Edelgas", 1, 17), + // ===== ZWEITE PERIODE ===== + // Elemente mit 2 Elektronenschalen (1s² + 2s/2p) + // Wichtige biologische und technische Elemente + + new Element( 3, "Li", "Lithium", 6.940, 0.98, 0.534, "Alkalimetall", 1, 0), // Leichtestes Metall, wichtig für Batterien + new Element( 4, "Be", "Beryllium", 9.012, 1.57, 1.850, "Erdalkalimetall", 1, 1), // Leicht aber giftig, Luft-/Raumfahrt + new Element( 5, "B", "Bor", 10.810,2.04, 2.340, "Halbmetall", 1, 12), // Grenze zwischen Metall/Nichtmetall + new Element( 6, "C", "Kohlenstoff", 12.011,2.55, 2.267, "Nichtmetall", 1, 13), // Basis allen Lebens, vielfältigste Chemie + new Element( 7, "N", "Stickstoff", 14.007,3.04, 0.001, "Nichtmetall", 1, 14), // 78% der Atmosphäre, wichtig für Proteine + new Element( 8, "O", "Sauerstoff", 15.999,3.44, 0.001, "Nichtmetall", 1, 15), // Lebenswichtig, 21% der Atmosphäre + new Element( 9, "F", "Fluor", 18.998,3.98, 0.002, "Halogen", 1, 16), // Höchste Elektronegativität, sehr reaktiv + new Element( 10, "Ne", "Neon", 20.180,0.00, 0.001, "Edelgas", 1, 17), // Leuchtreklame, chemisch inert - // Dritte Periode - new Element( 11, "Na", "Natrium", 22.990,0.93, 0.971, "Alkalimetall", 2, 0), - new Element( 12, "Mg", "Magnesium", 24.305,1.31, 1.738, "Erdalkalimetall", 2, 1), - new Element( 13, "Al", "Aluminium", 26.982,1.61, 2.698, "Post-Übergangsmetall",2, 12), - new Element( 14, "Si", "Silizium", 28.085,1.90, 2.329, "Halbmetall", 2, 13), - new Element( 15, "P", "Phosphor", 30.974,2.19, 1.820, "Nichtmetall", 2, 14), - new Element( 16, "S", "Schwefel", 32.060,2.58, 2.067, "Nichtmetall", 2, 15), + // ===== DRITTE PERIODE ===== + // Erste Periode mit d-Orbital-Möglichkeiten + // Viele technisch wichtige Elemente (Aluminium, Silizium) + + new Element( 11, "Na", "Natrium", 22.990,0.93, 0.971, "Alkalimetall", 2, 0), // Kochsalz, lebenswichtig für Nerven + new Element( 12, "Mg", "Magnesium", 24.305,1.31, 1.738, "Erdalkalimetall", 2, 1), // Chlorophyll-Zentrum, leichte Legierungen + new Element( 13, "Al", "Aluminium", 26.982,1.61, 2.698, "Post-Übergangsmetall",2, 12), // Häufigstes Metall der Erdkruste + new Element( 14, "Si", "Silizium", 28.085,1.90, 2.329, "Halbmetall", 2, 13), // Basis der Halbleiter-Technologie + new Element( 15, "P", "Phosphor", 30.974,2.19, 1.820, "Nichtmetall", 2, 14), // DNA/RNA, Energie-Übertragung (ATP) + new Element( 16, "S", "Schwefel", 32.060,2.58, 2.067, "Nichtmetall", 2, 15), // Proteine, Vulkanismus new Element( 17, "Cl", "Chlor", 35.450,3.16, 0.003, "Halogen", 2, 16), new Element( 18, "Ar", "Argon", 39.948,0.00, 0.002, "Edelgas", 2, 17), diff --git a/Project_Periodensystem.View/NavigationService.cs b/Project_Periodensystem.View/NavigationService.cs index 1f06609..9468956 100644 --- a/Project_Periodensystem.View/NavigationService.cs +++ b/Project_Periodensystem.View/NavigationService.cs @@ -8,30 +8,126 @@ using Project_Periodensystem.Controller; namespace Project_Periodensystem.View { /// - /// Navigation Service Implementation im View-Layer - /// Implementiert INavigationService Interface vom Controller - /// SAUBERE TRENNUNG: Controller definiert WAS, View definiert WIE + /// Concrete Implementation des Navigation Service für Avalonia UI + /// + /// ZWECK UND ARCHITEKTUR: + /// - Implementiert INavigationService Interface aus dem Controller-Layer + /// - Trennt sauber "WAS navigiert wird" (Controller) von "WIE navigiert wird" (View) + /// - Kapselt alle Avalonia-spezifische Navigation-Logik + /// - Ermöglicht verschiedene UI-Frameworks ohne Controller-Änderungen + /// + /// DESIGN PATTERNS: + /// - Interface Implementation: Konkrete Umsetzung der abstrakten Navigation + /// - Dependency Injection: MainWindow wird von außen injiziert + /// - Facade Pattern: Vereinfacht komplexe UI-Navigation für Controller + /// - Bridge Pattern: Verbindet Controller (Abstraktion) mit View (Implementation) + /// + /// C# KONZEPTE: + /// - Interface Implementation: public class NavigationService : INavigationService + /// - Constructor Injection: MainWindow als Dependency + /// - Null-Conditional Checks: Robuste Parameter-Validierung + /// - Exception Handling: Try-Catch für alle UI-Operationen + /// - Method Delegation: Weiterleitung von Interface-Aufrufen an UI-Code + /// + /// AVALONIA-INTEGRATION: + /// - MainWindow.Content: Zentrale Inhaltsbereich für Page-Wechsel + /// - UserControl-basierte Pages: PeriodicTablePage, AboutPage, LandingPage + /// - Theme-Management: Avalonia Application Resources + /// - Application.Current: Globaler Zugriff auf App-Instance + /// + /// ZIRKULÄRE ABHÄNGIGKEITEN: + /// - Problem: Controller braucht NavigationService, NavigationService braucht Controller + /// - Lösung: Zweistufige Initialisierung mit SetDataController() + /// - Constructor nimmt MainWindow, SetDataController() löst Zirkularität auf /// public class NavigationService : INavigationService { + // ===== PRIVATE FELDER ===== + + /// + /// Referenz zum Hauptfenster der Avalonia-Anwendung + /// + /// ZWECK: + /// - Zentrale Kontrolle über den Hauptinhaltsbereich + /// - Zugriff auf MainWindow.Content für Page-Wechsel + /// - Window-Eigenschaften (Titel, Größe, etc.) ändern + /// + /// C# KONZEPTE: + /// - Readonly Field: Kann nur im Konstruktor gesetzt werden + /// - Reference Type: Hält Verweis auf MainWindow-Objekt + /// - Encapsulation: Private field mit controlled access + /// private readonly MainWindow _mainWindow; + + /// + /// Referenz zum Data Controller (für Daten-Zugriff) + /// + /// ZWECK: + /// - Navigation-Pages benötigen Zugriff auf Element-Daten + /// - Vermeidung direkter Model/Persistence-Zugriffe aus View + /// - Konsistente Daten-API für alle UI-Komponenten + /// + /// ZIRKULÄRE ABHÄNGIGKEIT: + /// - Controller erstellt NavigationService + /// - NavigationService braucht Controller für Daten + /// - Lösung: Nullable field + SetDataController() nach Konstruktion + /// private PeriodensystemController? _dataController; + // ===== KONSTRUKTOR UND INITIALISIERUNG ===== + /// - /// Konstruktor + /// Konstruktor mit Dependency Injection des MainWindow + /// + /// ZWECK: + /// - Initialisiert Navigation Service mit UI-Kontext + /// - Stellt sicher, dass MainWindow verfügbar ist + /// - Validiert kritische Dependencies + /// + /// C# KONZEPTE: + /// - Constructor Dependency Injection: MainWindow als Parameter + /// - ArgumentNullException: Robuste Parameter-Validierung + /// - Null-Coalescing Throw: mainWindow ?? throw new ArgumentNullException() + /// - Self-Documenting Code: Logging der Initialisierung + /// + /// PARAMETER-VALIDIERUNG: + /// - Null-Check mit Exception: Verhindert spätere NullReferenceExceptions + /// - Early Fail Principle: Probleme sofort erkennbar + /// - Descriptive Exception: nameof() für präzise Fehlermeldung /// + /// Das Hauptfenster der Avalonia-Anwendung + /// Wenn mainWindow null ist public NavigationService(MainWindow mainWindow) { _mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow)); - Logger.Log("NavigationService initialisiert - saubere Interface-Trennung"); + Logger.Log("NavigationService initialisiert - implementiert Interface-basierte Navigation"); } /// - /// Data Controller setzen (löst zirkuläre Abhängigkeit) + /// Setzt den Data Controller nach der Konstruktion (löst zirkuläre Abhängigkeit) + /// + /// ZWECK: + /// - Zweistufige Initialisierung zur Auflösung zirkulärer Dependencies + /// - Ermöglicht Controller-Access für datenabhängige Navigation + /// - Trennt UI-Initialisierung von Daten-Initialisierung + /// + /// ZIRKULÄRES ABHÄNGIGKEITS-PROBLEM: + /// 1. MainWindow erstellt PeriodensystemController + /// 2. Controller braucht NavigationService (this) + /// 3. NavigationService braucht Controller für Daten + /// 4. Lösung: Controller nach NavigationService-Konstruktion setzen + /// + /// C# KONZEPTE: + /// - Two-Phase Construction: Konstruktor + Setter für komplexe Dependencies + /// - Null-Conditional Assignment: _dataController wird aus null zu gültigem Objekt + /// - Method Chaining möglich: SetDataController() könnte 'this' zurückgeben /// + /// Der PeriodensystemController für Datenzugriff + /// Wenn dataController null ist public void SetDataController(PeriodensystemController dataController) { _dataController = dataController ?? throw new ArgumentNullException(nameof(dataController)); + Logger.Log("DataController in NavigationService gesetzt - zirkuläre Abhängigkeit aufgelöst"); } /// diff --git a/Project_Periodensystem.View/PeriodicTablePage.axaml.cs b/Project_Periodensystem.View/PeriodicTablePage.axaml.cs index 794754a..eef5190 100644 --- a/Project_Periodensystem.View/PeriodicTablePage.axaml.cs +++ b/Project_Periodensystem.View/PeriodicTablePage.axaml.cs @@ -37,21 +37,13 @@ namespace Project_Periodensystem.View periodicGrid = this.FindControl("PeriodicGrid"); } - /// - /// Controller setzen (Dependency Injection für MVC) - /// public void SetController(PeriodensystemController controller) { _controller = controller ?? throw new ArgumentNullException(nameof(controller)); Logger.Log("PeriodicTablePage: Controller gesetzt (MVC-Pattern)"); - - // Elemente laden und anzeigen LoadAndDisplayElements(); } - /// - /// Lädt Elemente über Controller und zeigt sie an - /// private void LoadAndDisplayElements() { try @@ -62,7 +54,6 @@ namespace Project_Periodensystem.View return; } - // Daten über Controller laden (nicht direkt!) var elements = _controller.GetAllElements(); if (!elements.Any()) @@ -71,7 +62,6 @@ namespace Project_Periodensystem.View return; } - // Datenvalidierung über Controller if (!_controller.ValidateData()) { Logger.Log("Datenvalidierung fehlgeschlagen"); @@ -80,7 +70,6 @@ namespace Project_Periodensystem.View Logger.Log($"Lade {elements.Count} Elemente in das Grid"); - // UI-Update für jedes Element int successCount = 0; foreach (var element in elements) { @@ -96,8 +85,6 @@ namespace Project_Periodensystem.View } Logger.Log($"{successCount} von {elements.Count} Elementen erfolgreich geladen"); - - // Legend-Buttons sammeln für Highlighting-Funktionalität CollectLegendButtons(); } catch (Exception ex) @@ -106,10 +93,6 @@ namespace Project_Periodensystem.View } } - /// - /// Erstellt UI-Button für ein Element (nur View-Logik) - /// - /// Element für das der Button erstellt wird private void CreateElementButton(Element element) { if (element == null) @@ -118,15 +101,12 @@ namespace Project_Periodensystem.View return; } - // UI-Komponenten erstellen var button = new Button { Classes = { "ElementTile" } }; var panel = new StackPanel(); - // Button-Referenz für Highlighting speichern - button.Tag = element; // Element-Daten im Tag speichern + button.Tag = element; _elementButtons.Add(button); - // Hintergrundfarbe über Converter setzen var backgroundColor = new SeriesToColorConverter() .Convert(element.Series, typeof(Brush), null, CultureInfo.InvariantCulture) as Brush; @@ -135,7 +115,6 @@ namespace Project_Periodensystem.View button.Background = backgroundColor; } - // Text-Elemente erstellen var symbolText = new TextBlock { Text = element.Symbol, @@ -152,12 +131,10 @@ namespace Project_Periodensystem.View HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center }; - // Layout zusammenbauen - panel.Children.Add(numberText); // Number on top - panel.Children.Add(symbolText); // Symbol below + panel.Children.Add(numberText); + panel.Children.Add(symbolText); button.Content = panel; - // Grid-Position setzen (0-basiert) int gridRow = element.Row; int gridColumn = element.Column; @@ -166,7 +143,6 @@ namespace Project_Periodensystem.View Grid.SetRow(button, gridRow); Grid.SetColumn(button, gridColumn); - // Button zum Grid hinzufügen if (periodicGrid != null) { periodicGrid.Children.Add(button);