Code bereinigt und kommentiert
This commit is contained in:
parent
e4aa132c79
commit
93b5502112
115
Project_Periodensystem.Controller/ElementStatistics.cs
Normal file
115
Project_Periodensystem.Controller/ElementStatistics.cs
Normal file
@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Project_Periodensystem.Model;
|
||||
|
||||
namespace Project_Periodensystem.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class ElementStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private readonly List<Element> _elements;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="elements">Liste der Elemente für statistische Auswertungen (kann null sein)</param>
|
||||
public ElementStatistics(List<Element> elements)
|
||||
{
|
||||
// Null-Safety: Falls null übergeben wird, erstelle eine leere Liste
|
||||
// Verhindert NullReferenceExceptions in allen nachfolgenden Methoden
|
||||
_elements = elements ?? new List<Element>();
|
||||
}
|
||||
|
||||
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<string> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Project_Periodensystem.Controller/ElementValidator.cs
Normal file
157
Project_Periodensystem.Controller/ElementValidator.cs
Normal file
@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Project_Periodensystem.Model;
|
||||
|
||||
namespace Project_Periodensystem.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class ElementValidator
|
||||
{
|
||||
// Private readonly Liste der zu validierenden Elemente
|
||||
// readonly = kann nach Initialisierung nicht mehr geändert werden
|
||||
private readonly List<Element> _elements;
|
||||
|
||||
/// <summary>
|
||||
/// Konstruktor - Initialisiert den Validator mit einer Element-Liste
|
||||
/// </summary>
|
||||
/// <param name="elements">Liste der zu validierenden Elemente</param>
|
||||
public ElementValidator(List<Element> elements)
|
||||
{
|
||||
// Null-Check mit Fallback zu leerer Liste - verhindert NullReferenceExceptions
|
||||
_elements = elements ?? new List<Element>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>true = alle Validierungen bestanden, false = Fehler gefunden</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="element">Das zu prüfende Element (kann null sein)</param>
|
||||
/// <returns>true = Position ist gültig, false = Position ungültig oder Element ist null</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,15 +3,86 @@ using System;
|
||||
namespace Project_Periodensystem.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public interface INavigationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Navigiert zur Hauptansicht des Periodensystems
|
||||
///
|
||||
/// IMPLEMENTIERUNG:
|
||||
/// - Zeigt das vollständige, interaktive Periodensystem an
|
||||
/// - Hauptfunktion der Anwendung
|
||||
/// - Alle Element-Tiles werden geladen und angezeigt
|
||||
/// </summary>
|
||||
void NavigateToPeriodicTable();
|
||||
|
||||
/// <summary>
|
||||
/// Navigiert zur About/Info-Seite der Anwendung
|
||||
///
|
||||
/// IMPLEMENTIERUNG:
|
||||
/// - Zeigt Anwendungsinformationen, Version, Credits
|
||||
/// - Dokumentation und Hilfe-Inhalte
|
||||
/// - Links zu weiteren Ressourcen
|
||||
/// </summary>
|
||||
void NavigateToAbout();
|
||||
|
||||
/// <summary>
|
||||
/// Navigiert zurück zur Startseite/Hauptmenü
|
||||
///
|
||||
/// IMPLEMENTIERUNG:
|
||||
/// - Zeigt das Hauptmenü mit Navigation-Optionen
|
||||
/// - Reset-Funktion zum Anwendungsstart
|
||||
/// - Home-Button Funktionalität
|
||||
/// </summary>
|
||||
void NavigateToLanding();
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
void ToggleTheme();
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
void ShowExportConfirmation();
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,48 +7,131 @@ using Project_Periodensystem.Persistence;
|
||||
namespace Project_Periodensystem.Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class PeriodensystemController
|
||||
{
|
||||
// Nullable Field um Warning zu vermeiden
|
||||
private List<Element> _elements = new List<Element>();
|
||||
|
||||
// Navigation Service Interface (KEINE direkte UI-Dependency!)
|
||||
private readonly INavigationService? _navigationService;
|
||||
// ===== PRIVATE FELDER =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private List<Element> _elements = new List<Element>();
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
private readonly INavigationService? _navigationService;
|
||||
|
||||
// ===== HELPER-KLASSEN FÜR CODE-ORGANISATION =====
|
||||
|
||||
/// <summary>
|
||||
/// Spezialisierte Klasse für Validierungslogik
|
||||
/// Nullable: Wird erst nach dem Laden der Elemente initialisiert
|
||||
/// Separation of Concerns: Validierung ist getrennt von anderen Aufgaben
|
||||
/// </summary>
|
||||
private ElementValidator? _validator;
|
||||
|
||||
/// <summary>
|
||||
/// Spezialisierte Klasse für statistische Berechnungen
|
||||
/// Nullable: Wird erst nach dem Laden der Elemente initialisiert
|
||||
/// Single Responsibility: Nur für Statistiken zuständig
|
||||
/// </summary>
|
||||
private ElementStatistics? _statistics;
|
||||
|
||||
// ===== KONSTRUKTOR =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public PeriodensystemController(INavigationService? navigationService = null)
|
||||
{
|
||||
_navigationService = navigationService;
|
||||
LoadElements();
|
||||
LoadElements(); // Daten sofort beim Erstellen laden
|
||||
}
|
||||
|
||||
// ===== PRIVATE INITIALISIERUNGSMETHODEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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<Element>();
|
||||
|
||||
// Helper-Klassen können nicht initialisiert werden (bleiben null)
|
||||
_validator = null;
|
||||
_statistics = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PUBLIC API METHODEN - ELEMENTZUGRIFF =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>Liste aller Elemente</returns>
|
||||
public List<Element> GetAllElements()
|
||||
{
|
||||
return _elements;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>Array aller Elemente</returns>
|
||||
public Element[] GetAllElementsAsArray()
|
||||
{
|
||||
return _elements.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>Array mit allen Element-Symbolen</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt Atomgewichte als Array zurück
|
||||
/// </summary>
|
||||
/// <returns>Array mit allen Atomgewichten</returns>
|
||||
public double[] GetAtomicWeights()
|
||||
{
|
||||
double[] weights = new double[_elements.Count];
|
||||
for (int i = 0; i < _elements.Count; i++)
|
||||
{
|
||||
weights[i] = _elements[i].AtomicWeight;
|
||||
}
|
||||
return weights;
|
||||
}
|
||||
// ===== DELEGATION AN HELPER-KLASSEN =====
|
||||
|
||||
/// <summary>
|
||||
/// Berechnet Durchschnittsgewicht aus Array
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>Durchschnittliches Atomgewicht</returns>
|
||||
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;
|
||||
}
|
||||
// ===== VALIDIERUNGS-METHODEN (delegiert an ElementValidator) =====
|
||||
|
||||
/// <summary>
|
||||
/// Speichert Elemente persistent (NEUE FUNKTION)
|
||||
/// Validiert die gesamte Element-Datenbasis
|
||||
/// Delegation an ElementValidator.ValidateData()
|
||||
/// Fallback: false bei null-Validator
|
||||
/// </summary>
|
||||
public void SaveElements()
|
||||
{
|
||||
try
|
||||
{
|
||||
DataManager.SaveElements(_elements);
|
||||
Logger.Log("Elemente erfolgreich gespeichert");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogException(ex, "SaveElements");
|
||||
}
|
||||
}
|
||||
public bool ValidateData() => _validator?.ValidateData() ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// Sucht ein Element nach Atomnummer
|
||||
/// Validiert die Position eines spezifischen Elements im Periodensystem
|
||||
/// Delegation an ElementValidator.ValidateElementPosition()
|
||||
/// Fallback: false bei null-Validator
|
||||
/// </summary>
|
||||
public bool ValidateElementPosition(Element? element) => _validator?.ValidateElementPosition(element) ?? false;
|
||||
|
||||
// ===== STATISTIK-METHODEN (delegiert an ElementStatistics) =====
|
||||
|
||||
/// <summary>
|
||||
/// Extrahiert alle Atomgewichte als Array
|
||||
/// Delegation an ElementStatistics.GetAtomicWeights()
|
||||
/// Fallback: leeres Array bei null-Statistics
|
||||
/// </summary>
|
||||
public double[] GetAtomicWeights() => _statistics?.GetAtomicWeights() ?? new double[0];
|
||||
|
||||
/// <summary>
|
||||
/// Berechnet das durchschnittliche Atomgewicht
|
||||
/// Delegation an ElementStatistics.GetAverageAtomicWeight()
|
||||
/// Fallback: 0.0 bei null-Statistics
|
||||
/// </summary>
|
||||
public double GetAverageAtomicWeight() => _statistics?.GetAverageAtomicWeight() ?? 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Sammelt alle einzigartigen Element-Serien
|
||||
/// Delegation an ElementStatistics.GetAllSeries()
|
||||
/// Fallback: leere Liste bei null-Statistics
|
||||
/// </summary>
|
||||
public List<string> GetAllSeries() => _statistics?.GetAllSeries() ?? new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public (int totalElements, int uniqueSeries, double avgWeight) GetStatistics() => _statistics?.GetStatistics() ?? (0, 0, 0.0);
|
||||
|
||||
/// <summary>
|
||||
/// Findet das leichteste und schwerste Element
|
||||
/// Tuple mit nullable Elementen: (Element?, Element?)
|
||||
/// Delegation an ElementStatistics.GetWeightExtremes()
|
||||
/// Fallback: (null, null) bei null-Statistics
|
||||
/// </summary>
|
||||
public (Element? lightest, Element? heaviest) GetWeightExtremes() => _statistics?.GetWeightExtremes() ?? (null, null);
|
||||
|
||||
/// <summary>
|
||||
/// Extrahiert grundlegende Informationen eines Elements
|
||||
/// Tuple mit gemischten Typen: (string, string, double)
|
||||
/// Delegation an ElementStatistics.GetElementInfo()
|
||||
/// Fallback: ("?", "Unknown", 0.0) bei null-Statistics
|
||||
/// </summary>
|
||||
public (string symbol, string name, double weight) GetElementInfo(int atomicNumber) => _statistics?.GetElementInfo(atomicNumber) ?? ("?", "Unknown", 0.0);
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public (int row, int column) GetElementPosition(int atomicNumber) => _statistics?.GetElementPosition(atomicNumber) ?? (-1, -1);
|
||||
|
||||
// ===== ELEMENT-SUCHFUNKTIONEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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 */ }
|
||||
/// </summary>
|
||||
/// <param name="atomicNumber">Atomnummer des gesuchten Elements</param>
|
||||
/// <returns>Element oder null wenn nicht gefunden</returns>
|
||||
public Element? GetElementByAtomicNumber(int atomicNumber)
|
||||
{
|
||||
if (atomicNumber <= 0)
|
||||
{
|
||||
Logger.Log($"Ungültige Atomnummer: {atomicNumber}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="symbol">Symbol des gesuchten Elements</param>
|
||||
/// <returns>Element oder null wenn nicht gefunden</returns>
|
||||
public Element? GetElementBySymbol(string symbol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(symbol))
|
||||
{
|
||||
Logger.Log("Leeres Symbol übergeben");
|
||||
return null;
|
||||
}
|
||||
// Guard Clause: Ungültige Eingaben frühzeitig abfangen
|
||||
if (string.IsNullOrWhiteSpace(symbol)) return null;
|
||||
|
||||
return _elements.FirstOrDefault(e =>
|
||||
string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase));
|
||||
// Case-insensitive Suche für bessere Benutzerfreundlichkeit
|
||||
return _elements.FirstOrDefault(e => string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="series">Gewünschte Elementserie</param>
|
||||
/// <returns>Liste der Elemente der angegebenen Serie</returns>
|
||||
public List<Element> GetElementsBySeries(string series)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(series))
|
||||
{
|
||||
return new List<Element>();
|
||||
// Guard Clause: Ungültige Eingaben behandeln
|
||||
if (string.IsNullOrWhiteSpace(series)) return new List<Element>();
|
||||
|
||||
// LINQ-Filterung mit case-insensitive Vergleich
|
||||
return _elements.Where(e => string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
}
|
||||
|
||||
return _elements.Where(e =>
|
||||
string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
}
|
||||
// ===== DATEN-MANAGEMENT =====
|
||||
|
||||
/// <summary>
|
||||
/// Validiert Grid-Position eines Elements
|
||||
/// </summary>
|
||||
/// <param name="element">Zu validierendes Element</param>
|
||||
/// <returns>True wenn Position gültig ist</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt alle verfügbaren Element-Serien zurück
|
||||
/// </summary>
|
||||
/// <returns>Liste aller Serien</returns>
|
||||
public List<string> GetAllSeries()
|
||||
{
|
||||
return _elements.Select(e => e.Series)
|
||||
.Distinct()
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))
|
||||
.OrderBy(s => s)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Überprüft ob alle Elemente korrekt geladen wurden
|
||||
/// </summary>
|
||||
/// <returns>True wenn Daten vollständig sind</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public void RefreshData()
|
||||
{
|
||||
Logger.Log("Daten werden neu geladen...");
|
||||
LoadElements();
|
||||
LoadElements(); // Vollständige Reinitialisierung
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
/// <returns>Anzahl der Elemente</returns>
|
||||
public int GetElementCount()
|
||||
{
|
||||
return _elements.Count;
|
||||
}
|
||||
public int GetElementCount() => _elements.Count;
|
||||
|
||||
// ===== NAVIGATION CONTROLLER METHODEN =====
|
||||
|
||||
/// <summary>
|
||||
/// Gibt Element-Position als Tuple zurück
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="atomicNumber">Atomnummer des Elements</param>
|
||||
/// <returns>Tuple mit (Row, Column) oder (-1, -1) wenn nicht gefunden</returns>
|
||||
public (int row, int column) GetElementPosition(int atomicNumber)
|
||||
{
|
||||
var element = GetElementByAtomicNumber(atomicNumber);
|
||||
if (element != null)
|
||||
{
|
||||
return (element.Row, element.Column);
|
||||
}
|
||||
return (-1, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt Grundinfos eines Elements als Tuple zurück
|
||||
/// </summary>
|
||||
/// <param name="atomicNumber">Atomnummer</param>
|
||||
/// <returns>Tuple mit (Symbol, Name, Gewicht)</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt Statistiken als Tuple zurück
|
||||
/// </summary>
|
||||
/// <returns>Tuple mit (Anzahl Elemente, Anzahl Serien, Durchschnittsgewicht)</returns>
|
||||
public (int totalElements, int uniqueSeries, double avgWeight) GetStatistics()
|
||||
{
|
||||
var seriesCount = GetAllSeries().Count;
|
||||
var avgWeight = GetAverageAtomicWeight();
|
||||
|
||||
return (_elements.Count, seriesCount, avgWeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Findet leichtestes und schwerstes Element
|
||||
/// </summary>
|
||||
/// <returns>Tuple mit (leichtestes Element, schwerstes Element) oder null-Werte wenn keine Elemente</returns>
|
||||
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) =====
|
||||
|
||||
/// <summary>
|
||||
/// Behandelt Navigation zum Periodensystem (Controller-Logic)
|
||||
/// 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
|
||||
/// </summary>
|
||||
public void HandleNavigateToPeriodicTable()
|
||||
{
|
||||
@ -371,7 +502,12 @@ namespace Project_Periodensystem.Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Behandelt Navigation zur About-Seite (Controller-Logic)
|
||||
/// Navigiert zur Informationsseite über die Anwendung
|
||||
///
|
||||
/// ZWECK:
|
||||
/// - Anzeige von Anwendungsinformationen
|
||||
/// - Credits und Versionsinformationen
|
||||
/// - Hilfe und Dokumentation
|
||||
/// </summary>
|
||||
public void HandleNavigateToAbout()
|
||||
{
|
||||
@ -387,7 +523,12 @@ namespace Project_Periodensystem.Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public void HandleNavigateToLanding()
|
||||
{
|
||||
@ -403,7 +544,16 @@ namespace Project_Periodensystem.Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public void HandleToggleTheme()
|
||||
{
|
||||
@ -419,17 +569,23 @@ namespace Project_Periodensystem.Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public void HandleExportData()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Log("Controller: Daten-Export angefordert");
|
||||
|
||||
// Geschäftslogik für Export
|
||||
SaveElements(); // Persistierung
|
||||
|
||||
_navigationService?.ShowExportConfirmation();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -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.
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class Element
|
||||
{
|
||||
// Ordnungszahl des Elements (z. B. 1 für Wasserstoff)
|
||||
// ===== IDENTIFIKATIONS-EIGENSCHAFTEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
public int AtomicNumber { get; set; }
|
||||
|
||||
// Chemisches Symbol des Elements (z. B. "H" für Wasserstoff)
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
public string Symbol { get; set; }
|
||||
|
||||
// Vollständiger Name des Elements (z. B. "Hydrogen")
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// - "Einsteinium" (nach Albert Einstein)
|
||||
/// </summary>
|
||||
public string ElementName { get; set; }
|
||||
|
||||
// Atommasse (mittlere Masse eines Atoms in u)
|
||||
// ===== PHYSIKALISCHE EIGENSCHAFTEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public double AtomicWeight { get; set; }
|
||||
|
||||
// Elektronegativität nach Pauling-Skala (z. B. 2.1)
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
public double Electronegativity { get; set; }
|
||||
|
||||
// Dichte des Elements in g/cm³
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
public double Density { get; set; }
|
||||
|
||||
// Serie oder Gruppe, zu der das Element gehört (z. B. "Halogen", "Alkalimetall")
|
||||
// ===== KLASSIFIKATIONS-EIGENSCHAFTEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public string Series { get; set; }
|
||||
|
||||
// ===== POSITIONIERUNGS-EIGENSCHAFTEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public int Row { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public int Column { get; set; }
|
||||
|
||||
// Konstruktor: Erzeugt ein neues Element-Objekt mit allen relevanten Eigenschaften.
|
||||
// ===== KONSTRUKTOR =====
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,68 @@ using System.Linq;
|
||||
namespace Project_Periodensystem.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public static class Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private static readonly string LogFilePath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Periodensystem", "app.log");
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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 =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="message">Zu protokollierende Nachricht</param>
|
||||
/// <param name="message">Zu protokollierende Nachricht (wird validiert)</param>
|
||||
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 =====
|
||||
|
||||
/// <summary>
|
||||
/// 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"); }
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception die protokolliert werden soll</param>
|
||||
/// <param name="context">Zusätzlicher Kontext</param>
|
||||
/// <param name="context">Zusätzlicher Kontext (Methodenname, Operation, etc.)</param>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="message">Warnung</param>
|
||||
/// <param name="message">Warnung (wird mit WARNING: Präfix versehen)</param>
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
Log($"WARNING: {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="message">Fehlermeldung</param>
|
||||
/// <param name="message">Fehlermeldung (wird mit ERROR: Präfix versehen)</param>
|
||||
public static void LogError(string message)
|
||||
{
|
||||
Log($"ERROR: {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="message">Debug-Nachricht</param>
|
||||
/// <param name="message">Debug-Nachricht (wird mit DEBUG: Präfix versehen)</param>
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
public static void LogDebug(string message)
|
||||
{
|
||||
Log($"DEBUG: {message}");
|
||||
}
|
||||
|
||||
// ===== DATEI-MANAGEMENT UND UTILITY-METHODEN =====
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>Vollständiger Pfad zur Log-Datei</returns>
|
||||
/// <returns>Vollständiger Pfad zur Log-Datei (absoluter Pfad)</returns>
|
||||
public static string GetLogFilePath()
|
||||
{
|
||||
return LogFilePath;
|
||||
}
|
||||
|
||||
// ===== SPEZIALISIERTE DATENTYP-LOGGING (C# LERNZWECK) =====
|
||||
|
||||
/// <summary>
|
||||
/// 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]"
|
||||
/// </summary>
|
||||
/// <param name="title">Titel für das Array</param>
|
||||
/// <param name="items">Array von Items</param>
|
||||
/// <param name="title">Beschreibender Titel für das Array</param>
|
||||
/// <param name="items">Array von Strings zur Anzeige</param>
|
||||
public static void LogArray(string title, string[] items)
|
||||
{
|
||||
Log($"{title}: [{string.Join(", ", items)}]");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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]"
|
||||
/// </summary>
|
||||
/// <param name="title">Titel für das Array</param>
|
||||
/// <param name="numbers">Array von Zahlen</param>
|
||||
/// <param name="title">Beschreibender Titel für das Array</param>
|
||||
/// <param name="numbers">Array von Double-Werten zur Anzeige</param>
|
||||
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)}]");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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"
|
||||
/// </summary>
|
||||
/// <param name="title">Titel</param>
|
||||
/// <param name="tupleInfo">Tuple als string</param>
|
||||
/// <param name="title">Beschreibender Titel für das Tuple</param>
|
||||
/// <param name="tupleInfo">Bereits formatierte Tuple-Darstellung als String</param>
|
||||
public static void LogTuple(string title, string tupleInfo)
|
||||
{
|
||||
Log($"{title}: {tupleInfo}");
|
||||
|
||||
@ -7,83 +7,233 @@ using Project_Periodensystem.Model;
|
||||
namespace Project_Periodensystem.Persistence
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public static class DataManager
|
||||
{
|
||||
// ===== PFAD-KONFIGURATION =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private static readonly string DataDirectory = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"Periodensystem");
|
||||
|
||||
/// <summary>
|
||||
/// Vollständiger Pfad zur Element-Datenbasis (JSON)
|
||||
/// Speichert alle chemischen Elemente mit ihren Eigenschaften
|
||||
/// </summary>
|
||||
private static readonly string ElementsFile = Path.Combine(DataDirectory, "elements.json");
|
||||
private static readonly string SettingsFile = Path.Combine(DataDirectory, "settings.json");
|
||||
|
||||
/// <summary>
|
||||
/// Speichert Elemente als JSON
|
||||
/// Vollständiger Pfad zu Benutzereinstellungen (JSON)
|
||||
/// Speichert Präferenzen wie Theme, Sprache, letzte Nutzung
|
||||
/// </summary>
|
||||
private static readonly string SettingsFile = Path.Combine(DataDirectory, "settings.json");
|
||||
|
||||
// ===== ELEMENT-DATEN PERSISTENZ =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="elements">Liste der zu speichernden Elemente</param>
|
||||
public static void SaveElements(List<Element> 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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>Liste aller verfügbaren chemischen Elemente</returns>
|
||||
public static List<Element> LoadElements()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prüfung ob benutzerdefinierte Daten existieren
|
||||
if (File.Exists(ElementsFile))
|
||||
{
|
||||
var json = File.ReadAllText(ElementsFile);
|
||||
var elements = JsonSerializer.Deserialize<List<Element>>(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 =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="settings">AppSettings-Objekt mit Benutzer-Präferenzen</param>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns>AppSettings-Objekt (entweder geladen oder mit Defaults)</returns>
|
||||
public static AppSettings LoadSettings()
|
||||
{
|
||||
try
|
||||
@ -92,25 +242,87 @@ namespace Project_Periodensystem.Persistence
|
||||
{
|
||||
var json = File.ReadAllText(SettingsFile);
|
||||
var settings = JsonSerializer.Deserialize<AppSettings>(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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class AppSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public string LastTheme { get; set; } = "Dark";
|
||||
|
||||
/// <summary>
|
||||
/// Zeitstempel der letzten Anwendungsnutzung
|
||||
///
|
||||
/// ZWECK:
|
||||
/// - Nutzungsstatistiken
|
||||
/// - "Willkommen zurück"-Funktionen
|
||||
/// - Data Analytics für Verbesserungen
|
||||
///
|
||||
/// DEFAULT: DateTime.Now - Aktueller Zeitpunkt bei Erstellung
|
||||
/// </summary>
|
||||
public DateTime LastUsed { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public string PreferredLanguage { get; set; } = "German";
|
||||
}
|
||||
}
|
||||
@ -3,31 +3,99 @@ using Project_Periodensystem.Model;
|
||||
|
||||
namespace Project_Periodensystem.Persistence
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public static class PeriodicTableData
|
||||
{
|
||||
/// <summary>
|
||||
/// 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");
|
||||
/// </summary>
|
||||
public static List<Element> Elements { get; } = new List<Element>
|
||||
{
|
||||
// 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
|
||||
|
||||
// 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),
|
||||
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
|
||||
|
||||
// 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),
|
||||
// ===== 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 =====
|
||||
// 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),
|
||||
|
||||
|
||||
@ -8,30 +8,126 @@ using Project_Periodensystem.Controller;
|
||||
namespace Project_Periodensystem.View
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public class NavigationService : INavigationService
|
||||
{
|
||||
private readonly MainWindow _mainWindow;
|
||||
private PeriodensystemController? _dataController;
|
||||
// ===== PRIVATE FELDER =====
|
||||
|
||||
/// <summary>
|
||||
/// Konstruktor
|
||||
/// 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
|
||||
/// </summary>
|
||||
private readonly MainWindow _mainWindow;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private PeriodensystemController? _dataController;
|
||||
|
||||
// ===== KONSTRUKTOR UND INITIALISIERUNG =====
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="mainWindow">Das Hauptfenster der Avalonia-Anwendung</param>
|
||||
/// <exception cref="ArgumentNullException">Wenn mainWindow null ist</exception>
|
||||
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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="dataController">Der PeriodensystemController für Datenzugriff</param>
|
||||
/// <exception cref="ArgumentNullException">Wenn dataController null ist</exception>
|
||||
public void SetDataController(PeriodensystemController dataController)
|
||||
{
|
||||
_dataController = dataController ?? throw new ArgumentNullException(nameof(dataController));
|
||||
Logger.Log("DataController in NavigationService gesetzt - zirkuläre Abhängigkeit aufgelöst");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -37,21 +37,13 @@ namespace Project_Periodensystem.View
|
||||
periodicGrid = this.FindControl<Grid>("PeriodicGrid");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controller setzen (Dependency Injection für MVC)
|
||||
/// </summary>
|
||||
public void SetController(PeriodensystemController controller)
|
||||
{
|
||||
_controller = controller ?? throw new ArgumentNullException(nameof(controller));
|
||||
Logger.Log("PeriodicTablePage: Controller gesetzt (MVC-Pattern)");
|
||||
|
||||
// Elemente laden und anzeigen
|
||||
LoadAndDisplayElements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lädt Elemente über Controller und zeigt sie an
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erstellt UI-Button für ein Element (nur View-Logik)
|
||||
/// </summary>
|
||||
/// <param name="element">Element für das der Button erstellt wird</param>
|
||||
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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user