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
|
namespace Project_Periodensystem.Controller
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface für Navigation - trennt Controller von UI-Dependencies
|
/// Navigation Service Interface - Implementiert Dependency Inversion Principle
|
||||||
/// Controller definiert WAS navigiert wird, View definiert WIE
|
///
|
||||||
|
/// 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>
|
/// </summary>
|
||||||
public interface INavigationService
|
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();
|
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();
|
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();
|
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();
|
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();
|
void ShowExportConfirmation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,48 +7,131 @@ using Project_Periodensystem.Persistence;
|
|||||||
namespace Project_Periodensystem.Controller
|
namespace Project_Periodensystem.Controller
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controller für das Periodensystem - verwaltet die Geschäftslogik
|
/// Haupt-Controller für das Periodensystem - implementiert das MVC-Pattern
|
||||||
/// und trennt View von Model gemäß MVC-Pattern
|
///
|
||||||
/// SAUBERE ARCHITEKTUR: Verwendet Interface für Navigation
|
/// 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>
|
/// </summary>
|
||||||
public class PeriodensystemController
|
public class PeriodensystemController
|
||||||
{
|
{
|
||||||
// Nullable Field um Warning zu vermeiden
|
// ===== PRIVATE FELDER =====
|
||||||
private List<Element> _elements = new List<Element>();
|
|
||||||
|
|
||||||
// Navigation Service Interface (KEINE direkte UI-Dependency!)
|
|
||||||
private readonly INavigationService? _navigationService;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public PeriodensystemController(INavigationService? navigationService = null)
|
public PeriodensystemController(INavigationService? navigationService = null)
|
||||||
{
|
{
|
||||||
_navigationService = navigationService;
|
_navigationService = navigationService;
|
||||||
LoadElements();
|
LoadElements(); // Daten sofort beim Erstellen laden
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== PRIVATE INITIALISIERUNGSMETHODEN =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
private void LoadElements()
|
private void LoadElements()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// NEUE METHODE: DataManager statt direkt PeriodicTableData
|
// Elemente aus DataManager laden (Persistence Layer)
|
||||||
_elements = DataManager.LoadElements();
|
_elements = DataManager.LoadElements();
|
||||||
Logger.Log($"Controller: {_elements.Count} Elemente erfolgreich geladen (via DataManager)");
|
Logger.Log($"Controller: {_elements.Count} Elemente erfolgreich geladen (via DataManager)");
|
||||||
|
|
||||||
// NEUE FEATURES demonstrieren:
|
// Helper-Klassen mit den geladenen Daten initialisieren
|
||||||
// Array-Operationen testen
|
// 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();
|
var symbols = GetAllSymbols();
|
||||||
Logger.LogArray("Element-Symbole (erste 10)", symbols.Take(10).ToArray());
|
Logger.LogArray("Element-Symbole (erste 10)", symbols.Take(10).ToArray());
|
||||||
|
|
||||||
// Tuple-Operationen testen
|
// Tuples: Strukturierte Rückgabe mehrerer Werte
|
||||||
var (totalElements, uniqueSeries, avgWeight) = GetStatistics();
|
var (totalElements, uniqueSeries, avgWeight) = GetStatistics();
|
||||||
Logger.LogTuple("Statistiken", $"Elemente: {totalElements}, Serien: {uniqueSeries}, Ø-Gewicht: {avgWeight:F2}");
|
Logger.LogTuple("Statistiken", $"Elemente: {totalElements}, Serien: {uniqueSeries}, Ø-Gewicht: {avgWeight:F2}");
|
||||||
|
|
||||||
// Extremwerte finden
|
// Tuples mit Objekten: Extremwerte finden
|
||||||
var (lightest, heaviest) = GetWeightExtremes();
|
var (lightest, heaviest) = GetWeightExtremes();
|
||||||
if (lightest != null && heaviest != null)
|
if (lightest != null && heaviest != null)
|
||||||
{
|
{
|
||||||
@ -58,304 +141,352 @@ namespace Project_Periodensystem.Controller
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
// Graceful Degradation: Bei Fehlern eine leere Liste verwenden
|
||||||
Logger.Log($"EXCEPTION in LoadElements: {ex.Message}");
|
Logger.Log($"EXCEPTION in LoadElements: {ex.Message}");
|
||||||
_elements = new List<Element>();
|
_elements = new List<Element>();
|
||||||
|
|
||||||
|
// Helper-Klassen können nicht initialisiert werden (bleiben null)
|
||||||
|
_validator = null;
|
||||||
|
_statistics = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== PUBLIC API METHODEN - ELEMENTZUGRIFF =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns>Liste aller Elemente</returns>
|
|
||||||
public List<Element> GetAllElements()
|
public List<Element> GetAllElements()
|
||||||
{
|
{
|
||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns>Array aller Elemente</returns>
|
|
||||||
public Element[] GetAllElementsAsArray()
|
public Element[] GetAllElementsAsArray()
|
||||||
{
|
{
|
||||||
return _elements.ToArray();
|
return _elements.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns>Array mit allen Element-Symbolen</returns>
|
|
||||||
public string[] GetAllSymbols()
|
public string[] GetAllSymbols()
|
||||||
{
|
{
|
||||||
|
// Klassische Array-Erstellung mit fester Größe
|
||||||
string[] symbols = new string[_elements.Count];
|
string[] symbols = new string[_elements.Count];
|
||||||
|
|
||||||
|
// Manuelle Population mit for-Schleife (imperativer Stil)
|
||||||
for (int i = 0; i < _elements.Count; i++)
|
for (int i = 0; i < _elements.Count; i++)
|
||||||
{
|
{
|
||||||
symbols[i] = _elements[i].Symbol;
|
symbols[i] = _elements[i].Symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbols;
|
return symbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// ===== DELEGATION AN HELPER-KLASSEN =====
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns>Durchschnittliches Atomgewicht</returns>
|
|
||||||
public double GetAverageAtomicWeight()
|
|
||||||
{
|
|
||||||
double[] weights = GetAtomicWeights();
|
|
||||||
if (weights.Length == 0) return 0;
|
|
||||||
|
|
||||||
double sum = 0;
|
// ===== VALIDIERUNGS-METHODEN (delegiert an ElementValidator) =====
|
||||||
for (int i = 0; i < weights.Length; i++)
|
|
||||||
{
|
|
||||||
sum += weights[i];
|
|
||||||
}
|
|
||||||
return sum / weights.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Speichert Elemente persistent (NEUE FUNKTION)
|
/// Validiert die gesamte Element-Datenbasis
|
||||||
|
/// Delegation an ElementValidator.ValidateData()
|
||||||
|
/// Fallback: false bei null-Validator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SaveElements()
|
public bool ValidateData() => _validator?.ValidateData() ?? false;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DataManager.SaveElements(_elements);
|
|
||||||
Logger.Log("Elemente erfolgreich gespeichert");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogException(ex, "SaveElements");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="atomicNumber">Atomnummer des gesuchten Elements</param>
|
|
||||||
/// <returns>Element oder null wenn nicht gefunden</returns>
|
|
||||||
public Element? GetElementByAtomicNumber(int atomicNumber)
|
public Element? GetElementByAtomicNumber(int atomicNumber)
|
||||||
{
|
{
|
||||||
if (atomicNumber <= 0)
|
|
||||||
{
|
|
||||||
Logger.Log($"Ungültige Atomnummer: {atomicNumber}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber);
|
return _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="symbol">Symbol des gesuchten Elements</param>
|
|
||||||
/// <returns>Element oder null wenn nicht gefunden</returns>
|
|
||||||
public Element? GetElementBySymbol(string symbol)
|
public Element? GetElementBySymbol(string symbol)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(symbol))
|
// Guard Clause: Ungültige Eingaben frühzeitig abfangen
|
||||||
{
|
if (string.IsNullOrWhiteSpace(symbol)) return null;
|
||||||
Logger.Log("Leeres Symbol übergeben");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _elements.FirstOrDefault(e =>
|
// Case-insensitive Suche für bessere Benutzerfreundlichkeit
|
||||||
string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase));
|
return _elements.FirstOrDefault(e => string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="series">Gewünschte Elementserie</param>
|
|
||||||
/// <returns>Liste der Elemente der angegebenen Serie</returns>
|
|
||||||
public List<Element> GetElementsBySeries(string series)
|
public List<Element> GetElementsBySeries(string series)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(series))
|
// Guard Clause: Ungültige Eingaben behandeln
|
||||||
{
|
if (string.IsNullOrWhiteSpace(series)) return new List<Element>();
|
||||||
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 =>
|
// ===== DATEN-MANAGEMENT =====
|
||||||
string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validiert Grid-Position eines Elements
|
/// Lädt alle Elementdaten neu aus dem Persistence-Layer
|
||||||
/// </summary>
|
///
|
||||||
/// <param name="element">Zu validierendes Element</param>
|
/// ZWECK:
|
||||||
/// <returns>True wenn Position gültig ist</returns>
|
/// - Aktualisierung bei geänderten Datenquellen
|
||||||
public bool ValidateElementPosition(Element element)
|
/// - Recovery nach Datenfehlern
|
||||||
{
|
/// - Synchronisation mit externen Datenänderungen
|
||||||
if (element == null)
|
///
|
||||||
{
|
/// ABLAUF:
|
||||||
Logger.Log("Null-Element kann nicht validiert werden");
|
/// 1. Benutzer über Neuladen informieren
|
||||||
return false;
|
/// 2. LoadElements() ruft gesamte Initialisierungslogik auf
|
||||||
}
|
/// 3. Helper-Klassen werden mit neuen Daten reinitialisiert
|
||||||
|
///
|
||||||
// Periodensystem hat 7 Perioden (0-6) und 18 Gruppen (0-17)
|
/// VERWENDUNG:
|
||||||
// Plus Lanthanoid/Actinoid-Reihen bei Zeile 8 und 9
|
/// - Nach Datenimport
|
||||||
bool validRow = element.Row >= 0 && element.Row <= 9;
|
/// - Bei Corruption Recovery
|
||||||
bool validColumn = element.Column >= 0 && element.Column <= 17;
|
/// - Für Entwicklungs-/Debug-Zwecke
|
||||||
|
|
||||||
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)
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RefreshData()
|
public void RefreshData()
|
||||||
{
|
{
|
||||||
Logger.Log("Daten werden neu geladen...");
|
Logger.Log("Daten werden neu geladen...");
|
||||||
LoadElements();
|
LoadElements(); // Vollständige Reinitialisierung
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns>Anzahl der Elemente</returns>
|
public int GetElementCount() => _elements.Count;
|
||||||
public int GetElementCount()
|
|
||||||
{
|
// ===== NAVIGATION CONTROLLER METHODEN =====
|
||||||
return _elements.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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>
|
/// <summary>
|
||||||
/// Gibt Grundinfos eines Elements als Tuple zurück
|
/// Navigiert zur Hauptansicht des Periodensystems
|
||||||
/// </summary>
|
///
|
||||||
/// <param name="atomicNumber">Atomnummer</param>
|
/// ZWECK:
|
||||||
/// <returns>Tuple mit (Symbol, Name, Gewicht)</returns>
|
/// - Wechsel zur interaktiven Periodensystem-Darstellung
|
||||||
public (string symbol, string name, double weight) GetElementInfo(int atomicNumber)
|
/// - Hauptfunktion der Anwendung
|
||||||
{
|
///
|
||||||
var element = GetElementByAtomicNumber(atomicNumber);
|
/// FEHLERBEHANDLUNG:
|
||||||
if (element != null)
|
/// - Exception wird geloggt, aber nicht weitergegeben
|
||||||
{
|
/// - Navigation Service kann null sein (optional dependency)
|
||||||
return (element.Symbol, element.ElementName, element.AtomicWeight);
|
/// - Graceful Degradation bei Navigationsfehlern
|
||||||
}
|
|
||||||
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)
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void HandleNavigateToPeriodicTable()
|
public void HandleNavigateToPeriodicTable()
|
||||||
{
|
{
|
||||||
@ -371,7 +502,12 @@ namespace Project_Periodensystem.Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public void HandleNavigateToAbout()
|
public void HandleNavigateToAbout()
|
||||||
{
|
{
|
||||||
@ -387,7 +523,12 @@ namespace Project_Periodensystem.Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public void HandleNavigateToLanding()
|
public void HandleNavigateToLanding()
|
||||||
{
|
{
|
||||||
@ -403,7 +544,16 @@ namespace Project_Periodensystem.Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public void HandleToggleTheme()
|
public void HandleToggleTheme()
|
||||||
{
|
{
|
||||||
@ -419,17 +569,23 @@ namespace Project_Periodensystem.Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public void HandleExportData()
|
public void HandleExportData()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Log("Controller: Daten-Export angefordert");
|
Logger.Log("Controller: Daten-Export angefordert");
|
||||||
|
|
||||||
// Geschäftslogik für Export
|
|
||||||
SaveElements(); // Persistierung
|
|
||||||
|
|
||||||
_navigationService?.ShowExportConfirmation();
|
_navigationService?.ShowExportConfirmation();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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
|
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
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
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; }
|
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,
|
public Element(int atomicNumber, string symbol, string elementname, double atomicWeight,
|
||||||
double electronegativity, double density, string series, int row, int column)
|
double electronegativity, double density, string series, int row, int column)
|
||||||
{
|
{
|
||||||
// Weist den Eigenschaften beim Erzeugen eines Objekts die übergebenen Werte zu
|
// Eigenschafts-Zuweisungen mit Selbst-Dokumentation
|
||||||
AtomicNumber = atomicNumber;
|
AtomicNumber = atomicNumber; // Eindeutige Identifikation
|
||||||
Symbol = symbol;
|
Symbol = symbol; // Kurze chemische Bezeichnung
|
||||||
ElementName = elementname;
|
ElementName = elementname; // Vollständiger Name
|
||||||
AtomicWeight = atomicWeight;
|
AtomicWeight = atomicWeight; // Relative Atommasse
|
||||||
Electronegativity = electronegativity;
|
Electronegativity = electronegativity; // Bindungsverhalten
|
||||||
Density = density;
|
Density = density; // Physikalische Eigenschaft
|
||||||
Series = series;
|
Series = series; // Chemische Klassifikation
|
||||||
Row = row;
|
Row = row; // Periodensystem-Position vertikal
|
||||||
Column = column;
|
Column = column; // Periodensystem-Position horizontal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,16 +5,68 @@ using System.Linq;
|
|||||||
namespace Project_Periodensystem.Model
|
namespace Project_Periodensystem.Model
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public static class Logger
|
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(
|
private static readonly string LogFilePath = Path.Combine(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
"Periodensystem", "app.log");
|
"Periodensystem", "app.log");
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
static Logger()
|
static Logger()
|
||||||
{
|
{
|
||||||
@ -29,25 +81,52 @@ namespace Project_Periodensystem.Model
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Falls Log-Datei nicht erstellt werden kann, nur Konsole verwenden
|
// Falls Log-Datei nicht erstellt werden kann, nur Konsole verwenden
|
||||||
|
// Graceful Degradation: Hauptfunktionalität nicht beeinträchtigen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== HAUPTPROTOKOLLIERUNGS-METHODEN =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="message">Zu protokollierende Nachricht</param>
|
/// <param name="message">Zu protokollierende Nachricht (wird validiert)</param>
|
||||||
public static void Log(string message)
|
public static void Log(string message)
|
||||||
{
|
{
|
||||||
|
// Guard Clause: Ungültige Eingaben frühzeitig abfangen
|
||||||
if (string.IsNullOrWhiteSpace(message))
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Zeitstempel im ISO-ähnlichen Format für bessere Lesbarkeit
|
||||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
var logEntry = $"[{timestamp}] {message}";
|
var logEntry = $"[{timestamp}] {message}";
|
||||||
|
|
||||||
// Konsolen-Ausgabe (für Debug)
|
// Konsolen-Ausgabe (für Entwicklung und sofortiges Feedback)
|
||||||
Console.WriteLine(logEntry);
|
Console.WriteLine(logEntry);
|
||||||
|
|
||||||
// Datei-Ausgabe (für Persistenz)
|
// Datei-Ausgabe (für Persistenz und spätere Analyse)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.AppendAllText(LogFilePath, logEntry + Environment.NewLine);
|
File.AppendAllText(LogFilePath, logEntry + Environment.NewLine);
|
||||||
@ -55,22 +134,46 @@ namespace Project_Periodensystem.Model
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Fehler beim Schreiben ignorieren um App nicht zum Absturz zu bringen
|
// Fehler beim Schreiben ignorieren um App nicht zum Absturz zu bringen
|
||||||
|
// Silent Fail: Logging ist nice-to-have, nicht essential
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== SPEZIALISIERTE LOGGING-METHODEN =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="ex">Exception die protokolliert werden soll</param>
|
/// <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 = "")
|
public static void LogException(Exception ex, string context = "")
|
||||||
{
|
{
|
||||||
|
// Null-Schutz: Auch ungültige Exception-Objekte handhaben
|
||||||
if (ex == null)
|
if (ex == null)
|
||||||
{
|
{
|
||||||
Log("Null-Exception übergeben");
|
Log("Null-Exception übergeben - möglicher Programmierfehler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Formatierung je nach verfügbarem Kontext
|
||||||
var message = string.IsNullOrWhiteSpace(context)
|
var message = string.IsNullOrWhiteSpace(context)
|
||||||
? $"EXCEPTION: {ex.Message}\nStack: {ex.StackTrace}"
|
? $"EXCEPTION: {ex.Message}\nStack: {ex.StackTrace}"
|
||||||
: $"EXCEPTION in {context}: {ex.Message}\nStack: {ex.StackTrace}";
|
: $"EXCEPTION in {context}: {ex.Message}\nStack: {ex.StackTrace}";
|
||||||
@ -79,35 +182,87 @@ namespace Project_Periodensystem.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="message">Warnung</param>
|
/// <param name="message">Warnung (wird mit WARNING: Präfix versehen)</param>
|
||||||
public static void LogWarning(string message)
|
public static void LogWarning(string message)
|
||||||
{
|
{
|
||||||
Log($"WARNING: {message}");
|
Log($"WARNING: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="message">Fehlermeldung</param>
|
/// <param name="message">Fehlermeldung (wird mit ERROR: Präfix versehen)</param>
|
||||||
public static void LogError(string message)
|
public static void LogError(string message)
|
||||||
{
|
{
|
||||||
Log($"ERROR: {message}");
|
Log($"ERROR: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="message">Debug-Nachricht</param>
|
/// <param name="message">Debug-Nachricht (wird mit DEBUG: Präfix versehen)</param>
|
||||||
[System.Diagnostics.Conditional("DEBUG")]
|
[System.Diagnostics.Conditional("DEBUG")]
|
||||||
public static void LogDebug(string message)
|
public static void LogDebug(string message)
|
||||||
{
|
{
|
||||||
Log($"DEBUG: {message}");
|
Log($"DEBUG: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== DATEI-MANAGEMENT UND UTILITY-METHODEN =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public static void ClearLog()
|
public static void ClearLog()
|
||||||
{
|
{
|
||||||
@ -116,7 +271,7 @@ namespace Project_Periodensystem.Model
|
|||||||
if (File.Exists(LogFilePath))
|
if (File.Exists(LogFilePath))
|
||||||
{
|
{
|
||||||
File.Delete(LogFilePath);
|
File.Delete(LogFilePath);
|
||||||
Log("Log-Datei gelöscht");
|
Log("Log-Datei gelöscht und neu initialisiert");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -126,40 +281,97 @@ namespace Project_Periodensystem.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <returns>Vollständiger Pfad zur Log-Datei</returns>
|
/// <returns>Vollständiger Pfad zur Log-Datei (absoluter Pfad)</returns>
|
||||||
public static string GetLogFilePath()
|
public static string GetLogFilePath()
|
||||||
{
|
{
|
||||||
return LogFilePath;
|
return LogFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== SPEZIALISIERTE DATENTYP-LOGGING (C# LERNZWECK) =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="title">Titel für das Array</param>
|
/// <param name="title">Beschreibender Titel für das Array</param>
|
||||||
/// <param name="items">Array von Items</param>
|
/// <param name="items">Array von Strings zur Anzeige</param>
|
||||||
public static void LogArray(string title, string[] items)
|
public static void LogArray(string title, string[] items)
|
||||||
{
|
{
|
||||||
Log($"{title}: [{string.Join(", ", items)}]");
|
Log($"{title}: [{string.Join(", ", items)}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="title">Titel für das Array</param>
|
/// <param name="title">Beschreibender Titel für das Array</param>
|
||||||
/// <param name="numbers">Array von Zahlen</param>
|
/// <param name="numbers">Array von Double-Werten zur Anzeige</param>
|
||||||
public static void LogArray(string title, double[] numbers)
|
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();
|
var formattedNumbers = numbers.Select(n => n.ToString("F2")).ToArray();
|
||||||
Log($"{title}: [{string.Join(", ", formattedNumbers)}]");
|
Log($"{title}: [{string.Join(", ", formattedNumbers)}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="title">Titel</param>
|
/// <param name="title">Beschreibender Titel für das Tuple</param>
|
||||||
/// <param name="tupleInfo">Tuple als string</param>
|
/// <param name="tupleInfo">Bereits formatierte Tuple-Darstellung als String</param>
|
||||||
public static void LogTuple(string title, string tupleInfo)
|
public static void LogTuple(string title, string tupleInfo)
|
||||||
{
|
{
|
||||||
Log($"{title}: {tupleInfo}");
|
Log($"{title}: {tupleInfo}");
|
||||||
|
|||||||
@ -7,83 +7,233 @@ using Project_Periodensystem.Model;
|
|||||||
namespace Project_Periodensystem.Persistence
|
namespace Project_Periodensystem.Persistence
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public static class DataManager
|
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(
|
private static readonly string DataDirectory = Path.Combine(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
"Periodensystem");
|
"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 ElementsFile = Path.Combine(DataDirectory, "elements.json");
|
||||||
private static readonly string SettingsFile = Path.Combine(DataDirectory, "settings.json");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Speichert Elemente als JSON
|
/// Vollständiger Pfad zu Benutzereinstellungen (JSON)
|
||||||
|
/// Speichert Präferenzen wie Theme, Sprache, letzte Nutzung
|
||||||
/// </summary>
|
/// </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)
|
public static void SaveElements(List<Element> elements)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Verzeichnis erstellen falls nicht vorhanden
|
||||||
Directory.CreateDirectory(DataDirectory);
|
Directory.CreateDirectory(DataDirectory);
|
||||||
|
|
||||||
|
// JSON-Serialisierung mit lesbarer Formatierung
|
||||||
var json = JsonSerializer.Serialize(elements, new JsonSerializerOptions { WriteIndented = true });
|
var json = JsonSerializer.Serialize(elements, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
|
||||||
|
// Atomare Datei-Operation (ersetzt bestehende Datei)
|
||||||
File.WriteAllText(ElementsFile, json);
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogException(ex, "SaveElements");
|
Logger.LogException(ex, "SaveElements");
|
||||||
|
// Keine Weiterleitung der Exception - Speichern ist optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
|
/// <returns>Liste aller verfügbaren chemischen Elemente</returns>
|
||||||
public static List<Element> LoadElements()
|
public static List<Element> LoadElements()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Prüfung ob benutzerdefinierte Daten existieren
|
||||||
if (File.Exists(ElementsFile))
|
if (File.Exists(ElementsFile))
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(ElementsFile);
|
var json = File.ReadAllText(ElementsFile);
|
||||||
var elements = JsonSerializer.Deserialize<List<Element>>(json);
|
var elements = JsonSerializer.Deserialize<List<Element>>(json);
|
||||||
|
|
||||||
|
// Validierung der deserialisierten Daten
|
||||||
if (elements != null && elements.Count > 0)
|
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;
|
return elements;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogException(ex, "LoadElements");
|
Logger.LogException(ex, "LoadElements - JSON Loading");
|
||||||
|
// Kein Re-throw - Fallback folgt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback zu eingebauten Daten
|
// Fallback zu eingebauten, statischen Daten
|
||||||
Logger.Log("Fallback zu eingebauten Element-Daten");
|
Logger.Log("Fallback zu eingebauten Element-Daten (PeriodicTableData)");
|
||||||
return PeriodicTableData.Elements;
|
return PeriodicTableData.Elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== BENUTZEREINSTELLUNGEN PERSISTENZ =====
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
|
/// <param name="settings">AppSettings-Objekt mit Benutzer-Präferenzen</param>
|
||||||
public static void SaveSettings(AppSettings settings)
|
public static void SaveSettings(AppSettings settings)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Verzeichnis sicherstellen (gleich wie bei Elementen)
|
||||||
Directory.CreateDirectory(DataDirectory);
|
Directory.CreateDirectory(DataDirectory);
|
||||||
|
|
||||||
|
// JSON-Serialisierung mit Pretty-Print
|
||||||
var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
|
var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
|
||||||
|
// Atomare Speicherung
|
||||||
File.WriteAllText(SettingsFile, json);
|
File.WriteAllText(SettingsFile, json);
|
||||||
|
|
||||||
|
Logger.Log($"Benutzereinstellungen gespeichert: Theme={settings.LastTheme}, Sprache={settings.PreferredLanguage}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogException(ex, "SaveSettings");
|
Logger.LogException(ex, "SaveSettings");
|
||||||
|
// Keine Exception-Weiterleitung - Settings sind optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
|
/// <returns>AppSettings-Objekt (entweder geladen oder mit Defaults)</returns>
|
||||||
public static AppSettings LoadSettings()
|
public static AppSettings LoadSettings()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -92,25 +242,87 @@ namespace Project_Periodensystem.Persistence
|
|||||||
{
|
{
|
||||||
var json = File.ReadAllText(SettingsFile);
|
var json = File.ReadAllText(SettingsFile);
|
||||||
var settings = JsonSerializer.Deserialize<AppSettings>(json);
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogException(ex, "LoadSettings");
|
Logger.LogException(ex, "LoadSettings");
|
||||||
|
// Kein Re-throw - Fallback zu Defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback zu Default-Settings
|
||||||
|
Logger.Log("Fallback zu Standard-Benutzereinstellungen");
|
||||||
return new AppSettings();
|
return new AppSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public class AppSettings
|
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";
|
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;
|
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";
|
public string PreferredLanguage { get; set; } = "German";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,31 +3,99 @@ using Project_Periodensystem.Model;
|
|||||||
|
|
||||||
namespace Project_Periodensystem.Persistence
|
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
|
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>
|
public static List<Element> Elements { get; } = new List<Element>
|
||||||
{
|
{
|
||||||
// Erste Periode
|
// ===== ERSTE PERIODE =====
|
||||||
new Element( 1, "H", "Wasserstoff", 1.008, 2.20, 0.000, "Nichtmetall", 0, 0),
|
// Die einfachsten Elemente: Wasserstoff und Helium
|
||||||
new Element( 2, "He", "Helium", 4.003, 0.00, 0.001, "Edelgas", 0, 17),
|
// Besonderheiten: Nur 1s-Orbitale gefüllt, einzigartige Eigenschaften
|
||||||
|
|
||||||
// Zweite Periode
|
new Element( 1, "H", "Wasserstoff", 1.008, 2.20, 0.000, "Nichtmetall", 0, 0), // Leichtestes Element, Grundbaustein des Universums
|
||||||
new Element( 3, "Li", "Lithium", 6.940, 0.98, 0.534, "Alkalimetall", 1, 0),
|
new Element( 2, "He", "Helium", 4.003, 0.00, 0.001, "Edelgas", 0, 17), // Edelgas mit vollständiger Schale, chemisch inert
|
||||||
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),
|
|
||||||
|
|
||||||
// Dritte Periode
|
// ===== ZWEITE PERIODE =====
|
||||||
new Element( 11, "Na", "Natrium", 22.990,0.93, 0.971, "Alkalimetall", 2, 0),
|
// Elemente mit 2 Elektronenschalen (1s² + 2s/2p)
|
||||||
new Element( 12, "Mg", "Magnesium", 24.305,1.31, 1.738, "Erdalkalimetall", 2, 1),
|
// Wichtige biologische und technische Elemente
|
||||||
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( 3, "Li", "Lithium", 6.940, 0.98, 0.534, "Alkalimetall", 1, 0), // Leichtestes Metall, wichtig für Batterien
|
||||||
new Element( 15, "P", "Phosphor", 30.974,2.19, 1.820, "Nichtmetall", 2, 14),
|
new Element( 4, "Be", "Beryllium", 9.012, 1.57, 1.850, "Erdalkalimetall", 1, 1), // Leicht aber giftig, Luft-/Raumfahrt
|
||||||
new Element( 16, "S", "Schwefel", 32.060,2.58, 2.067, "Nichtmetall", 2, 15),
|
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( 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),
|
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
|
namespace Project_Periodensystem.View
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Navigation Service Implementation im View-Layer
|
/// Concrete Implementation des Navigation Service für Avalonia UI
|
||||||
/// Implementiert INavigationService Interface vom Controller
|
///
|
||||||
/// SAUBERE TRENNUNG: Controller definiert WAS, View definiert WIE
|
/// 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>
|
/// </summary>
|
||||||
public class NavigationService : INavigationService
|
public class NavigationService : INavigationService
|
||||||
{
|
{
|
||||||
private readonly MainWindow _mainWindow;
|
// ===== PRIVATE FELDER =====
|
||||||
private PeriodensystemController? _dataController;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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)
|
public NavigationService(MainWindow mainWindow)
|
||||||
{
|
{
|
||||||
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
|
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
|
||||||
Logger.Log("NavigationService initialisiert - saubere Interface-Trennung");
|
Logger.Log("NavigationService initialisiert - implementiert Interface-basierte Navigation");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
|
/// <param name="dataController">Der PeriodensystemController für Datenzugriff</param>
|
||||||
|
/// <exception cref="ArgumentNullException">Wenn dataController null ist</exception>
|
||||||
public void SetDataController(PeriodensystemController dataController)
|
public void SetDataController(PeriodensystemController dataController)
|
||||||
{
|
{
|
||||||
_dataController = dataController ?? throw new ArgumentNullException(nameof(dataController));
|
_dataController = dataController ?? throw new ArgumentNullException(nameof(dataController));
|
||||||
|
Logger.Log("DataController in NavigationService gesetzt - zirkuläre Abhängigkeit aufgelöst");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -37,21 +37,13 @@ namespace Project_Periodensystem.View
|
|||||||
periodicGrid = this.FindControl<Grid>("PeriodicGrid");
|
periodicGrid = this.FindControl<Grid>("PeriodicGrid");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controller setzen (Dependency Injection für MVC)
|
|
||||||
/// </summary>
|
|
||||||
public void SetController(PeriodensystemController controller)
|
public void SetController(PeriodensystemController controller)
|
||||||
{
|
{
|
||||||
_controller = controller ?? throw new ArgumentNullException(nameof(controller));
|
_controller = controller ?? throw new ArgumentNullException(nameof(controller));
|
||||||
Logger.Log("PeriodicTablePage: Controller gesetzt (MVC-Pattern)");
|
Logger.Log("PeriodicTablePage: Controller gesetzt (MVC-Pattern)");
|
||||||
|
|
||||||
// Elemente laden und anzeigen
|
|
||||||
LoadAndDisplayElements();
|
LoadAndDisplayElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lädt Elemente über Controller und zeigt sie an
|
|
||||||
/// </summary>
|
|
||||||
private void LoadAndDisplayElements()
|
private void LoadAndDisplayElements()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -62,7 +54,6 @@ namespace Project_Periodensystem.View
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daten über Controller laden (nicht direkt!)
|
|
||||||
var elements = _controller.GetAllElements();
|
var elements = _controller.GetAllElements();
|
||||||
|
|
||||||
if (!elements.Any())
|
if (!elements.Any())
|
||||||
@ -71,7 +62,6 @@ namespace Project_Periodensystem.View
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Datenvalidierung über Controller
|
|
||||||
if (!_controller.ValidateData())
|
if (!_controller.ValidateData())
|
||||||
{
|
{
|
||||||
Logger.Log("Datenvalidierung fehlgeschlagen");
|
Logger.Log("Datenvalidierung fehlgeschlagen");
|
||||||
@ -80,7 +70,6 @@ namespace Project_Periodensystem.View
|
|||||||
|
|
||||||
Logger.Log($"Lade {elements.Count} Elemente in das Grid");
|
Logger.Log($"Lade {elements.Count} Elemente in das Grid");
|
||||||
|
|
||||||
// UI-Update für jedes Element
|
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
foreach (var element in elements)
|
foreach (var element in elements)
|
||||||
{
|
{
|
||||||
@ -96,8 +85,6 @@ namespace Project_Periodensystem.View
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.Log($"{successCount} von {elements.Count} Elementen erfolgreich geladen");
|
Logger.Log($"{successCount} von {elements.Count} Elementen erfolgreich geladen");
|
||||||
|
|
||||||
// Legend-Buttons sammeln für Highlighting-Funktionalität
|
|
||||||
CollectLegendButtons();
|
CollectLegendButtons();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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)
|
private void CreateElementButton(Element element)
|
||||||
{
|
{
|
||||||
if (element == null)
|
if (element == null)
|
||||||
@ -118,15 +101,12 @@ namespace Project_Periodensystem.View
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI-Komponenten erstellen
|
|
||||||
var button = new Button { Classes = { "ElementTile" } };
|
var button = new Button { Classes = { "ElementTile" } };
|
||||||
var panel = new StackPanel();
|
var panel = new StackPanel();
|
||||||
|
|
||||||
// Button-Referenz für Highlighting speichern
|
button.Tag = element;
|
||||||
button.Tag = element; // Element-Daten im Tag speichern
|
|
||||||
_elementButtons.Add(button);
|
_elementButtons.Add(button);
|
||||||
|
|
||||||
// Hintergrundfarbe über Converter setzen
|
|
||||||
var backgroundColor = new SeriesToColorConverter()
|
var backgroundColor = new SeriesToColorConverter()
|
||||||
.Convert(element.Series, typeof(Brush), null, CultureInfo.InvariantCulture) as Brush;
|
.Convert(element.Series, typeof(Brush), null, CultureInfo.InvariantCulture) as Brush;
|
||||||
|
|
||||||
@ -135,7 +115,6 @@ namespace Project_Periodensystem.View
|
|||||||
button.Background = backgroundColor;
|
button.Background = backgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text-Elemente erstellen
|
|
||||||
var symbolText = new TextBlock
|
var symbolText = new TextBlock
|
||||||
{
|
{
|
||||||
Text = element.Symbol,
|
Text = element.Symbol,
|
||||||
@ -152,12 +131,10 @@ namespace Project_Periodensystem.View
|
|||||||
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center
|
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center
|
||||||
};
|
};
|
||||||
|
|
||||||
// Layout zusammenbauen
|
panel.Children.Add(numberText);
|
||||||
panel.Children.Add(numberText); // Number on top
|
panel.Children.Add(symbolText);
|
||||||
panel.Children.Add(symbolText); // Symbol below
|
|
||||||
button.Content = panel;
|
button.Content = panel;
|
||||||
|
|
||||||
// Grid-Position setzen (0-basiert)
|
|
||||||
int gridRow = element.Row;
|
int gridRow = element.Row;
|
||||||
int gridColumn = element.Column;
|
int gridColumn = element.Column;
|
||||||
|
|
||||||
@ -166,7 +143,6 @@ namespace Project_Periodensystem.View
|
|||||||
Grid.SetRow(button, gridRow);
|
Grid.SetRow(button, gridRow);
|
||||||
Grid.SetColumn(button, gridColumn);
|
Grid.SetColumn(button, gridColumn);
|
||||||
|
|
||||||
// Button zum Grid hinzufügen
|
|
||||||
if (periodicGrid != null)
|
if (periodicGrid != null)
|
||||||
{
|
{
|
||||||
periodicGrid.Children.Add(button);
|
periodicGrid.Children.Add(button);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user