themes verbessert, inkl about page

This commit is contained in:
OliverT87 2025-06-24 14:48:42 +02:00
parent abb0d8c06a
commit 961cbd198a
12 changed files with 763 additions and 325 deletions

View File

@ -1,17 +1,196 @@
using System.Collections.ObjectModel; using System;
using System.Collections.Generic;
using System.Linq;
using Project_Periodensystem.Model; using Project_Periodensystem.Model;
using Project_Periodensystem.Persistence; using Project_Periodensystem.Persistence;
namespace Project_Periodensystem.Controller namespace Project_Periodensystem.Controller
{ {
/// <summary>
/// Controller für das Periodensystem - verwaltet die Geschäftslogik
/// und trennt View von Model gemäß MVC-Pattern
/// </summary>
public class PeriodensystemController public class PeriodensystemController
{ {
public ObservableCollection<Element> Elements { get; } // Nullable Field um Warning zu vermeiden
private List<Element> _elements = new List<Element>();
/// <summary>
/// Konstruktor - initialisiert den Controller
/// </summary>
public PeriodensystemController() public PeriodensystemController()
{ {
// Gießt die statische List<> in eine ObservableCollection LoadElements();
Elements = new ObservableCollection<Element>(PeriodicTableData.Elements); }
/// <summary>
/// Lädt alle Elemente über den Persistence-Layer
/// </summary>
private void LoadElements()
{
try
{
// Verwende die korrekte statische Methode aus PeriodicTableData
_elements = PeriodicTableData.Elements.ToList();
Logger.Log($"Controller: {_elements.Count} Elemente erfolgreich geladen");
}
catch (Exception ex)
{
Logger.Log($"EXCEPTION in LoadElements: {ex.Message}");
_elements = new List<Element>();
}
}
/// <summary>
/// Gibt alle verfügbaren Elemente zurück
/// </summary>
/// <returns>Liste aller Elemente</returns>
public List<Element> GetAllElements()
{
return _elements;
}
/// <summary>
/// Sucht ein Element nach Atomnummer
/// </summary>
/// <param name="atomicNumber">Atomnummer des gesuchten Elements</param>
/// <returns>Element oder null wenn nicht gefunden</returns>
public Element? GetElementByAtomicNumber(int atomicNumber)
{
if (atomicNumber <= 0)
{
Logger.Log($"Ungültige Atomnummer: {atomicNumber}");
return null;
}
return _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber);
}
/// <summary>
/// Sucht ein Element nach Symbol
/// </summary>
/// <param name="symbol">Symbol des gesuchten Elements</param>
/// <returns>Element oder null wenn nicht gefunden</returns>
public Element? GetElementBySymbol(string symbol)
{
if (string.IsNullOrWhiteSpace(symbol))
{
Logger.Log("Leeres Symbol übergeben");
return null;
}
return _elements.FirstOrDefault(e =>
string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Filtert Elemente nach Serie
/// </summary>
/// <param name="series">Gewünschte Elementserie</param>
/// <returns>Liste der Elemente der angegebenen Serie</returns>
public List<Element> GetElementsBySeries(string series)
{
if (string.IsNullOrWhiteSpace(series))
{
return new List<Element>();
}
return _elements.Where(e =>
string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase))
.ToList();
}
/// <summary>
/// Validiert Grid-Position eines Elements
/// </summary>
/// <param name="element">Zu validierendes Element</param>
/// <returns>True wenn Position gültig ist</returns>
public bool ValidateElementPosition(Element element)
{
if (element == null)
{
Logger.Log("Null-Element kann nicht validiert werden");
return false;
}
// Periodensystem hat 7 Perioden (0-6) und 18 Gruppen (0-17)
// Plus Lanthanoid/Actinoid-Reihen bei Zeile 8 und 9
bool validRow = element.Row >= 0 && element.Row <= 9;
bool validColumn = element.Column >= 0 && element.Column <= 17;
if (!validRow || !validColumn)
{
Logger.Log($"Ungültige Position für {element.Symbol}: ({element.Row},{element.Column})");
return false;
}
return true;
}
/// <summary>
/// Gibt alle verfügbaren Element-Serien zurück
/// </summary>
/// <returns>Liste aller Serien</returns>
public List<string> GetAllSeries()
{
return _elements.Select(e => e.Series)
.Distinct()
.Where(s => !string.IsNullOrWhiteSpace(s))
.OrderBy(s => s)
.ToList();
}
/// <summary>
/// Überprüft ob alle Elemente korrekt geladen wurden
/// </summary>
/// <returns>True wenn Daten vollständig sind</returns>
public bool ValidateData()
{
if (_elements.Count == 0)
{
Logger.Log("Keine Elemente geladen");
return false;
}
// Überprüfe auf Duplikate bei Atomnummern
var duplicateNumbers = _elements.GroupBy(e => e.AtomicNumber)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
if (duplicateNumbers.Any())
{
Logger.Log($"Doppelte Atomnummern gefunden: {string.Join(", ", duplicateNumbers)}");
return false;
}
// Überprüfe auf leere Symbole
var emptySymbols = _elements.Where(e => string.IsNullOrWhiteSpace(e.Symbol));
if (emptySymbols.Any())
{
Logger.Log("Elemente mit leeren Symbolen gefunden");
return false;
}
Logger.Log($"Datenvalidierung erfolgreich - {_elements.Count} Elemente validiert");
return true;
}
/// <summary>
/// Lädt Daten neu (für Refresh-Funktionalität)
/// </summary>
public void RefreshData()
{
Logger.Log("Daten werden neu geladen...");
LoadElements();
}
/// <summary>
/// Gibt die Anzahl der geladenen Elemente zurück
/// </summary>
/// <returns>Anzahl der Elemente</returns>
public int GetElementCount()
{
return _elements.Count;
} }
} }
} }

View File

@ -0,0 +1,136 @@
using System;
using System.IO;
namespace Project_Periodensystem.Model
{
/// <summary>
/// Einfacher Logger für Debug-Ausgaben und Fehlerprotokollierung
/// </summary>
public static class Logger
{
private static readonly string LogFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Periodensystem", "app.log");
/// <summary>
/// Statischer Initializer - erstellt Log-Verzeichnis
/// </summary>
static Logger()
{
try
{
var logDirectory = Path.GetDirectoryName(LogFilePath);
if (logDirectory != null && !Directory.Exists(logDirectory))
{
Directory.CreateDirectory(logDirectory);
}
}
catch
{
// Falls Log-Datei nicht erstellt werden kann, nur Konsole verwenden
}
}
/// <summary>
/// Protokolliert eine Nachricht sowohl in Konsole als auch Datei
/// </summary>
/// <param name="message">Zu protokollierende Nachricht</param>
public static void Log(string message)
{
if (string.IsNullOrWhiteSpace(message))
return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var logEntry = $"[{timestamp}] {message}";
// Konsolen-Ausgabe (für Debug)
Console.WriteLine(logEntry);
// Datei-Ausgabe (für Persistenz)
try
{
File.AppendAllText(LogFilePath, logEntry + Environment.NewLine);
}
catch
{
// Fehler beim Schreiben ignorieren um App nicht zum Absturz zu bringen
}
}
/// <summary>
/// Protokolliert eine Exception mit Stack-Trace
/// </summary>
/// <param name="ex">Exception die protokolliert werden soll</param>
/// <param name="context">Zusätzlicher Kontext</param>
public static void LogException(Exception ex, string context = "")
{
if (ex == null)
{
Log("Null-Exception übergeben");
return;
}
var message = string.IsNullOrWhiteSpace(context)
? $"EXCEPTION: {ex.Message}\nStack: {ex.StackTrace}"
: $"EXCEPTION in {context}: {ex.Message}\nStack: {ex.StackTrace}";
Log(message);
}
/// <summary>
/// Protokolliert eine Warnung
/// </summary>
/// <param name="message">Warnung</param>
public static void LogWarning(string message)
{
Log($"WARNING: {message}");
}
/// <summary>
/// Protokolliert einen Fehler
/// </summary>
/// <param name="message">Fehlermeldung</param>
public static void LogError(string message)
{
Log($"ERROR: {message}");
}
/// <summary>
/// Protokolliert Debug-Informationen (nur in Debug-Build)
/// </summary>
/// <param name="message">Debug-Nachricht</param>
[System.Diagnostics.Conditional("DEBUG")]
public static void LogDebug(string message)
{
Log($"DEBUG: {message}");
}
/// <summary>
/// Löscht die Log-Datei (für Cleanup)
/// </summary>
public static void ClearLog()
{
try
{
if (File.Exists(LogFilePath))
{
File.Delete(LogFilePath);
Log("Log-Datei gelöscht");
}
}
catch (Exception ex)
{
Log($"Fehler beim Löschen der Log-Datei: {ex.Message}");
}
}
/// <summary>
/// Gibt den Pfad zur Log-Datei zurück
/// </summary>
/// <returns>Vollständiger Pfad zur Log-Datei</returns>
public static string GetLogFilePath()
{
return LogFilePath;
}
}
}

View File

@ -1,9 +0,0 @@
namespace Project_Periodensystem.Model
{
public enum AppTheme
{
Dark,
Light,
Classic
}
}

View File

@ -1,7 +1,6 @@
<!-- Datei: Project_Periodensystem.View/AboutPage.axaml --> <!-- Datei: Project_Periodensystem.View/AboutPage.axaml -->
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="using:Avalonia.Interactivity"
x:Class="Project_Periodensystem.View.AboutPage"> x:Class="Project_Periodensystem.View.AboutPage">
<UserControl.Styles> <UserControl.Styles>
@ -10,10 +9,11 @@
<Setter Property="Foreground" Value="White"/> <Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Arial"/> <Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/> <Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Width" Value="250"/> <Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="40"/> <Setter Property="Height" Value="40"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="CornerRadius" Value="5"/>
</Style> </Style>
<Style Selector="Button:pointerover /template/ ContentPresenter"> <Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="DarkRed"/> <Setter Property="Background" Value="DarkRed"/>
@ -22,49 +22,142 @@
<Setter Property="Background" Value="Red"/> <Setter Property="Background" Value="Red"/>
</Style> </Style>
<Style Selector="Button TextBlock"> <Style Selector="Button TextBlock">
<Setter Property="FontSize" Value="16"/> <Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Arial"/> <Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/> <Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<!-- Spezielle Styles für About-Seite -->
<Style Selector="TextBlock.Title">
<Setter Property="FontSize" Value="36"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,0,30"/>
</Style>
<Style Selector="TextBlock.Subtitle">
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,10"/>
</Style>
<Style Selector="TextBlock.InfoBold">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,5"/>
</Style>
<Style Selector="TextBlock.Info">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,3"/>
</Style>
<!-- Einfache Theme-unabhängige Styles -->
<Style Selector="Border.InfoCard">
<Setter Property="CornerRadius" Value="10"/>
<Setter Property="Padding" Value="30,25"/>
<Setter Property="Margin" Value="20"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BoxShadow" Value="0 4 8 0 #40000000"/>
</Style>
<!-- Theme-Ressourcen verwenden -->
<Style Selector="Border.InfoCard">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundChromeMediumBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"/>
</Style>
<Style Selector="Rectangle.Divider">
<Setter Property="Fill" Value="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"/>
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Grid> <Grid Margin="20">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Content --> <!-- Titel -->
<StackPanel Grid.Row="0" <TextBlock Grid.Row="1"
VerticalAlignment="Center" Classes="Title"
HorizontalAlignment="Center"> Text="Periodensystem der Elemente"/>
<TextBlock Text="Author: Oliver Träger"
FontSize="24"
Margin="0,0,0,20"/>
<TextBlock Text="Klasse: ITFS2"
FontSize="24"
Margin="0,0,0,20"/>
</StackPanel>
<!-- Footer with Buttons --> <!-- Info-Card -->
<Grid Grid.Row="1" <Border Grid.Row="2"
HorizontalAlignment="Stretch" Classes="InfoCard"
Margin="20,0,20,20"> MaxWidth="600">
<StackPanel>
<!-- Projekt Info -->
<TextBlock Classes="Subtitle"
Text="Projekt Information"/>
<Rectangle Classes="Divider"
Height="2"
Margin="0,10,0,20"/>
<TextBlock Classes="InfoBold"
Text="Author:"/>
<TextBlock Classes="Info"
Text="Oliver Träger"/>
<TextBlock Classes="InfoBold"
Text="Klasse:"
Margin="0,15,0,0"/>
<TextBlock Classes="Info"
FontWeight="Bold"
Text="ITFS2"/>
<TextBlock Classes="InfoBold"
Text="Semester:"
Margin="0,15,0,0"/>
<TextBlock Classes="Info"
FontWeight="Bold"
Text="Sommersemester 2025"/>
<Rectangle Classes="Divider"
Height="2"
Margin="0,25,0,15"/>
<!-- Technische Details -->
<TextBlock Classes="Info"
Text="Entwickelt mit Avalonia UI Framework"/>
<TextBlock Classes="Info"
Text="MVC-Architektur mit C# .NET"/>
<TextBlock Classes="Info"
Text="Interaktives Periodensystem mit 118 Elementen"/>
</StackPanel>
</Border>
<!-- Button-Bereich -->
<Grid Grid.Row="3"
Margin="0,30,0,0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Grid.Column="0" <Button Name="BackButton"
HorizontalAlignment="Left" Grid.Column="1"
Click="ThemeButton_Click"> Click="BackButton_Click">
<TextBlock Text="Theme wechseln"/> <TextBlock Text="← Zurück"/>
</Button> </Button>
<Button Grid.Column="1" <Button Name="ThemeButton"
HorizontalAlignment="Right" Grid.Column="3"
Click="BackButton_Click"> Click="ThemeButton_Click">
<TextBlock Text="Zurück"/> <TextBlock Text="Theme wechseln"/>
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -8,44 +8,67 @@ using Project_Periodensystem.Model;
namespace Project_Periodensystem.View namespace Project_Periodensystem.View
{ {
/// <summary>
/// Code-Behind für die About-Seite
/// </summary>
public partial class AboutPage : UserControl public partial class AboutPage : UserControl
{ {
/// <summary>
/// Konstruktor
/// </summary>
public AboutPage() public AboutPage()
{ {
InitializeComponent(); InitializeComponent();
} }
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (this.GetVisualRoot() is MainWindow mainWindow)
{
mainWindow.UpdateTheme(MainWindow.CurrentTheme);
}
}
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
/// <summary>
/// Zurück-Button - Navigation zur Landing Page
/// </summary>
private void BackButton_Click(object? sender, RoutedEventArgs e) private void BackButton_Click(object? sender, RoutedEventArgs e)
{ {
if (MainWindow.Instance != null) try
{ {
MainWindow.Instance.ShowLandingPage(); var mainWindow = TopLevel.GetTopLevel(this) as Window;
if (mainWindow != null)
{
var landingPage = new LandingPage();
mainWindow.Content = landingPage;
Logger.Log("Navigation zurück zur Landing Page");
}
}
catch (System.Exception ex)
{
Logger.Log($"Fehler bei Navigation zurück: {ex.Message}");
} }
} }
/// <summary>
/// Event-Handler für Theme-Button - VEREINHEITLICHT mit anderen Seiten
/// </summary>
private void ThemeButton_Click(object? sender, RoutedEventArgs e) private void ThemeButton_Click(object? sender, RoutedEventArgs e)
{ {
var themes = Enum.GetValues<AppTheme>(); try
MainWindow.CurrentTheme = themes[(Array.IndexOf(themes, MainWindow.CurrentTheme) + 1) % themes.Length];
if (MainWindow.Instance != null)
{ {
MainWindow.Instance.UpdateTheme(MainWindow.CurrentTheme); // GLEICHE LOGIK wie auf LandingPage und PeriodicTablePage
var app = Application.Current;
if (app != null)
{
var currentTheme = app.ActualThemeVariant;
app.RequestedThemeVariant = currentTheme == Avalonia.Styling.ThemeVariant.Dark
? Avalonia.Styling.ThemeVariant.Light
: Avalonia.Styling.ThemeVariant.Dark;
Logger.Log($"Theme gewechselt zu: {app.RequestedThemeVariant}");
}
}
catch (Exception ex)
{
Logger.Log($"Fehler beim Theme-Wechsel: {ex.Message}");
} }
} }
} }

View File

@ -1,7 +1,9 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Project_Periodensystem.View.App"> x:Class="Project_Periodensystem.View.App"
<Application.Styles> RequestedThemeVariant="Dark">
<FluentTheme />
</Application.Styles> <Application.Styles>
<FluentTheme />
</Application.Styles>
</Application> </Application>

View File

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Project_Periodensystem.Model;
namespace Project_Periodensystem.View namespace Project_Periodensystem.View
{ {
@ -15,7 +16,11 @@ namespace Project_Periodensystem.View
{ {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
// Standard Dark Theme beim Start
RequestedThemeVariant = Avalonia.Styling.ThemeVariant.Dark;
desktop.MainWindow = new MainWindow(); desktop.MainWindow = new MainWindow();
Logger.Log("Application initialized with Dark theme");
} }
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();

View File

@ -8,6 +8,8 @@
<Setter Property="Foreground" Value="White"/> <Setter Property="Foreground" Value="White"/>
<Setter Property="FontFamily" Value="Arial"/> <Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/> <Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Width" Value="250"/>
<Setter Property="Height" Value="40"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/>
</Style> </Style>
@ -17,50 +19,53 @@
<Style Selector="Button:pressed /template/ ContentPresenter"> <Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="Red"/> <Setter Property="Background" Value="Red"/>
</Style> </Style>
<Style Selector="Button TextBlock">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</UserControl.Styles> </UserControl.Styles>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Hauptcontent --> <TextBlock Grid.Row="1"
<Grid Grid.Row="0"> Text="Periodensystem der Elemente"
<StackPanel VerticalAlignment="Center" FontSize="32"
Margin="0,-100,0,0"> FontWeight="Bold"
<TextBlock Text="Willkommen zum" HorizontalAlignment="Center"
FontSize="36" Margin="0,0,0,50"/>
FontWeight="Light"
HorizontalAlignment="Center"
Margin="0,0,0,10"/>
<TextBlock Text="Periodensystem der Elemente!" <Button Grid.Row="2"
FontSize="72" Name="PeriodicTableButton"
FontWeight="Bold" Click="PeriodicTableButton_Click"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Margin="0,0,0,40"/> Margin="0,10">
<TextBlock Text="Periodensystem anzeigen"/>
</Button>
<Button Name="StartButton" <Button Grid.Row="3"
Width="200" Name="ThemeButton"
Height="60"
HorizontalAlignment="Center"
Click="StartButton_Click">
<TextBlock Text="Start" FontSize="24"/>
</Button>
</StackPanel>
</Grid>
<!-- Theme Button -->
<Button Name="ThemeButton"
Grid.Row="1"
Click="ThemeButton_Click" Click="ThemeButton_Click"
HorizontalAlignment="Left" HorizontalAlignment="Center"
VerticalAlignment="Bottom" Margin="0,10">
Width="250" <TextBlock Text="Theme wechseln"/>
Height="40" </Button>
Margin="20,0,0,20">
<TextBlock Text="Theme wechseln" FontSize="16"/> <Button Grid.Row="4"
Name="AboutButton"
Click="AboutButton_Click"
HorizontalAlignment="Center"
Margin="0,10">
<TextBlock Text="About"/>
</Button> </Button>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -1,79 +1,95 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.VisualTree;
using Project_Periodensystem.Model; using Project_Periodensystem.Model;
using System;
using System.Collections.Generic;
namespace Project_Periodensystem.View namespace Project_Periodensystem.View
{ {
/// <summary>
/// Code-Behind für die Landing Page
/// </summary>
public partial class LandingPage : UserControl public partial class LandingPage : UserControl
{ {
private readonly Random random = new(); /// <summary>
private readonly Dictionary<AppTheme, string> themeColors; /// Konstruktor
private DateTime lastClickTime = DateTime.MinValue; /// </summary>
private const int DEBOUNCE_MS = 500;
public LandingPage() public LandingPage()
{ {
Logger.Log($"=== LandingPage wird initialisiert am {DateTime.Now:dd.MM.yyyy HH:mm:ss} ===");
InitializeComponent(); InitializeComponent();
themeColors = new Dictionary<AppTheme, string>
{
{ AppTheme.Dark, "#5C5144" },
{ AppTheme.Light, "#E8DFD8" },
{ AppTheme.Classic, "#7B8B6F" }
};
Logger.Log("LandingPage initialisiert");
} }
private void InitializeComponent() /// <summary>
/// Event-Handler für Periodensystem-Button
/// </summary>
private void PeriodicTableButton_Click(object? sender, RoutedEventArgs e)
{ {
AvaloniaXamlLoader.Load(this); try
}
private void ThemeButton_Click(object? sender, RoutedEventArgs e)
{
var themes = Enum.GetValues<AppTheme>();
MainWindow.CurrentTheme = themes[(Array.IndexOf(themes, MainWindow.CurrentTheme) + 1) % themes.Length];
if (MainWindow.Instance != null)
{ {
MainWindow.Instance.UpdateTheme(MainWindow.CurrentTheme); var mainWindow = TopLevel.GetTopLevel(this) as Window;
if (mainWindow != null)
{
var periodicTablePage = new PeriodicTablePage();
mainWindow.Content = periodicTablePage;
Logger.Log("Navigation zum Periodensystem");
}
}
catch (System.Exception ex)
{
Logger.Log($"Fehler bei Navigation zum Periodensystem: {ex.Message}");
} }
} }
private void StartButton_Click(object? sender, RoutedEventArgs e) /// <summary>
/// Event-Handler für About-Button
/// </summary>
private void AboutButton_Click(object? sender, RoutedEventArgs e)
{ {
// Add multiple ways to see if this method is called
System.Diagnostics.Debug.WriteLine("StartButton_Click CALLED!");
Console.WriteLine("StartButton_Click CALLED!");
// Remove the MessageBox line - it's not available
Logger.Log("=== StartButton_Click CALLED ===");
try try
{ {
if (MainWindow.Instance != null) var mainWindow = TopLevel.GetTopLevel(this) as Window;
if (mainWindow != null)
{ {
Logger.Log("Found MainWindow instance - calling ShowPeriodicTable"); var aboutPage = new AboutPage();
MainWindow.Instance.ShowPeriodicTable(); mainWindow.Content = aboutPage;
Logger.Log("ShowPeriodicTable called successfully"); Logger.Log("Navigation zur About-Seite");
}
}
catch (System.Exception ex)
{
Logger.Log($"Fehler bei Navigation zu About: {ex.Message}");
}
}
/// <summary>
/// Event-Handler für Theme-Button
/// </summary>
private void ThemeButton_Click(object? sender, RoutedEventArgs e)
{
try
{
Logger.Log("Theme-Button geklickt");
var app = Application.Current;
if (app != null)
{
var currentTheme = app.ActualThemeVariant;
Logger.Log($"Aktuelles Theme: {currentTheme}");
var newTheme = currentTheme == Avalonia.Styling.ThemeVariant.Dark
? Avalonia.Styling.ThemeVariant.Light
: Avalonia.Styling.ThemeVariant.Dark;
app.RequestedThemeVariant = newTheme;
Logger.Log($"Theme gewechselt zu: {newTheme}");
} }
else else
{ {
Logger.Log("MainWindow.Instance is null!"); Logger.Log("Application.Current ist null!");
} }
} }
catch (Exception ex) catch (System.Exception ex)
{ {
Logger.Log($"ERROR in StartButton_Click: {ex.Message}"); Logger.Log($"Fehler beim Theme-Wechsel: {ex.Message}");
} }
} }
} }

View File

@ -1,152 +1,65 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.VisualTree;
using Avalonia.Threading;
using Project_Periodensystem.Model; using Project_Periodensystem.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using static Project_Periodensystem.Model.AppTheme;
namespace Project_Periodensystem.View namespace Project_Periodensystem.View
{ {
/// <summary>
/// Vereinfachtes MainWindow - nur noch Container für Content
/// </summary>
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private ContentControl? mainContent; private ContentControl? mainContent;
public static AppTheme CurrentTheme { get; set; } = AppTheme.Dark;
public static MainWindow? Instance { get; private set; } // Add this line
public static Dictionary<AppTheme, (string Background, string Foreground)> ThemeColors { get; } = new()
{
{ AppTheme.Dark, ("#2F2F2F", "#FFFFFF") },
{ AppTheme.Light, ("#FFFFFF", "#000000") },
{ AppTheme.Classic, ("#E8E8E8", "#000000") }
};
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
Instance = this; // Set the static reference
mainContent = this.FindControl<ContentControl>("MainContent"); mainContent = this.FindControl<ContentControl>("MainContent");
ShowLandingPage(); ShowLandingPage();
} }
/// <summary>
/// Zeigt die Landing Page an
/// </summary>
public void ShowLandingPage() public void ShowLandingPage()
{ {
mainContent!.Content = new LandingPage(); if (mainContent != null)
Dispatcher.UIThread.Post(() => UpdateTheme(CurrentTheme), DispatcherPriority.Loaded); {
mainContent.Content = new LandingPage();
Logger.Log("Landing Page angezeigt");
}
} }
/// <summary>
/// Zeigt das Periodensystem an
/// </summary>
public void ShowPeriodicTable() public void ShowPeriodicTable()
{ {
Logger.Log("ShowPeriodicTable called"); Logger.Log("ShowPeriodicTable called");
try try
{ {
mainContent!.Content = new PeriodicTablePage(); if (mainContent != null)
Logger.Log("PeriodicTablePage created and set"); {
Dispatcher.UIThread.Post(() => UpdateTheme(CurrentTheme), DispatcherPriority.Loaded); mainContent.Content = new PeriodicTablePage();
Logger.Log("Theme update posted"); Logger.Log("PeriodicTablePage created and set");
}
} }
catch (Exception ex) catch (System.Exception ex)
{ {
Logger.Log($"Error in ShowPeriodicTable: {ex.Message}"); Logger.Log($"Error in ShowPeriodicTable: {ex.Message}");
} }
} }
/// <summary>
/// Zeigt die About Page an
/// </summary>
public void ShowAboutPage() public void ShowAboutPage()
{ {
mainContent!.Content = new AboutPage(); if (mainContent != null)
Dispatcher.UIThread.Post(() => UpdateTheme(CurrentTheme), DispatcherPriority.Loaded);
}
public void UpdateTheme(AppTheme theme)
{
CurrentTheme = theme;
if (mainContent?.Content is UserControl control)
{ {
var (background, foreground) = ThemeColors[theme]; mainContent.Content = new AboutPage();
control.Background = new SolidColorBrush(Color.Parse(background)); Logger.Log("About Page angezeigt");
// For PeriodicTablePage, use a timer to ensure visual tree is ready
if (control is PeriodicTablePage)
{
// Wait a bit longer for the visual tree to be fully constructed
var timer = new System.Timers.Timer(100); // 100ms delay
timer.Elapsed += (sender, e) =>
{
timer.Stop();
timer.Dispose();
Dispatcher.UIThread.Post(() =>
{
try
{
var allButtons = control.GetVisualDescendants().OfType<Button>().ToList();
Logger.Log($"Timer: Found {allButtons.Count} buttons in PeriodicTablePage");
foreach (var button in allButtons)
{
// Skip navigation buttons
if ((button.Width == 250 && button.Height == 40) ||
(button.Width == 200 && button.Height == 40))
{
continue;
}
// Force white text for element tiles
button.Foreground = new SolidColorBrush(Colors.WhiteSmoke);
// Force white for TextBlocks inside
var buttonTextBlocks = button.GetVisualDescendants().OfType<TextBlock>().ToList();
foreach (var tb in buttonTextBlocks)
{
tb.Foreground = new SolidColorBrush(Colors.White);
Logger.Log($"Timer: Set element tile text to white: '{tb.Text}'");
}
}
}
catch (Exception ex)
{
Logger.Log($"Timer error: {ex.Message}");
}
});
};
timer.Start();
}
// Update regular text colors
var textBlocks = control.GetVisualDescendants().OfType<TextBlock>().ToList();
foreach (var textBlock in textBlocks)
{
var parent = textBlock.Parent;
if (parent is Button)
continue;
textBlock.Foreground = new SolidColorBrush(Color.Parse(foreground));
}
} }
} }
private bool IsElementTileButton(Button button)
{
// Check if this button has element-related content or styling
if (button.Content is StackPanel stackPanel)
{
var textBlocks = stackPanel.Children.OfType<TextBlock>().ToList();
if (textBlocks.Count >= 2) // Element tiles typically have symbol and number
{
return true;
}
}
// Check if button content is directly a TextBlock with short text
if (button.Content is TextBlock textBlock &&
textBlock.Text?.Length <= 3)
{
return true;
}
return false;
}
} }
} }

View File

@ -193,20 +193,33 @@
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Name="ThemeButton" <Button Name="BackButton"
Grid.Column="0" Grid.Column="0"
Click="BackButton_Click"
Width="200"
Height="40">
<TextBlock Text="← Zurück"
FontSize="16"
HorizontalAlignment="Center"/>
</Button>
<Button Name="ThemeButton"
Grid.Column="2"
Click="ThemeButton_Click" Click="ThemeButton_Click"
Width="250" Width="250"
Height="40"> Height="40"
HorizontalAlignment="Center">
<TextBlock Text="Theme wechseln" <TextBlock Text="Theme wechseln"
FontSize="16" FontSize="16"
HorizontalAlignment="Center"/> HorizontalAlignment="Center"/>
</Button> </Button>
<Button x:Name="AboutButton" <Button x:Name="AboutButton"
Grid.Column="2" Grid.Column="4"
Click="AboutButton_Click" Click="AboutButton_Click"
Width="200" Width="200"
Height="40"> Height="40">

View File

@ -1,10 +1,9 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Project_Periodensystem.Model; // ElementModel should be here using Project_Periodensystem.Controller;
using Project_Periodensystem.Persistence; using Project_Periodensystem.Model;
using Project_Periodensystem.View.Converters; using Project_Periodensystem.View.Converters;
using System; using System;
using System.Globalization; using System.Globalization;
@ -12,92 +11,105 @@ using System.Linq;
namespace Project_Periodensystem.View namespace Project_Periodensystem.View
{ {
/// <summary>
/// Code-Behind für das Periodensystem - nur UI-Logik, keine Geschäftslogik
/// </summary>
public partial class PeriodicTablePage : UserControl public partial class PeriodicTablePage : UserControl
{ {
private readonly Grid? periodicGrid; private readonly Grid? periodicGrid;
private readonly PeriodensystemController _controller;
/// <summary>
/// Konstruktor - initialisiert UI und Controller
/// </summary>
public PeriodicTablePage() public PeriodicTablePage()
{ {
InitializeComponent(); InitializeComponent();
periodicGrid = this.Find<Grid>("PeriodicGrid");
InitializePeriodicTable(); // Controller initialisieren (MVC-Pattern)
_controller = new PeriodensystemController();
// Grid-Referenz für Element-Buttons
periodicGrid = this.FindControl<Grid>("PeriodicGrid");
// Elemente laden und anzeigen
LoadAndDisplayElements();
} }
private void InitializeComponent() /// <summary>
{ /// Lädt Elemente über Controller und zeigt sie an
AvaloniaXamlLoader.Load(this); /// </summary>
} private void LoadAndDisplayElements()
private void InitializePeriodicTable()
{ {
try try
{ {
Logger.Log("Initialisiere PeriodicTable..."); // Daten über Controller laden (nicht direkt!)
var elements = _controller.GetAllElements();
// Prüfen ob das Grid gefunden wurde if (!elements.Any())
if (periodicGrid == null)
{ {
Logger.Log("FEHLER: PeriodicGrid nicht gefunden!"); Logger.Log("Keine Elemente vom Controller erhalten");
return; return;
} }
// Prüfen ob Elemente vorhanden sind // Datenvalidierung über Controller
if (PeriodicTableData.Elements == null || !PeriodicTableData.Elements.Any()) if (!_controller.ValidateData())
{ {
Logger.Log("FEHLER: Keine Elemente in PeriodicTableData!"); Logger.Log("Datenvalidierung fehlgeschlagen");
return; return;
} }
// Debug: Alle Elemente ausgeben Logger.Log($"Lade {elements.Count} Elemente in das Grid");
Logger.Log($"Anzahl Elemente: {PeriodicTableData.Elements.Count()}");
var firstFew = PeriodicTableData.Elements.Take(5); // UI-Update für jedes Element
foreach (var el in firstFew) int successCount = 0;
foreach (var element in elements)
{ {
Logger.Log($"Element: {el.Symbol} - Row: {el.Row}, Column: {el.Column}"); if (_controller.ValidateElementPosition(element))
{
CreateElementButton(element);
successCount++;
}
else
{
Logger.Log($"Element {element?.Symbol ?? "NULL"} übersprungen - ungültige Position");
}
} }
// Speziell nach H und He suchen Logger.Log($"{successCount} von {elements.Count} Elementen erfolgreich geladen");
var hydrogen = PeriodicTableData.Elements.FirstOrDefault(e => e.Symbol == "H");
var helium = PeriodicTableData.Elements.FirstOrDefault(e => e.Symbol == "He");
if (hydrogen != null)
Logger.Log($"Wasserstoff gefunden: Row {hydrogen.Row}, Column {hydrogen.Column}");
else
Logger.Log("Wasserstoff NICHT gefunden!");
if (helium != null)
Logger.Log($"Helium gefunden: Row {helium.Row}, Column {helium.Column}");
else
Logger.Log("Helium NICHT gefunden!");
// Elemente hinzufügen
foreach (var element in PeriodicTableData.Elements)
{
Logger.Log($"Füge Element hinzu: {element.Symbol} ({element.Row},{element.Column})");
CreateElementButton(element);
}
Logger.Log("PeriodicTable wurde initialisiert");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Log($"FEHLER beim Initialisieren des PeriodicTable: {ex.Message}"); Logger.Log($"EXCEPTION in LoadAndDisplayElements: {ex.Message}");
Logger.Log($"StackTrace: {ex.StackTrace}");
} }
} }
/// <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)
{
Logger.Log("Null-Element kann nicht angezeigt werden");
return;
}
// UI-Komponenten erstellen
var button = new Button { Classes = { "ElementTile" } }; var button = new Button { Classes = { "ElementTile" } };
var panel = new StackPanel(); var panel = new StackPanel();
// Set background color based on element series // Hintergrundfarbe über Converter setzen
var backgroundColor = new SeriesToColorConverter().Convert(element.Series, typeof(Brush), null, CultureInfo.InvariantCulture) as Brush; var backgroundColor = new SeriesToColorConverter()
.Convert(element.Series, typeof(Brush), null, CultureInfo.InvariantCulture) as Brush;
if (backgroundColor != null) if (backgroundColor != null)
{ {
button.Background = backgroundColor; button.Background = backgroundColor;
} }
// Text-Elemente erstellen
var symbolText = new TextBlock var symbolText = new TextBlock
{ {
Text = element.Symbol, Text = element.Symbol,
@ -114,46 +126,96 @@ namespace Project_Periodensystem.View
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center
}; };
// Layout zusammenbauen
panel.Children.Add(numberText); // Number on top panel.Children.Add(numberText); // Number on top
panel.Children.Add(symbolText); // Symbol below panel.Children.Add(symbolText); // Symbol below
button.Content = panel; button.Content = panel;
// FIX: Korrekte Grid-Positionierung ohne Math.Max für Spalten! // Grid-Position setzen (0-basiert)
int gridRow = element.Row; // Row direkt verwenden (0-basiert) int gridRow = element.Row;
int gridColumn = element.Column; // Column direkt verwenden (0-basiert) int gridColumn = element.Column;
Logger.Log($"Element {element.Symbol}: Original({element.Row},{element.Column}) -> Grid({gridRow},{gridColumn})"); Logger.Log($"Element {element.Symbol}: Grid({gridRow},{gridColumn})");
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);
Logger.Log($"Element {element.Symbol} wurde zum Grid hinzugefügt an Position ({gridRow},{gridColumn})"); }
else
{
Logger.Log("PeriodicGrid ist null - Button kann nicht hinzugefügt werden");
} }
} }
/// <summary>
/// Event-Handler für Theme-Button
/// </summary>
private void ThemeButton_Click(object? sender, RoutedEventArgs e) private void ThemeButton_Click(object? sender, RoutedEventArgs e)
{ {
var nextTheme = MainWindow.CurrentTheme switch try
{ {
AppTheme.Dark => AppTheme.Light, // Theme-Wechsel über Application
AppTheme.Light => AppTheme.Classic, var app = Application.Current;
_ => AppTheme.Dark if (app != null)
}; {
var currentTheme = app.ActualThemeVariant;
app.RequestedThemeVariant = currentTheme == Avalonia.Styling.ThemeVariant.Dark
? Avalonia.Styling.ThemeVariant.Light
: Avalonia.Styling.ThemeVariant.Dark;
if (this.Parent is ContentControl content && content.Parent is MainWindow mainWindow) Logger.Log($"Theme gewechselt zu: {app.RequestedThemeVariant}");
}
}
catch (Exception ex)
{ {
mainWindow.UpdateTheme(nextTheme); Logger.Log($"Fehler beim Theme-Wechsel: {ex.Message}");
} }
} }
/// <summary>
/// Event-Handler für About-Button
/// </summary>
private void AboutButton_Click(object? sender, RoutedEventArgs e) private void AboutButton_Click(object? sender, RoutedEventArgs e)
{ {
if (this.Parent is ContentControl content && content.Parent is MainWindow mainWindow) try
{ {
mainWindow.ShowAboutPage(); // Navigation zur About-Seite
var mainWindow = TopLevel.GetTopLevel(this) as Window;
if (mainWindow != null)
{
var aboutPage = new AboutPage();
mainWindow.Content = aboutPage;
Logger.Log("Navigation zur About-Seite");
}
}
catch (Exception ex)
{
Logger.Log($"Fehler bei Navigation zu About: {ex.Message}");
}
}
/// <summary>
/// Event-Handler für Zurück-Button (falls gewünscht)
/// </summary>
private void BackButton_Click(object? sender, RoutedEventArgs e)
{
try
{
var mainWindow = TopLevel.GetTopLevel(this) as Window;
if (mainWindow != null)
{
var landingPage = new LandingPage();
mainWindow.Content = landingPage;
Logger.Log("Navigation zurück zur Landing Page");
}
}
catch (Exception ex)
{
Logger.Log($"Fehler bei Navigation zurück: {ex.Message}");
} }
} }
} }