Überarbeite Fahrzeugverwaltung

This commit is contained in:
younes elhaddoury 2025-08-31 17:11:58 +02:00
parent 127a53f003
commit b0bb865a87
8 changed files with 364 additions and 1204 deletions

150
README.md
View File

@ -1,138 +1,24 @@
# WpfApp4AutoHandlung
# 🚗 Fahrzeugverwaltung
# Fahrzeugverwaltung
Ein modernes C# WPF-Anwendungssystem zur effizienten Verwaltung von Fahrzeugen mit automatischer Wertermittlung und PDF-Berichtserstellung.
Ein einfaches WPF-Projekt zur Verwaltung von Fahrzeugen. Daten werden in einer lokalen SQLite-Datenbank gespeichert.
## 📋 Projektübersicht
## Funktionen
- Fahrzeuge anlegen, bearbeiten und löschen
- Suche und Übersicht mit DataGrid
- Export eines Fahrzeugs als PDF
- Berechnung eines ungefähren Fahrzeugwerts
Die Fahrzeugverwaltung ist eine Desktop-Anwendung, die es Autohändlern, Fuhrparkmanagern und Privatpersonen ermöglicht, ihre Fahrzeuge strukturiert zu verwalten. Das System bietet eine intuitive Benutzeroberfläche zur Erfassung von Fahrzeugdaten, automatische Wertberechnung basierend auf Alter und Kilometerstand sowie professionelle PDF-Berichte.
## Bedienung
1. Marke und Modell wählen, restliche Felder ausfüllen.
2. **Hinzufügen** speichert ein neues Fahrzeug.
3. Ein Fahrzeug in der Liste auswählen und mit **Bearbeiten** ändern oder mit **Löschen** entfernen.
4. **Als PDF speichern** erzeugt ein Informationsblatt.
## ✨ Features
## Entwicklung
Dieses Projekt wurde im Rahmen einer Übung von **Younes**, **Saad** und **Ayman** erstellt.
### 🔧 Fahrzeugverwaltung
- **Vollständige Datenverwaltung**: Marke, Modell, Baujahr, Leistung, Kilometerstand, Farbe und Kaufpreis
- **Automatische Wertberechnung**: Intelligente Ermittlung des aktuellen Fahrzeugwerts
- **Oldtimer-Erkennung**: Automatische Klassifizierung von Fahrzeugen über 30 Jahren
- **Formatierte Anzeige**: Benutzerfreundliche Darstellung von Preisen und Kilometerständen
### 📊 Berichte & Export
- **Einzelfahrzeug-PDF**: Detaillierte Infoblätter mit allen Fahrzeugdaten
- **Fahrzeugliste-PDF**: Übersichtliche Tabelle aller verwalteten Fahrzeuge
- **Zusammenfassungen**: Gesamtwerte, Wertverluste und statistische Auswertungen
- **Professionelles Design**: Moderne PDF-Layouts mit strukturierten Tabellen
### 🧮 Wertermittlung
- **Altersbasierte Bewertung**: 15% Wertverlust in den ersten 3 Jahren, dann 10% jährlich
- **Kilometerstand-Faktor**: 2% Wertverlust pro 10.000 km
- **Mindestwert-Schutz**: Automatische Begrenzung auf mindestens 5% des Kaufpreises
- **Transparente Berechnung**: Nachvollziehbare Bewertungslogik
## 🛠️ Technologie-Stack
- **Framework**: .NET Framework / .NET Core
- **UI-Technologie**: WPF (Windows Presentation Foundation)
- **PDF-Generierung**: iTextSharp
- **Datenbank** : SQLite
- **Programmiersprache**: C#
- **Architektur**: MVVM Pattern
## 📦 Installation
### Voraussetzungen
- Windows 10 oder höher
- .NET Framework 4.8 oder .NET 6.0+
- Visual Studio 2019+ (für Entwicklung)
## 🚀 Verwendung
### Neues Fahrzeug hinzufügen
1. Anwendung starten
2. "Neues Fahrzeug" Button klicken
3. Fahrzeugdaten eingeben
4. Speichern - automatische Wertberechnung erfolgt
## 🎯 Kernkomponenten
### Fahrzeug-Klasse
- Eigenschaften für alle Fahrzeugdaten
- Automatische Formatierung von Preisen und Kilometerständen
- Berechnungsmethoden für Alter und aktuellen Wert
- Oldtimer-Statusprüfung
### PdfService
- Erstellung detaillierter Fahrzeug-Infoblätter
- Generierung von Fahrzeuglisten im Tabellenformat
- Professionelle Formatierung mit iTextSharp
- Automatische Zusammenfassungen und Statistiken
## 🔄 Wertberechnungslogik
```csharp
// Vereinfachtes Beispiel der Wertberechnung
public decimal BerechneAktuellenWert()
{
decimal wert = Kaufpreis;
// Altersbasierte Abschreibung
int alter = GetAlter();
for (int i = 0; i < alter; i++)
{
decimal abschreibung = (i < 3) ? 0.15m : 0.10m;
wert *= (1 - abschreibung);
}
// Kilometerstand-Abschreibung
decimal kmAbschreibung = (Kilometerstand / 10000) * 0.02m;
wert *= (1 - kmAbschreibung);
// Mindestwert sicherstellen
decimal mindestwert = Kaufpreis * 0.05m;
return Math.Max(wert, mindestwert);
}
## Start
```
## 🤝 Beitragen
Beiträge sind willkommen! Bitte beachten Sie:
1. Fork des Repositories
2. Feature-Branch erstellen (`git checkout -b feature/AmazingFeature`)
3. Änderungen committen (`git commit -m 'Add some AmazingFeature'`)
4. Branch pushen (`git push origin feature/AmazingFeature`)
5. Pull Request erstellen
## 📋 Roadmap
- [ ] Datenbankintegration (SQLite/SQL Server)
- [ ] Erweiterte Suchfunktionen
- [ ] Fahrzeugbilder-Verwaltung
- [ ] Export nach Excel
- [ ] Wartungshistorie
- [ ] Benachrichtigungen für TÜV/Wartung
- [ ] Multi-User-Unterstützung
- [ ] Web-API für externe Systeme
## 🐛 Bekannte Probleme
- PDF-Generierung kann bei sehr großen Listen langsam sein
- PDF-Öffnung direkt nach dem Saving funktioniert nicht
- Währungsformatierung ist derzeit nur für EUR optimiert
- Keine Unterstützung für Fahrzeuge vor 1900
## 📜 Lizenz
Dieses Projekt ist unter der YSA Lizenz lizenziert - siehe [LICENSE](LICENSE) Datei für Details.
## 👥 Autor
**El Haddoury Younes Ayman Alshian Saad Akki ** - *Initial work*
## 🙏 Danksagungen
- iTextSharp Team für die ausgezeichnete PDF-Bibliothek
- Microsoft für das WPF Framework
- Allen Testern und Contributoren
---
**Gefällt Ihnen das Projekt? Geben Sie uns einen Stern!**
dotnet build
```
Die Anwendung kann anschließend mit Visual Studio oder `dotnet run` gestartet werden.

View File

@ -6,15 +6,15 @@ namespace FahrzeugVerwaltung
public partial class App : Application
{
// startet die Anwendung und richtet Fehlerbehandlung ein
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
DispatcherUnhandledException += App_DispatcherUnhandledException;
}
// behandelt UI-Ausnahmen
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show($"Ein unerwarteter Fehler ist aufgetreten:\n{e.Exception.Message}",
@ -22,6 +22,7 @@ namespace FahrzeugVerwaltung
e.Handled = true;
}
// behandelt kritische Ausnahmen
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show($"Ein kritischer Fehler ist aufgetreten:\n{((Exception)e.ExceptionObject).Message}",

View File

@ -1,8 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
namespace FahrzeugVerwaltung
{
@ -18,18 +17,12 @@ namespace FahrzeugVerwaltung
InitializeDatabase();
}
/// <summary>
/// Initialisiert die Datenbank und erstellt die Tabelle falls sie nicht existiert
/// </summary>
// erstellt die Datenbank und Tabelle
private void InitializeDatabase()
{
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string createTableQuery = @"
string query = @"
CREATE TABLE IF NOT EXISTS Fahrzeuge (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Marke TEXT NOT NULL,
@ -41,80 +34,42 @@ namespace FahrzeugVerwaltung
Farbe TEXT NOT NULL,
ErstelltAm DATETIME DEFAULT CURRENT_TIMESTAMP
)";
using (var command = new SQLiteCommand(createTableQuery, connection))
{
using var command = new SQLiteCommand(query, connection);
command.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Initialisieren der Datenbank: {ex.Message}");
}
}
/// <summary>
/// Speichert ein neues Fahrzeug in der Datenbank
/// </summary>
/// <param name="fahrzeug">Das zu speichernde Fahrzeug</param>
/// <returns>Die ID des gespeicherten Fahrzeugs</returns>
// speichert ein Fahrzeug und gibt die neue Id zurück
public int SpeichereFahrzeug(Fahrzeug fahrzeug)
{
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string insertQuery = @"
string query = @"
INSERT INTO Fahrzeuge (Marke, Modell, Baujahr, Leistung, Kilometerstand, Kaufpreis, Farbe)
VALUES (@Marke, @Modell, @Baujahr, @Leistung, @Kilometerstand, @Kaufpreis, @Farbe);
VALUES (@Marke, @Modell, @Baujahr, @Leistung, @Kilometer, @Kaufpreis, @Farbe);
SELECT last_insert_rowid();";
using (var command = new SQLiteCommand(insertQuery, connection))
{
using var command = new SQLiteCommand(query, connection);
command.Parameters.AddWithValue("@Marke", fahrzeug.Marke);
command.Parameters.AddWithValue("@Modell", fahrzeug.Modell);
command.Parameters.AddWithValue("@Baujahr", fahrzeug.Baujahr);
command.Parameters.AddWithValue("@Leistung", fahrzeug.Leistung);
command.Parameters.AddWithValue("@Kilometerstand", fahrzeug.Kilometerstand);
command.Parameters.AddWithValue("@Kilometer", fahrzeug.Kilometerstand);
command.Parameters.AddWithValue("@Kaufpreis", fahrzeug.Kaufpreis);
command.Parameters.AddWithValue("@Farbe", fahrzeug.Farbe);
var result = command.ExecuteScalar();
return Convert.ToInt32(result);
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Speichern des Fahrzeugs: {ex.Message}");
}
return Convert.ToInt32(command.ExecuteScalar());
}
/// <summary>
/// Lädt alle Fahrzeuge aus der Datenbank
/// </summary>
/// <returns>Liste aller Fahrzeuge</returns>
// lädt alle Fahrzeuge
public List<Fahrzeug> LadeAlleFahrzeuge()
{
var fahrzeuge = new List<Fahrzeug>();
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string selectQuery = "SELECT * FROM Fahrzeuge ORDER BY Marke, Modell";
using (var command = new SQLiteCommand(selectQuery, connection))
using (var reader = command.ExecuteReader())
{
string query = "SELECT * FROM Fahrzeuge ORDER BY Marke, Modell";
using var command = new SQLiteCommand(query, connection);
using var reader = command.ExecuteReader();
while (reader.Read())
{
var fahrzeug = new Fahrzeug
fahrzeuge.Add(new Fahrzeug
{
Id = Convert.ToInt32(reader["Id"]),
Marke = reader["Marke"].ToString(),
@ -124,56 +79,33 @@ namespace FahrzeugVerwaltung
Kilometerstand = Convert.ToInt32(reader["Kilometerstand"]),
Kaufpreis = Convert.ToDecimal(reader["Kaufpreis"]),
Farbe = reader["Farbe"].ToString()
};
fahrzeuge.Add(fahrzeug);
});
}
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Laden der Fahrzeuge: {ex.Message}");
}
return fahrzeuge;
}
/// <summary>
/// Sucht Fahrzeuge basierend auf einem Suchbegriff
/// </summary>
/// <param name="suchbegriff">Der Suchbegriff</param>
/// <returns>Liste der gefundenen Fahrzeuge</returns>
// sucht Fahrzeuge nach Begriff
public List<Fahrzeug> SucheFahrzeuge(string suchbegriff)
{
var fahrzeuge = new List<Fahrzeug>();
if (string.IsNullOrWhiteSpace(suchbegriff))
return LadeAlleFahrzeuge();
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
var fahrzeuge = new List<Fahrzeug>();
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string searchQuery = @"
string query = @"
SELECT * FROM Fahrzeuge
WHERE Marke LIKE @Suchbegriff
OR Modell LIKE @Suchbegriff
OR Farbe LIKE @Suchbegriff
OR CAST(Baujahr AS TEXT) LIKE @Suchbegriff
WHERE Marke LIKE @S
OR Modell LIKE @S
OR Farbe LIKE @S
OR CAST(Baujahr AS TEXT) LIKE @S
ORDER BY Marke, Modell";
using (var command = new SQLiteCommand(searchQuery, connection))
{
command.Parameters.AddWithValue("@Suchbegriff", $"%{suchbegriff}%");
using (var reader = command.ExecuteReader())
{
using var command = new SQLiteCommand(query, connection);
command.Parameters.AddWithValue("@S", $"%{suchbegriff}%");
using var reader = command.ExecuteReader();
while (reader.Read())
{
var fahrzeug = new Fahrzeug
fahrzeuge.Add(new Fahrzeug
{
Id = Convert.ToInt32(reader["Id"]),
Marke = reader["Marke"].ToString(),
@ -183,123 +115,57 @@ namespace FahrzeugVerwaltung
Kilometerstand = Convert.ToInt32(reader["Kilometerstand"]),
Kaufpreis = Convert.ToDecimal(reader["Kaufpreis"]),
Farbe = reader["Farbe"].ToString()
};
fahrzeuge.Add(fahrzeug);
});
}
}
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Suchen der Fahrzeuge: {ex.Message}");
}
return fahrzeuge;
}
/// <summary>
/// Aktualisiert ein bestehendes Fahrzeug in der Datenbank
/// </summary>
/// <param name="fahrzeug">Das zu aktualisierende Fahrzeug</param>
/// <returns>True wenn erfolgreich, sonst False</returns>
// aktualisiert ein Fahrzeug
public bool AktualisiereFahrzeug(Fahrzeug fahrzeug)
{
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string updateQuery = @"
string query = @"
UPDATE Fahrzeuge
SET Marke = @Marke,
Modell = @Modell,
Baujahr = @Baujahr,
Leistung = @Leistung,
Kilometerstand = @Kilometerstand,
Kaufpreis = @Kaufpreis,
Farbe = @Farbe
WHERE Id = @Id";
using (var command = new SQLiteCommand(updateQuery, connection))
{
SET Marke=@Marke,
Modell=@Modell,
Baujahr=@Baujahr,
Leistung=@Leistung,
Kilometerstand=@Kilometer,
Kaufpreis=@Kaufpreis,
Farbe=@Farbe
WHERE Id=@Id";
using var command = new SQLiteCommand(query, connection);
command.Parameters.AddWithValue("@Id", fahrzeug.Id);
command.Parameters.AddWithValue("@Marke", fahrzeug.Marke);
command.Parameters.AddWithValue("@Modell", fahrzeug.Modell);
command.Parameters.AddWithValue("@Baujahr", fahrzeug.Baujahr);
command.Parameters.AddWithValue("@Leistung", fahrzeug.Leistung);
command.Parameters.AddWithValue("@Kilometerstand", fahrzeug.Kilometerstand);
command.Parameters.AddWithValue("@Kilometer", fahrzeug.Kilometerstand);
command.Parameters.AddWithValue("@Kaufpreis", fahrzeug.Kaufpreis);
command.Parameters.AddWithValue("@Farbe", fahrzeug.Farbe);
int rowsAffected = command.ExecuteNonQuery();
return rowsAffected > 0;
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Aktualisieren des Fahrzeugs: {ex.Message}");
}
return command.ExecuteNonQuery() > 0;
}
/// <summary>
/// Löscht ein Fahrzeug aus der Datenbank
/// </summary>
/// <param name="id">Die ID des zu löschenden Fahrzeugs</param>
/// <returns>True wenn erfolgreich, sonst False</returns>
// löscht ein Fahrzeug
public bool LoescheFahrzeug(int id)
{
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string deleteQuery = "DELETE FROM Fahrzeuge WHERE Id = @Id";
using (var command = new SQLiteCommand(deleteQuery, connection))
{
string query = "DELETE FROM Fahrzeuge WHERE Id=@Id";
using var command = new SQLiteCommand(query, connection);
command.Parameters.AddWithValue("@Id", id);
int rowsAffected = command.ExecuteNonQuery();
return rowsAffected > 0;
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Löschen des Fahrzeugs: {ex.Message}");
}
return command.ExecuteNonQuery() > 0;
}
/// <summary>
/// Gibt die Anzahl der Fahrzeuge in der Datenbank zurück
/// </summary>
/// <returns>Anzahl der Fahrzeuge</returns>
// gibt die Anzahl der Fahrzeuge zurück
public int GetAnzahlFahrzeuge()
{
try
{
using (var connection = new SQLiteConnection(_connectionString))
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string countQuery = "SELECT COUNT(*) FROM Fahrzeuge";
using (var command = new SQLiteCommand(countQuery, connection))
{
var result = command.ExecuteScalar();
return Convert.ToInt32(result);
}
}
}
catch (Exception ex)
{
throw new Exception($"Fehler beim Ermitteln der Fahrzeuganzahl: {ex.Message}");
}
string query = "SELECT COUNT(*) FROM Fahrzeuge";
using var command = new SQLiteCommand(query, connection);
return Convert.ToInt32(command.ExecuteScalar());
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
namespace FahrzeugVerwaltung
@ -14,189 +14,57 @@ namespace FahrzeugVerwaltung
private decimal _kaufpreis;
private string _farbe;
public int Id
{
get => _id;
set
{
_id = value;
OnPropertyChanged(nameof(Id));
}
}
public int Id { get => _id; set { _id = value; OnPropertyChanged(nameof(Id)); } }
public string Marke { get => _marke; set { _marke = value; OnPropertyChanged(nameof(Marke)); } }
public string Modell { get => _modell; set { _modell = value; OnPropertyChanged(nameof(Modell)); } }
public int Baujahr { get => _baujahr; set { _baujahr = value; OnPropertyChanged(nameof(Baujahr)); OnPropertyChanged(nameof(AktuellerWert)); OnPropertyChanged(nameof(AktuellerWertFormatiert)); } }
public int Leistung { get => _leistung; set { _leistung = value; OnPropertyChanged(nameof(Leistung)); } }
public int Kilometerstand { get => _kilometerstand; set { _kilometerstand = value; OnPropertyChanged(nameof(Kilometerstand)); OnPropertyChanged(nameof(KilometerstandFormatiert)); OnPropertyChanged(nameof(AktuellerWert)); OnPropertyChanged(nameof(AktuellerWertFormatiert)); } }
public decimal Kaufpreis { get => _kaufpreis; set { _kaufpreis = value; OnPropertyChanged(nameof(Kaufpreis)); OnPropertyChanged(nameof(KaufpreisFormatiert)); OnPropertyChanged(nameof(AktuellerWert)); OnPropertyChanged(nameof(AktuellerWertFormatiert)); } }
public string Farbe { get => _farbe; set { _farbe = value; OnPropertyChanged(nameof(Farbe)); } }
public string Marke
{
get => _marke;
set
{
_marke = value;
OnPropertyChanged(nameof(Marke));
}
}
public string Modell
{
get => _modell;
set
{
_modell = value;
OnPropertyChanged(nameof(Modell));
}
}
public int Baujahr
{
get => _baujahr;
set
{
_baujahr = value;
OnPropertyChanged(nameof(Baujahr));
OnPropertyChanged(nameof(AktuellerWert));
OnPropertyChanged(nameof(AktuellerWertFormatiert));
}
}
public int Leistung
{
get => _leistung;
set
{
_leistung = value;
OnPropertyChanged(nameof(Leistung));
}
}
public int Kilometerstand
{
get => _kilometerstand;
set
{
_kilometerstand = value;
OnPropertyChanged(nameof(Kilometerstand));
OnPropertyChanged(nameof(KilometerstandFormatiert));
OnPropertyChanged(nameof(AktuellerWert));
OnPropertyChanged(nameof(AktuellerWertFormatiert));
}
}
public decimal Kaufpreis
{
get => _kaufpreis;
set
{
_kaufpreis = value;
OnPropertyChanged(nameof(Kaufpreis));
OnPropertyChanged(nameof(KaufpreisFormatiert));
OnPropertyChanged(nameof(AktuellerWert));
OnPropertyChanged(nameof(AktuellerWertFormatiert));
}
}
public string Farbe
{
get => _farbe;
set
{
_farbe = value;
OnPropertyChanged(nameof(Farbe));
}
}
// Formatierte Eigenschaften für die Anzeige
public string KilometerstandFormatiert => $"{Kilometerstand:N0} km";
public string KaufpreisFormatiert => $"{Kaufpreis:C}";
public string AktuellerWertFormatiert => $"{AktuellerWert:C}";
// Berechnet den aktuellen Wert basierend auf Alter und Kilometerstand
public decimal AktuellerWert
{
get
{
return BerechneAktuellenWert();
}
}
// berechnet den geschätzten aktuellen Wert
public decimal AktuellerWert => BerechneAktuellenWert();
/// <summary>
/// Berechnet den aktuellen Wert des Fahrzeugs
/// Formel: Wertverlust basierend auf Alter und Kilometerstand
/// </summary>
/// <returns>Aktueller Wert des Fahrzeugs</returns>
// ermittelt den aktuellen Wert basierend auf Alter und Laufleistung
public decimal BerechneAktuellenWert()
{
if (Kaufpreis <= 0 || Baujahr <= 0)
return 0;
if (Kaufpreis <= 0 || Baujahr <= 0) return 0;
int aktuellesJahr = DateTime.Now.Year;
int alter = aktuellesJahr - Baujahr;
// Wertverlust pro Jahr: 15% in den ersten 3 Jahren, dann 10% pro Jahr
decimal wertverlustProzent = 0;
int alter = DateTime.Now.Year - Baujahr;
decimal wertverlust = 0;
for (int i = 0; i < alter; i++)
{
if (i < 3)
wertverlustProzent += 0.15m; // 15% pro Jahr in den ersten 3 Jahren
else
wertverlustProzent += 0.10m; // 10% pro Jahr danach
}
wertverlust += (i < 3) ? 0.15m : 0.10m;
// Zusätzlicher Wertverlust basierend auf Kilometerstand
// Pro 10.000 km: 2% Wertverlust
decimal kmWertverlust = (Kilometerstand / 10000m) * 0.02m;
wertverlust += (Kilometerstand / 10000m) * 0.02m;
if (wertverlust > 0.90m) wertverlust = 0.90m;
wertverlustProzent += kmWertverlust;
// Maximal 90% Wertverlust
if (wertverlustProzent > 0.90m)
wertverlustProzent = 0.90m;
decimal aktuellerWert = Kaufpreis * (1 - wertverlustProzent);
// Mindestwert: 5% des Kaufpreises
decimal wert = Kaufpreis * (1 - wertverlust);
decimal mindestwert = Kaufpreis * 0.05m;
if (aktuellerWert < mindestwert)
aktuellerWert = mindestwert;
if (wert < mindestwert) wert = mindestwert;
return Math.Round(aktuellerWert, 2);
return Math.Round(wert, 2);
}
/// <summary>
/// Gibt eine detaillierte Beschreibung des Fahrzeugs zurück
/// </summary>
/// <returns>Formatierte Fahrzeugbeschreibung</returns>
public string GetDetailedDescription()
{
return $"{Marke} {Modell} ({Baujahr}) - {Leistung} PS - {KilometerstandFormatiert} - {Farbe}";
}
// liefert eine kurze Beschreibung
public string GetDetailedDescription() =>
$"{Marke} {Modell} ({Baujahr}) - {Leistung} PS - {KilometerstandFormatiert} - {Farbe}";
/// <summary>
/// Berechnet das Alter des Fahrzeugs in Jahren
/// </summary>
/// <returns>Alter in Jahren</returns>
public int GetAlter()
{
return DateTime.Now.Year - Baujahr;
}
// gibt das Alter des Fahrzeugs zurück
public int GetAlter() => DateTime.Now.Year - Baujahr;
/// <summary>
/// Prüft, ob das Fahrzeug als Oldtimer gilt (älter als 30 Jahre)
/// </summary>
/// <returns>True wenn Oldtimer, sonst False</returns>
public bool IsOldtimer()
{
return GetAlter() >= 30;
}
// prüft, ob das Fahrzeug ein Oldtimer ist
public bool IsOldtimer() => GetAlter() >= 30;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
private void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public override string ToString()
{
return $"{Marke} {Modell} ({Baujahr})";
}
public override string ToString() => $"{Marke} {Modell} ({Baujahr})";
}
}

View File

@ -1,193 +0,0 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
namespace FahrzeugVerwaltung
{
public static class Utilities
{
/// <summary>
/// Validiert eine Eingabe für numerische Werte
/// </summary>
public static bool IsValidNumber(string input, out double result, double min = double.MinValue, double max = double.MaxValue)
{
result = 0;
if (string.IsNullOrWhiteSpace(input))
return false;
if (double.TryParse(input, NumberStyles.Any, CultureInfo.CurrentCulture, out result))
{
return result >= min && result <= max;
}
return false;
}
/// <summary>
/// Validiert eine Eingabe für Ganzzahlen
/// </summary>
public static bool IsValidInteger(string input, out int result, int min = int.MinValue, int max = int.MaxValue)
{
result = 0;
if (string.IsNullOrWhiteSpace(input))
return false;
if (int.TryParse(input, out result))
{
return result >= min && result <= max;
}
return false;
}
/// <summary>
/// Validiert eine Eingabe für Dezimalzahlen
/// </summary>
public static bool IsValidDecimal(string input, out decimal result, decimal min = decimal.MinValue, decimal max = decimal.MaxValue)
{
result = 0;
if (string.IsNullOrWhiteSpace(input))
return false;
if (decimal.TryParse(input, NumberStyles.Any, CultureInfo.CurrentCulture, out result))
{
return result >= min && result <= max;
}
return false;
}
/// <summary>
/// Bereinigt Eingabetext von überflüssigen Zeichen
/// </summary>
public static string CleanInput(string input)
{
if (string.IsNullOrWhiteSpace(input))
return string.Empty;
// Mehrfache Leerzeichen durch einzelne ersetzen
return Regex.Replace(input.Trim(), @"\s+", " ");
}
/// <summary>
/// Zeigt eine Fehlermeldung an
/// </summary>
public static void ShowError(string message, string title = "Fehler")
{
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Error);
}
/// <summary>
/// Zeigt eine Warnmeldung an
/// </summary>
public static void ShowWarning(string message, string title = "Warnung")
{
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Warning);
}
/// <summary>
/// Zeigt eine Informationsmeldung an
/// </summary>
public static void ShowInfo(string message, string title = "Information")
{
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Information);
}
/// <summary>
/// Zeigt eine Bestätigungsdialog an
/// </summary>
public static bool ShowConfirmation(string message, string title = "Bestätigung")
{
return MessageBox.Show(message, title, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
}
/// <summary>
/// Formatiert einen Preis für die Anzeige
/// </summary>
public static string FormatPrice(decimal price)
{
return price.ToString("C", CultureInfo.CurrentCulture);
}
/// <summary>
/// Formatiert einen Kilometerstand für die Anzeige
/// </summary>
public static string FormatKilometers(int kilometers)
{
return $"{kilometers:N0} km";
}
/// <summary>
/// Berechnet die Differenz zwischen zwei Jahren
/// </summary>
public static int CalculateYearsDifference(int fromYear, int toYear)
{
return Math.Abs(toYear - fromYear);
}
/// <summary>
/// Prüft, ob ein Baujahr realistisch ist
/// </summary>
public static bool IsValidBuildYear(int year)
{
int currentYear = DateTime.Now.Year;
return year >= 1900 && year <= currentYear + 1;
}
/// <summary>
/// Generiert einen Dateinamen basierend auf Fahrzeugdaten
/// </summary>
public static string GenerateFileName(Fahrzeug fahrzeug, string extension = "html")
{
string marke = CleanInput(fahrzeug.Marke).Replace(" ", "_");
string modell = CleanInput(fahrzeug.Modell).Replace(" ", "_");
string datum = DateTime.Now.ToString("yyyyMMdd_HHmmss");
return $"Fahrzeug_{marke}_{modell}_{datum}.{extension}";
}
/// <summary>
/// Prüft, ob eine Textbox nur numerische Eingaben enthält
/// </summary>
public static void SetNumericOnly(TextBox textBox)
{
textBox.PreviewTextInput += (sender, e) =>
{
e.Handled = !IsNumeric(e.Text);
};
}
/// <summary>
/// Prüft, ob ein String nur Zahlen enthält
/// </summary>
private static bool IsNumeric(string text)
{
return Regex.IsMatch(text, @"^[0-9]+$");
}
/// <summary>
/// Konvertiert einen String sicher zu einem Integer
/// </summary>
public static int SafeParseInt(string value, int defaultValue = 0)
{
if (int.TryParse(value, out int result))
return result;
return defaultValue;
}
/// <summary>
/// Konvertiert einen String sicher zu einem Decimal
/// </summary>
public static decimal SafeParseDecimal(string value, decimal defaultValue = 0)
{
if (decimal.TryParse(value, out decimal result))
return result;
return defaultValue;
}
}
}

View File

@ -6,15 +6,16 @@
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="#FF007ACC"/>
<Setter Property="Background" Value="#FF2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="BorderBrush" Value="#FF005A9B"/>
<Setter Property="BorderBrush" Value="#FF1976D2"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF005A9B"/>
<Setter Property="Background" Value="#FF1976D2"/>
</Trigger>
</Style.Triggers>
</Style>
@ -32,7 +33,7 @@
</Style>
</Window.Resources>
<Grid Margin="10">
<Grid Margin="10" Background="#F5F5F5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="*"/>
@ -51,7 +52,6 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
@ -59,10 +59,10 @@
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Marke:" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Name="txtMarke" Margin="5"/>
<ComboBox Grid.Row="0" Grid.Column="1" Name="cmbMarke" Margin="5" SelectionChanged="CmbMarke_SelectionChanged"/>
<Label Grid.Row="1" Grid.Column="0" Content="Modell:" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Name="txtModell" Margin="5"/>
<ComboBox Grid.Row="1" Grid.Column="1" Name="cmbModell" Margin="5"/>
<Label Grid.Row="2" Grid.Column="0" Content="Baujahr:" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Name="txtBaujahr" Margin="5"/>
@ -78,10 +78,6 @@
<Label Grid.Row="6" Grid.Column="0" Content="Farbe:" VerticalAlignment="Center"/>
<TextBox Grid.Row="6" Grid.Column="1" Name="txtFarbe" Margin="5"/>
<Button Grid.Row="7" Grid.Column="1" Name="btnSpeichern" Content="Speichern"
Margin="5" HorizontalAlignment="Right" Width="120" Height="35"
Click="BtnSpeichern_Click"/>
</Grid>
</GroupBox>
<GroupBox Grid.Column="1" Grid.Row="0" Header="Fahrzeuge suchen" Margin="5">
@ -121,10 +117,11 @@
Text="Kein Fahrzeug ausgewählt" HorizontalAlignment="Center"/>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
<Button Name="btnPdf" Content="Als PDF speichern" Width="150" Height="35"
Margin="5" IsEnabled="False" Click="BtnPdf_Click"/>
<Button Name="btnAktualisieren" Content="Aktualisieren" Width="120" Height="35"
Margin="5" Click="BtnAktualisieren_Click"/>
<Button Name="btnHinzufuegen" Content="Hinzufügen" Width="120" Height="35" Click="BtnHinzufuegen_Click"/>
<Button Name="btnBearbeiten" Content="Bearbeiten" Width="120" Height="35" Click="BtnBearbeiten_Click"/>
<Button Name="btnLoeschen" Content="Löschen" Width="120" Height="35" Click="BtnLoeschen_Click"/>
<Button Name="btnPdf" Content="Als PDF speichern" Width="150" Height="35" IsEnabled="False" Click="BtnPdf_Click"/>
<Button Name="btnAktualisieren" Content="Aktualisieren" Width="120" Height="35" Click="BtnAktualisieren_Click"/>
</StackPanel>
</Grid>
</GroupBox>

View File

@ -1,11 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.IO;
using System.Text;
namespace FahrzeugVerwaltung
{
@ -15,79 +13,71 @@ namespace FahrzeugVerwaltung
private PdfService _pdfService;
private List<Fahrzeug> _aktuelleFahrzeuge;
private Fahrzeug _ausgewaehltesFahrzeug;
private Dictionary<string, List<string>> _markenModelle;
public MainWindow()
{
InitializeComponent();
InitializeServices();
InitializeMarken();
LadeFahrzeuge();
}
/// <summary>
/// Initialisiert die Services
/// </summary>
// legt die Dienste an
private void InitializeServices()
{
try
{
_databaseService = new DatabaseService();
_pdfService = new PdfService();
_aktuelleFahrzeuge = new List<Fahrzeug>();
}
catch (Exception ex)
// füllt die Listen für Marken und Modelle
private void InitializeMarken()
{
MessageBox.Show($"Fehler beim Initialisieren der Anwendung: {ex.Message}",
"Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
_markenModelle = new Dictionary<string, List<string>>
{
{"Audi", new List<string>{"A3","A4","A6","Q3","Q5","Q7"}},
{"BMW", new List<string>{"1er","3er","5er","X1","X3","X5"}},
{"Mercedes", new List<string>{"A-Klasse","C-Klasse","E-Klasse","GLA","GLC","GLE"}},
{"Volkswagen", new List<string>{"Golf","Passat","Polo","Tiguan","T-Roc","Arteon"}},
{"Opel", new List<string>{"Corsa","Astra","Insignia","Mokka","Crossland","Grandland"}},
{"Ford", new List<string>{"Fiesta","Focus","Mondeo","Kuga","Puma","S-MAX"}},
{"Toyota", new List<string>{"Yaris","Corolla","Camry","RAV4","C-HR","Auris"}},
{"Honda", new List<string>{"Civic","Accord","Jazz","CR-V","HR-V","NSX"}},
{"Hyundai", new List<string>{"i20","i30","i40","Tucson","Santa Fe","Kona"}},
{"Nissan", new List<string>{"Micra","Juke","Qashqai","X-Trail","Leaf","Navara"}}
};
cmbMarke.ItemsSource = _markenModelle.Keys;
}
/// <summary>
/// Lädt alle Fahrzeuge aus der Datenbank
/// </summary>
// lädt alle Fahrzeuge
private void LadeFahrzeuge()
{
try
{
_aktuelleFahrzeuge = _databaseService.LadeAlleFahrzeuge();
dgFahrzeuge.ItemsSource = _aktuelleFahrzeuge;
UpdateStatusText();
}
catch (Exception ex)
{
MessageBox.Show($"Fehler beim Laden der Fahrzeuge: {ex.Message}",
"Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// Aktualisiert den Status-Text
/// </summary>
// zeigt Anzahl der Fahrzeuge an
private void UpdateStatusText()
{
if (_aktuelleFahrzeuge.Count == 0)
{
txtDetails.Text = "Keine Fahrzeuge vorhanden";
}
else
{
txtDetails.Text = $"{_aktuelleFahrzeuge.Count} Fahrzeug(e) gefunden";
}
}
/// <summary>
/// Speichert ein neues Fahrzeug
/// </summary>
private void BtnSpeichern_Click(object sender, RoutedEventArgs e)
// fügt ein neues Fahrzeug hinzu
private void BtnHinzufuegen_Click(object sender, RoutedEventArgs e)
{
try
{
if (!ValidateInput())
if (!PruefeEingaben())
return;
var fahrzeug = new Fahrzeug
{
Marke = txtMarke.Text.Trim(),
Modell = txtModell.Text.Trim(),
Marke = cmbMarke.SelectedItem.ToString(),
Modell = cmbModell.SelectedItem.ToString(),
Baujahr = int.Parse(txtBaujahr.Text.Trim()),
Leistung = int.Parse(txtLeistung.Text.Trim()),
Kilometerstand = int.Parse(txtKilometer.Text.Trim()),
@ -95,131 +85,126 @@ namespace FahrzeugVerwaltung
Farbe = txtFarbe.Text.Trim()
};
int neueId = _databaseService.SpeichereFahrzeug(fahrzeug);
fahrzeug.Id = neueId;
MessageBox.Show($"Fahrzeug '{fahrzeug.Marke} {fahrzeug.Modell}' wurde erfolgreich gespeichert!",
"Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
// Eingabefelder leeren
fahrzeug.Id = _databaseService.SpeichereFahrzeug(fahrzeug);
MessageBox.Show("Fahrzeug gespeichert.", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
ClearInputFields();
// Fahrzeugliste aktualisieren
LadeFahrzeuge();
}
catch (Exception ex)
// bearbeitet das ausgewählte Fahrzeug
private void BtnBearbeiten_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show($"Fehler beim Speichern des Fahrzeugs: {ex.Message}",
"Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
if (_ausgewaehltesFahrzeug == null || !PruefeEingaben())
return;
_ausgewaehltesFahrzeug.Marke = cmbMarke.SelectedItem.ToString();
_ausgewaehltesFahrzeug.Modell = cmbModell.SelectedItem.ToString();
_ausgewaehltesFahrzeug.Baujahr = int.Parse(txtBaujahr.Text.Trim());
_ausgewaehltesFahrzeug.Leistung = int.Parse(txtLeistung.Text.Trim());
_ausgewaehltesFahrzeug.Kilometerstand = int.Parse(txtKilometer.Text.Trim());
_ausgewaehltesFahrzeug.Kaufpreis = decimal.Parse(txtKaufpreis.Text.Trim());
_ausgewaehltesFahrzeug.Farbe = txtFarbe.Text.Trim();
if (_databaseService.AktualisiereFahrzeug(_ausgewaehltesFahrzeug))
MessageBox.Show("Fahrzeug aktualisiert.", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
LadeFahrzeuge();
}
// löscht das ausgewählte Fahrzeug
private void BtnLoeschen_Click(object sender, RoutedEventArgs e)
{
if (_ausgewaehltesFahrzeug == null)
return;
if (MessageBox.Show("Fahrzeug wirklich löschen?", "Bestätigung", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
if (_databaseService.LoescheFahrzeug(_ausgewaehltesFahrzeug.Id))
{
MessageBox.Show("Fahrzeug gelöscht.", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
ClearInputFields();
LadeFahrzeuge();
}
}
}
/// <summary>
/// Validiert die Eingaben
/// </summary>
private bool ValidateInput()
// prüft die Eingaben
private bool PruefeEingaben()
{
var errors = new List<string>();
if (string.IsNullOrWhiteSpace(txtMarke.Text))
errors.Add("Marke ist erforderlich");
if (string.IsNullOrWhiteSpace(txtModell.Text))
errors.Add("Modell ist erforderlich");
var fehler = new List<string>();
if (cmbMarke.SelectedItem == null)
fehler.Add("Marke wählen");
if (cmbModell.SelectedItem == null)
fehler.Add("Modell wählen");
if (!int.TryParse(txtBaujahr.Text, out int baujahr) || baujahr < 1900 || baujahr > DateTime.Now.Year)
errors.Add($"Baujahr muss zwischen 1900 und {DateTime.Now.Year} liegen");
fehler.Add($"Baujahr zwischen 1900 und {DateTime.Now.Year}");
if (!int.TryParse(txtLeistung.Text, out int leistung) || leistung <= 0)
errors.Add("Leistung muss eine positive Zahl sein");
fehler.Add("Leistung muss positiv sein");
if (!int.TryParse(txtKilometer.Text, out int kilometer) || kilometer < 0)
errors.Add("Kilometerstand muss 0 oder größer sein");
fehler.Add("Kilometerstand muss 0 oder größer sein");
if (!decimal.TryParse(txtKaufpreis.Text, out decimal kaufpreis) || kaufpreis <= 0)
errors.Add("Kaufpreis muss eine positive Zahl sein");
fehler.Add("Kaufpreis muss positiv sein");
if (string.IsNullOrWhiteSpace(txtFarbe.Text))
errors.Add("Farbe ist erforderlich");
fehler.Add("Farbe angeben");
if (errors.Any())
if (fehler.Any())
{
MessageBox.Show($"Bitte korrigieren Sie folgende Fehler:\n\n{string.Join("\n", errors)}",
"Eingabefehler", MessageBoxButton.OK, MessageBoxImage.Warning);
MessageBox.Show(string.Join("\n", fehler), "Eingabefehler", MessageBoxButton.OK, MessageBoxImage.Warning);
return false;
}
return true;
}
/// <summary>
/// Leert alle Eingabefelder
/// </summary>
// leert alle Eingabefelder
private void ClearInputFields()
{
txtMarke.Clear();
txtModell.Clear();
cmbMarke.SelectedIndex = -1;
cmbModell.ItemsSource = null;
txtBaujahr.Clear();
txtLeistung.Clear();
txtKilometer.Clear();
txtKaufpreis.Clear();
txtFarbe.Clear();
txtMarke.Focus();
cmbMarke.Focus();
}
/// <summary>
/// Sucht nach Fahrzeugen
/// </summary>
// sucht Fahrzeuge nach Begriff
private void BtnSuchen_Click(object sender, RoutedEventArgs e)
{
try
{
string suchbegriff = txtSuche.Text.Trim();
var suchbegriff = txtSuche.Text.Trim();
if (string.IsNullOrWhiteSpace(suchbegriff))
{
MessageBox.Show("Bitte geben Sie einen Suchbegriff ein.",
"Hinweis", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
_aktuelleFahrzeuge = _databaseService.SucheFahrzeuge(suchbegriff);
dgFahrzeuge.ItemsSource = _aktuelleFahrzeuge;
UpdateStatusText();
if (_aktuelleFahrzeuge.Count == 0)
{
MessageBox.Show($"Keine Fahrzeuge mit dem Suchbegriff '{suchbegriff}' gefunden.",
"Suchergebnis", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"Fehler bei der Suche: {ex.Message}",
"Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// Zeigt alle Fahrzeuge an
/// </summary>
// zeigt alle Fahrzeuge
private void BtnAlle_Click(object sender, RoutedEventArgs e)
{
txtSuche.Clear();
LadeFahrzeuge();
}
/// <summary>
/// Wird aufgerufen wenn ein Fahrzeug ausgewählt wird
/// </summary>
// reagiert auf Auswahl in der Liste
private void DgFahrzeuge_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_ausgewaehltesFahrzeug = dgFahrzeuge.SelectedItem as Fahrzeug;
if (_ausgewaehltesFahrzeug != null)
{
txtDetails.Text = $"Ausgewählt: {_ausgewaehltesFahrzeug.GetDetailedDescription()} | " +
$"Aktueller Wert: {_ausgewaehltesFahrzeug.AktuellerWertFormatiert} | " +
$"Wertverlust: {((_ausgewaehltesFahrzeug.Kaufpreis - _ausgewaehltesFahrzeug.AktuellerWert) / _ausgewaehltesFahrzeug.Kaufpreis * 100):F1}%";
txtDetails.Text = $"Ausgewählt: {_ausgewaehltesFahrzeug.GetDetailedDescription()}";
cmbMarke.SelectedItem = _ausgewaehltesFahrzeug.Marke;
CmbMarke_SelectionChanged(null, null);
cmbModell.SelectedItem = _ausgewaehltesFahrzeug.Modell;
txtBaujahr.Text = _ausgewaehltesFahrzeug.Baujahr.ToString();
txtLeistung.Text = _ausgewaehltesFahrzeug.Leistung.ToString();
txtKilometer.Text = _ausgewaehltesFahrzeug.Kilometerstand.ToString();
txtKaufpreis.Text = _ausgewaehltesFahrzeug.Kaufpreis.ToString();
txtFarbe.Text = _ausgewaehltesFahrzeug.Farbe;
btnPdf.IsEnabled = true;
}
else
@ -229,98 +214,49 @@ namespace FahrzeugVerwaltung
}
}
/// <summary>
/// Erstellt ein PDF für das ausgewählte Fahrzeug
/// </summary>
// erstellt ein PDF zum Fahrzeug
private void BtnPdf_Click(object sender, RoutedEventArgs e)
{
if (_ausgewaehltesFahrzeug == null)
{
MessageBox.Show("Bitte wählen Sie erst ein Fahrzeug aus.",
"Hinweis", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
try
{
var saveFileDialog = new SaveFileDialog
var dialog = new SaveFileDialog
{
Filter = "PDF-Dateien (*.pdf)|*.pdf",
FileName = $"Fahrzeug_{_ausgewaehltesFahrzeug.Marke}_{_ausgewaehltesFahrzeug.Modell}_{DateTime.Now:yyyyMMdd_HHmmss}.pdf",
Title = "PDF speichern"
FileName = $"Fahrzeug_{_ausgewaehltesFahrzeug.Marke}_{_ausgewaehltesFahrzeug.Modell}_{DateTime.Now:yyyyMMdd_HHmmss}.pdf"
};
if (saveFileDialog.ShowDialog() == true)
if (dialog.ShowDialog() == true)
{
bool erfolg = _pdfService.ErstelleFahrzeugPdf(_ausgewaehltesFahrzeug, saveFileDialog.FileName);
if (erfolg)
{
MessageBox.Show($"PDF wurde erfolgreich erstellt:\n{saveFileDialog.FileName}",
"Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
// Fragen ob PDF geöffnet werden soll
var result = MessageBox.Show("Möchten Sie das PDF jetzt öffnen?",
"PDF öffnen", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
System.Diagnostics.Process.Start(saveFileDialog.FileName);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Fehler beim Erstellen des PDFs: {ex.Message}",
"Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
if (_pdfService.ErstelleFahrzeugPdf(_ausgewaehltesFahrzeug, dialog.FileName))
MessageBox.Show("PDF erstellt.", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
/// <summary>
/// Aktualisiert die Fahrzeugliste
/// </summary>
// lädt die Liste neu
private void BtnAktualisieren_Click(object sender, RoutedEventArgs e)
{
LadeFahrzeuge();
MessageBox.Show("Fahrzeugliste wurde aktualisiert.",
"Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
/// <summary>
/// Behandelt das Schließen des Fensters
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
}
/// <summary>
/// Behandelt Tastatureingaben für Shortcuts
/// </summary>
// Tastenkürzel
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
// Strg+S zum Speichern
if (e.Key == System.Windows.Input.Key.S &&
System.Windows.Input.Keyboard.Modifiers == System.Windows.Input.ModifierKeys.Control)
if (e.Key == System.Windows.Input.Key.S && System.Windows.Input.Keyboard.Modifiers == System.Windows.Input.ModifierKeys.Control)
{
BtnSpeichern_Click(null, null);
BtnHinzufuegen_Click(null, null);
e.Handled = true;
}
// Strg+F zum Suchen
else if (e.Key == System.Windows.Input.Key.F &&
System.Windows.Input.Keyboard.Modifiers == System.Windows.Input.ModifierKeys.Control)
else if (e.Key == System.Windows.Input.Key.F && System.Windows.Input.Keyboard.Modifiers == System.Windows.Input.ModifierKeys.Control)
{
txtSuche.Focus();
e.Handled = true;
}
// F5 zum Aktualisieren
else if (e.Key == System.Windows.Input.Key.F5)
{
BtnAktualisieren_Click(null, null);
e.Handled = true;
}
// Escape zum Leeren der Eingabefelder
else if (e.Key == System.Windows.Input.Key.Escape)
{
ClearInputFields();
@ -329,5 +265,18 @@ namespace FahrzeugVerwaltung
base.OnKeyDown(e);
}
// passt Modellliste an
private void CmbMarke_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cmbMarke.SelectedItem == null)
{
cmbModell.ItemsSource = null;
return;
}
var marke = cmbMarke.SelectedItem.ToString();
cmbModell.ItemsSource = _markenModelle[marke];
}
}
}

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.pdf;
@ -9,272 +7,60 @@ namespace FahrzeugVerwaltung
{
public class PdfService
{
/// <summary>
/// Erstellt ein PDF-Infoblatt für ein Fahrzeug
/// </summary>
/// <param name="fahrzeug">Das Fahrzeug für das PDF</param>
/// <param name="dateiPfad">Der Pfad wo das PDF gespeichert werden soll</param>
/// <returns>True wenn erfolgreich, sonst False</returns>
// erstellt ein einfaches PDF für ein Fahrzeug
public bool ErstelleFahrzeugPdf(Fahrzeug fahrzeug, string dateiPfad)
{
try
{
// PDF-Dokument erstellen
using (var document = new iTextSharp.text.Document(PageSize.A4, 50, 50, 50, 50))
{
using (var writer = PdfWriter.GetInstance(document, new FileStream(dateiPfad, FileMode.Create)))
{
using var document = new Document(PageSize.A4);
PdfWriter.GetInstance(document, new FileStream(dateiPfad, FileMode.Create));
document.Open();
// Titel
var titelFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 18, BaseColor.DARK_GRAY);
var titel = new iTextSharp.text.Paragraph("FAHRZEUG-INFOBLATT", titelFont)
{
Alignment = Element.ALIGN_CENTER,
SpacingAfter = 20
};
document.Add(titel);
// Datum
var datumFont = FontFactory.GetFont(FontFactory.HELVETICA, 10, BaseColor.GRAY);
var datum = new iTextSharp.text.Paragraph($"Erstellt am: {DateTime.Now:dd.MM.yyyy HH:mm}", datumFont)
{
Alignment = Element.ALIGN_RIGHT,
SpacingAfter = 30
};
document.Add(datum);
// Fahrzeugdaten als Tabelle
var table = new PdfPTable(2)
{
WidthPercentage = 100,
SpacingAfter = 20
};
table.SetWidths(new float[] { 1f, 2f });
// Tabellen-Style
var headerFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 12, BaseColor.WHITE);
var contentFont = FontFactory.GetFont(FontFactory.HELVETICA, 11, BaseColor.BLACK);
var labelFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 11, BaseColor.DARK_GRAY);
// Header
var headerCell1 = new PdfPCell(new Phrase("Eigenschaft", headerFont))
{
BackgroundColor = BaseColor.DARK_GRAY,
Padding = 10,
HorizontalAlignment = Element.ALIGN_CENTER
};
var headerCell2 = new PdfPCell(new Phrase("Wert", headerFont))
{
BackgroundColor = BaseColor.DARK_GRAY,
Padding = 10,
HorizontalAlignment = Element.ALIGN_CENTER
};
table.AddCell(headerCell1);
table.AddCell(headerCell2);
// Fahrzeugdaten hinzufügen
AddTableRow(table, "Marke", fahrzeug.Marke, labelFont, contentFont);
AddTableRow(table, "Modell", fahrzeug.Modell, labelFont, contentFont);
AddTableRow(table, "Baujahr", fahrzeug.Baujahr.ToString(), labelFont, contentFont);
AddTableRow(table, "Leistung", $"{fahrzeug.Leistung} PS", labelFont, contentFont);
AddTableRow(table, "Kilometerstand", fahrzeug.KilometerstandFormatiert, labelFont, contentFont);
AddTableRow(table, "Farbe", fahrzeug.Farbe, labelFont, contentFont);
AddTableRow(table, "Kaufpreis", fahrzeug.KaufpreisFormatiert, labelFont, contentFont);
// Berechnete Werte
var calculatedFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 11, BaseColor.BLUE);
AddTableRow(table, "Aktueller Wert", fahrzeug.AktuellerWertFormatiert, labelFont, calculatedFont);
AddTableRow(table, "Alter", $"{fahrzeug.GetAlter()} Jahre", labelFont, calculatedFont);
AddTableRow(table, "Wertverlust", $"{((fahrzeug.Kaufpreis - fahrzeug.AktuellerWert) / fahrzeug.Kaufpreis * 100):F1}%", labelFont, calculatedFont);
document.Add(table);
// Zusätzliche Informationen
var infoTitel = new iTextSharp.text.Paragraph("ZUSÄTZLICHE INFORMATIONEN",
FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 14, BaseColor.DARK_GRAY))
{
SpacingBefore = 20,
SpacingAfter = 10
};
document.Add(infoTitel);
var infoText = new StringBuilder();
infoText.AppendLine($"• Fahrzeugtyp: {(fahrzeug.IsOldtimer() ? "Oldtimer" : "Gebrauchtwagen")}");
infoText.AppendLine($"• Vollständige Bezeichnung: {fahrzeug.GetDetailedDescription()}");
infoText.AppendLine($"• Durchschnittliche Laufleistung pro Jahr: {(fahrzeug.Kilometerstand / Math.Max(fahrzeug.GetAlter(), 1)):N0} km");
if (fahrzeug.IsOldtimer())
{
infoText.AppendLine("• Oldtimer-Status: Dieses Fahrzeug ist über 30 Jahre alt und gilt als Oldtimer.");
}
var info = new iTextSharp.text.Paragraph(infoText.ToString(),
FontFactory.GetFont(FontFactory.HELVETICA, 10, BaseColor.BLACK))
{
SpacingAfter = 20
};
document.Add(info);
// Bewertungshinweise
var bewertungTitel = new iTextSharp.text.Paragraph("BEWERTUNGSHINWEISE",
FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 14, BaseColor.DARK_GRAY))
{
SpacingBefore = 20,
SpacingAfter = 10
};
document.Add(bewertungTitel);
var bewertungText = @"Der angegebene aktuelle Wert ist eine automatische Schätzung basierend auf:
Alter des Fahrzeugs (15% Wertverlust pro Jahr in den ersten 3 Jahren, dann 10% pro Jahr)
Kilometerstand (2% Wertverlust pro 10.000 km)
Mindestwert von 5% des ursprünglichen Kaufpreises
Diese Bewertung ersetzt keine professionelle Fahrzeugbewertung und dient nur als Richtwert.
Faktoren wie Zustand, Wartungshistorie, Unfallschäden und Marktlage werden nicht berücksichtigt.";
var bewertung = new iTextSharp.text.Paragraph(bewertungText,
FontFactory.GetFont(FontFactory.HELVETICA, 9, BaseColor.GRAY))
{
SpacingAfter = 30
};
document.Add(bewertung);
// Fußzeile
var fusszeile = new iTextSharp.text.Paragraph("Erstellt mit Fahrzeugverwaltung - Alle Angaben ohne Gewähr",
FontFactory.GetFont(FontFactory.HELVETICA, 8, BaseColor.LIGHT_GRAY))
{
Alignment = Element.ALIGN_CENTER
};
document.Add(fusszeile);
document.Add(new Paragraph($"Fahrzeug: {fahrzeug.Marke} {fahrzeug.Modell}"));
document.Add(new Paragraph($"Baujahr: {fahrzeug.Baujahr}"));
document.Add(new Paragraph($"Leistung: {fahrzeug.Leistung} PS"));
document.Add(new Paragraph($"Kilometerstand: {fahrzeug.KilometerstandFormatiert}"));
document.Add(new Paragraph($"Farbe: {fahrzeug.Farbe}"));
document.Add(new Paragraph($"Kaufpreis: {fahrzeug.KaufpreisFormatiert}"));
document.Add(new Paragraph($"Aktueller Wert: {fahrzeug.AktuellerWertFormatiert}"));
document.Close();
}
}
return true;
}
catch (Exception ex)
catch
{
throw new Exception($"Fehler beim Erstellen des PDFs: {ex.Message}");
return false;
}
}
/// <summary>
/// Hilfsmethode zum Hinzufügen einer Tabellenzeile
/// </summary>
private void AddTableRow(PdfPTable table, string label, string value, Font labelFont, Font valueFont)
{
var labelCell = new PdfPCell(new Phrase(label, labelFont))
{
Padding = 8,
BackgroundColor = BaseColor.LIGHT_GRAY,
HorizontalAlignment = Element.ALIGN_LEFT
};
var valueCell = new PdfPCell(new Phrase(value, valueFont))
{
Padding = 8,
HorizontalAlignment = Element.ALIGN_LEFT
};
table.AddCell(labelCell);
table.AddCell(valueCell);
}
/// <summary>
/// Erstellt ein PDF mit einer Fahrzeugliste
/// </summary>
/// <param name="fahrzeuge">Liste der Fahrzeuge</param>
/// <param name="dateiPfad">Pfad für das PDF</param>
/// <returns>True wenn erfolgreich</returns>
public bool ErstelleFahrzeuglistePdf(System.Collections.Generic.List<Fahrzeug> fahrzeuge, string dateiPfad)
// erstellt ein einfaches PDF mit einer Fahrzeugliste
public bool ErstelleFahrzeuglistePdf(List<Fahrzeug> fahrzeuge, string dateiPfad)
{
try
{
using (var document = new iTextSharp.text.Document(PageSize.A4.Rotate(), 30, 30, 30, 30))
{
using (var writer = PdfWriter.GetInstance(document, new FileStream(dateiPfad, FileMode.Create)))
{
using var document = new Document(PageSize.A4.Rotate());
PdfWriter.GetInstance(document, new FileStream(dateiPfad, FileMode.Create));
document.Open();
// Titel
var titel = new iTextSharp.text.Paragraph("FAHRZEUGLISTE",
FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 16, BaseColor.DARK_GRAY))
{
Alignment = Element.ALIGN_CENTER,
SpacingAfter = 20
};
document.Add(titel);
// Datum und Anzahl
var info = new iTextSharp.text.Paragraph($"Erstellt am: {DateTime.Now:dd.MM.yyyy HH:mm} | Anzahl Fahrzeuge: {fahrzeuge.Count}",
FontFactory.GetFont(FontFactory.HELVETICA, 10, BaseColor.GRAY))
{
Alignment = Element.ALIGN_CENTER,
SpacingAfter = 20
};
document.Add(info);
// Tabelle mit allen Fahrzeugen
var table = new PdfPTable(8) { WidthPercentage = 100 };
table.SetWidths(new float[] { 1f, 1.2f, 0.8f, 0.8f, 1f, 1f, 0.8f, 1.2f });
// Header
var headerFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 10, BaseColor.WHITE);
var headers = new[] { "Marke", "Modell", "Baujahr", "Leistung", "Kilometer", "Kaufpreis", "Farbe", "Akt. Wert" };
foreach (var header in headers)
string[] headers = { "Marke", "Modell", "Baujahr", "Leistung", "Kilometer", "Kaufpreis", "Farbe", "Aktueller Wert" };
foreach (var h in headers)
table.AddCell(new PdfPCell(new Phrase(h)) { BackgroundColor = BaseColor.LIGHT_GRAY });
foreach (var f in fahrzeuge)
{
var headerCell = new PdfPCell(new Phrase(header, headerFont))
{
BackgroundColor = BaseColor.DARK_GRAY,
Padding = 5,
HorizontalAlignment = Element.ALIGN_CENTER
};
table.AddCell(headerCell);
table.AddCell(f.Marke);
table.AddCell(f.Modell);
table.AddCell(f.Baujahr.ToString());
table.AddCell($"{f.Leistung} PS");
table.AddCell(f.KilometerstandFormatiert);
table.AddCell(f.KaufpreisFormatiert);
table.AddCell(f.Farbe);
table.AddCell(f.AktuellerWertFormatiert);
}
// Datenzeilen
var contentFont = FontFactory.GetFont(FontFactory.HELVETICA, 9, BaseColor.BLACK);
foreach (var fahrzeug in fahrzeuge)
{
table.AddCell(new PdfPCell(new Phrase(fahrzeug.Marke, contentFont)) { Padding = 5 });
table.AddCell(new PdfPCell(new Phrase(fahrzeug.Modell, contentFont)) { Padding = 5 });
table.AddCell(new PdfPCell(new Phrase(fahrzeug.Baujahr.ToString(), contentFont)) { Padding = 5, HorizontalAlignment = Element.ALIGN_CENTER });
table.AddCell(new PdfPCell(new Phrase($"{fahrzeug.Leistung} PS", contentFont)) { Padding = 5, HorizontalAlignment = Element.ALIGN_CENTER });
table.AddCell(new PdfPCell(new Phrase(fahrzeug.KilometerstandFormatiert, contentFont)) { Padding = 5, HorizontalAlignment = Element.ALIGN_RIGHT });
table.AddCell(new PdfPCell(new Phrase(fahrzeug.KaufpreisFormatiert, contentFont)) { Padding = 5, HorizontalAlignment = Element.ALIGN_RIGHT });
table.AddCell(new PdfPCell(new Phrase(fahrzeug.Farbe, contentFont)) { Padding = 5 });
table.AddCell(new PdfPCell(new Phrase(fahrzeug.AktuellerWertFormatiert, contentFont)) { Padding = 5, HorizontalAlignment = Element.ALIGN_RIGHT });
}
document.Add(table);
// Zusammenfassung
var gesamtKaufpreis = fahrzeuge.Sum(f => f.Kaufpreis);
var gesamtAktuellerWert = fahrzeuge.Sum(f => f.AktuellerWert);
var gesamtWertverlust = gesamtKaufpreis - gesamtAktuellerWert;
var zusammenfassung = new iTextSharp.text.Paragraph($"\nZUSAMMENFASSUNG:\n" +
$"Gesamter Kaufpreis: {gesamtKaufpreis:C}\n" +
$"Gesamter aktueller Wert: {gesamtAktuellerWert:C}\n" +
$"Gesamter Wertverlust: {gesamtWertverlust:C} ({(gesamtWertverlust / gesamtKaufpreis * 100):F1}%)",
FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 11, BaseColor.DARK_GRAY))
{
SpacingBefore = 20
};
document.Add(zusammenfassung);
document.Close();
}
}
return true;
}
catch (Exception ex)
catch
{
throw new Exception($"Fehler beim Erstellen der Fahrzeugliste: {ex.Message}");
return false;
}
}
}