[feat:] first project version
This commit is contained in:
@@ -7,4 +7,8 @@
|
|||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
<FluentTheme />
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
<Application.Resources>
|
||||||
|
<x:Double x:Key="DatePickerThemeMinWidth">10</x:Double>
|
||||||
|
<x:Double x:Key="DatePickerThemeMaxWidth">1000</x:Double>
|
||||||
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
7
Backup.cs
Normal file
7
Backup.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
20
Exam.cs
Normal file
20
Exam.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace PLG_Exam
|
||||||
|
{
|
||||||
|
public class Exam
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Vorname { get; set; } = string.Empty;
|
||||||
|
public DateTime? Datum { get; set; }
|
||||||
|
public List<ExamTab> Tabs { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExamTab
|
||||||
|
{
|
||||||
|
public string Aufgabennummer { get; set; } = string.Empty;
|
||||||
|
public string Überschrift { get; set; } = string.Empty;
|
||||||
|
public string Inhalt { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,49 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="PLG_Exam.MainWindow"
|
x:Class="PLG_Exam.MainWindow"
|
||||||
Title="PLG_Exam">
|
Title="PLG Exam">
|
||||||
Welcome to Avalonia!
|
<DockPanel Margin="10">
|
||||||
|
<!-- Unterer Teil: Dateiverwaltung -->
|
||||||
|
<Border DockPanel.Dock="Bottom" Background="#232327" Margin="0,10,0,0">
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="10" VerticalAlignment="Center" Spacing="10">
|
||||||
|
<Button Content="Speichern" Click="OnSaveClick" />
|
||||||
|
<Button Content="Speichern unter..." Click="OnSaveAsClick" />
|
||||||
|
<Button Content="Öffnen" Click="OnOpenClick" />
|
||||||
|
<Button Content="Neu" Click="OnNewClick" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Oberer Teil: Name, Vorname, Datum -->
|
||||||
|
<Border DockPanel.Dock="Top" Background="#232327" Height="100" Margin="0,0,0,10">
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="350" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0" Margin="5" Spacing="5">
|
||||||
|
<TextBlock Text="Name" FontWeight="Bold" Foreground="White" />
|
||||||
|
<TextBox Name="NameField" Height="33"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="1" Margin="5" Spacing="5">
|
||||||
|
<TextBlock Text="Vorname" FontWeight="Bold" Foreground="White" />
|
||||||
|
<TextBox Name="VornameField" Height="33"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2" Margin="5" Spacing="5">
|
||||||
|
<TextBlock Text="Datum" HorizontalAlignment="Stretch" FontWeight="Bold" Foreground="White" />
|
||||||
|
<DatePicker Name="DatumField" Width="340" Height="33" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Mittlerer Teil: Tabansicht -->
|
||||||
|
<Border Background="#232327" Margin="0,0,0,0">
|
||||||
|
<DockPanel>
|
||||||
|
<Button Content="Weitere Aufgabenlösung hinzufügen" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,0" Click="OnAddTabClick" />
|
||||||
|
<TabControl Name="TabView" Margin="10">
|
||||||
|
|
||||||
|
</TabControl>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</DockPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -1,11 +1,515 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
namespace PLG_Exam;
|
namespace PLG_Exam
|
||||||
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
private int _tabCounter = 0;
|
||||||
|
private bool _isSaved = true;
|
||||||
|
private string? _currentFilePath;
|
||||||
|
public static Exam _currentExam = new();
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
InitializeBackupTimer();
|
||||||
|
AddNewTab();
|
||||||
|
|
||||||
|
this.KeyDown += OnKeyDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event für "Neuen Tab hinzufügen"
|
||||||
|
private void OnAddTabClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
AddNewTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnKeyDown(object? sender, Avalonia.Input.KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.KeyModifiers.HasFlag(Avalonia.Input.KeyModifiers.Control))
|
||||||
|
{
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Avalonia.Input.Key.S:
|
||||||
|
if (e.KeyModifiers.HasFlag(Avalonia.Input.KeyModifiers.Shift))
|
||||||
|
{
|
||||||
|
// Strg + Shift + S: Speichern Unter
|
||||||
|
OnSaveAsClick(sender, e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Strg + S: Speichern
|
||||||
|
OnSaveClick(sender, e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Avalonia.Input.Key.O:
|
||||||
|
// Strg + O: Öffnen
|
||||||
|
OnOpenClick(sender, e);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Avalonia.Input.Key.T:
|
||||||
|
// Strg + T: Neuer Tab
|
||||||
|
OnAddTabClick(sender, e);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Avalonia.Input.Key.N:
|
||||||
|
// Strg + N: Neu
|
||||||
|
OnNewClick(sender, e);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Avalonia.Input.Key.R:
|
||||||
|
// Strg + R: Abgeben (coming soon)
|
||||||
|
await OnSubmitClick(sender, e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSubmitClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await MessageBox.Show(this, "Abgeben-Funktion wird bald verfügbar sein!", "Abgeben", MessageBoxButton.Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void AddNewTab()
|
||||||
|
{
|
||||||
|
_tabCounter++;
|
||||||
|
|
||||||
|
var closeButton = new Button
|
||||||
|
{
|
||||||
|
Content = "×",
|
||||||
|
FontSize = 14,
|
||||||
|
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center,
|
||||||
|
VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center,
|
||||||
|
Width = 20,
|
||||||
|
Height = 20,
|
||||||
|
Background = new SolidColorBrush(Color.FromRgb(123,35,39)),
|
||||||
|
Foreground = new SolidColorBrush(Color.FromRgb(0,0,0)),
|
||||||
|
Padding = new Thickness(0),
|
||||||
|
Margin = new Thickness(5, 0, 0, 0),
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
ToolTip.SetTip(closeButton, "Tab schließen");
|
||||||
|
|
||||||
|
var headerStackPanel = new StackPanel
|
||||||
|
{
|
||||||
|
Orientation = Avalonia.Layout.Orientation.Horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
var tabHeader = new TextBlock
|
||||||
|
{
|
||||||
|
Text = $"{_tabCounter} - Neu",
|
||||||
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
headerStackPanel.Children.Add(tabHeader);
|
||||||
|
headerStackPanel.Children.Add(closeButton);
|
||||||
|
|
||||||
|
var tabItem = new TabItem
|
||||||
|
{
|
||||||
|
Header = headerStackPanel,
|
||||||
|
Content = CreateTabContent()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Schließen-Event hinzufügen
|
||||||
|
closeButton.Click += async (sender, e) =>
|
||||||
|
{
|
||||||
|
var result = await MessageBox.Show(this, "Diese Aktion schließt die Aufgabe unwiderruflich. Möchten Sie fortfahren?",
|
||||||
|
"Tab schließen", MessageBoxButton.YesNo);
|
||||||
|
if (result == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
TabView.Items.Remove(tabItem);
|
||||||
|
_isSaved = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TabView.Items.Add(tabItem);
|
||||||
|
TabView.SelectedItem = tabItem;
|
||||||
|
_isSaved = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Tab entfernen mit Sicherheitsabfrage
|
||||||
|
private async void OnRemoveTabClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (TabView.SelectedItem is TabItem selectedTab)
|
||||||
|
{
|
||||||
|
var result = await MessageBox.Show(this, "Dieser Tab wird unwiderruflich gelöscht. Möchten Sie fortfahren?",
|
||||||
|
"Tab löschen", MessageBoxButton.YesNo);
|
||||||
|
if (result == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
TabView.Items.Remove(selectedTab);
|
||||||
|
_isSaved = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichern
|
||||||
|
private async void OnSaveClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_currentFilePath))
|
||||||
|
{
|
||||||
|
await SaveAs();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SaveToFile(_currentFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnSaveAsClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await SaveAs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> SaveAs()
|
||||||
|
{
|
||||||
|
var saveDialog = new SaveFileDialog
|
||||||
|
{
|
||||||
|
DefaultExtension = "exam",
|
||||||
|
Filters = { new FileDialogFilter { Name = "Exam files", Extensions = { "exam" } } }
|
||||||
|
};
|
||||||
|
var filePath = await saveDialog.ShowAsync(this);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filePath))
|
||||||
|
{
|
||||||
|
SaveToFile(filePath);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveToFile(string filePath)
|
||||||
|
{
|
||||||
|
// Update Exam-Daten
|
||||||
|
_currentExam.Name = NameField.Text;
|
||||||
|
_currentExam.Vorname = VornameField.Text;
|
||||||
|
_currentExam.Datum = DatumField.SelectedDate?.UtcDateTime;
|
||||||
|
_currentExam.Tabs = TabView.Items.OfType<TabItem>()
|
||||||
|
.Select(tab =>
|
||||||
|
{
|
||||||
|
var grid = tab.Content as Grid;
|
||||||
|
if (grid == null) return null;
|
||||||
|
|
||||||
|
var aufgabennummer = (grid.Children[0] as Grid)?.Children[0] as TextBox;
|
||||||
|
var ueberschrift = (grid.Children[0] as Grid)?.Children[1] as TextBox;
|
||||||
|
var beschreibung = grid.Children[1] as TextBox;
|
||||||
|
|
||||||
|
return new ExamTab
|
||||||
|
{
|
||||||
|
Aufgabennummer = aufgabennummer?.Text ?? "",
|
||||||
|
Überschrift = ueberschrift?.Text ?? "",
|
||||||
|
Inhalt = beschreibung?.Text ?? ""
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.Where(tab => tab != null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(_currentExam, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
File.WriteAllText(filePath, json);
|
||||||
|
|
||||||
|
_currentFilePath = filePath;
|
||||||
|
_isSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Öffnen
|
||||||
|
private async void OnOpenClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var openDialog = new OpenFileDialog
|
||||||
|
{
|
||||||
|
AllowMultiple = false,
|
||||||
|
Filters = { new FileDialogFilter { Name = "Exam files", Extensions = { "exam" } } }
|
||||||
|
};
|
||||||
|
var result = await openDialog.ShowAsync(this);
|
||||||
|
|
||||||
|
if (result != null && result.Length > 0)
|
||||||
|
{
|
||||||
|
LoadFromFile(result[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFromFile(string filePath)
|
||||||
|
{
|
||||||
|
var fileContent = File.ReadAllText(filePath);
|
||||||
|
var exam = JsonSerializer.Deserialize<Exam>(fileContent);
|
||||||
|
|
||||||
|
if (exam == null) return;
|
||||||
|
|
||||||
|
// Daten wiederherstellen
|
||||||
|
_currentExam = exam;
|
||||||
|
NameField.Text = exam.Name;
|
||||||
|
VornameField.Text = exam.Vorname;
|
||||||
|
DatumField.SelectedDate = exam.Datum;
|
||||||
|
|
||||||
|
TabView.Items.Clear();
|
||||||
|
_tabCounter = 0;
|
||||||
|
|
||||||
|
foreach (var tab in exam.Tabs)
|
||||||
|
{
|
||||||
|
_tabCounter++;
|
||||||
|
var tabItem = new TabItem
|
||||||
|
{
|
||||||
|
Header = $"{tab.Aufgabennummer} - {tab.Überschrift}",
|
||||||
|
Content = CreateTabContent(tab.Aufgabennummer, tab.Überschrift, tab.Inhalt)
|
||||||
|
};
|
||||||
|
TabView.Items.Add(tabItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentFilePath = filePath;
|
||||||
|
_isSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neu
|
||||||
|
private async void OnNewClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!_isSaved)
|
||||||
|
{
|
||||||
|
var result = await MessageBox.Show(this, "Möchten Sie die aktuellen Änderungen speichern?",
|
||||||
|
"Nicht gespeicherte Änderungen", MessageBoxButton.YesNoCancel);
|
||||||
|
if (result == MessageBoxResult.Cancel) return;
|
||||||
|
if (result == MessageBoxResult.Yes) {
|
||||||
|
if((await SaveAs()) != true){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentExam = new Exam();
|
||||||
|
NameField.Text = "";
|
||||||
|
VornameField.Text = "";
|
||||||
|
DatumField.SelectedDate = null;
|
||||||
|
TabView.Items.Clear();
|
||||||
|
_tabCounter = 0;
|
||||||
|
_currentFilePath = null;
|
||||||
|
_isSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Grid CreateTabContent(string? aufgabennummer = null, string? ueberschrift = null, string? beschreibung = null)
|
||||||
|
{
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(33) });
|
||||||
|
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
|
||||||
|
|
||||||
|
var headerGrid = new Grid { Margin = new Thickness(0, 0, 0, 10) };
|
||||||
|
headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(250) });
|
||||||
|
headerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||||
|
|
||||||
|
var aufgabenNummerTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Name = "Aufgabennummer",
|
||||||
|
Watermark = "Aufgabennummer",
|
||||||
|
Margin = new Thickness(0,10,0,0),
|
||||||
|
Text = aufgabennummer ?? ""
|
||||||
|
};
|
||||||
|
|
||||||
|
var ueberschriftTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Name = "Überschrift",
|
||||||
|
Watermark = "Überschrift (optional)",
|
||||||
|
Margin = new Thickness(10,10,0,0),
|
||||||
|
Text = ueberschrift ?? ""
|
||||||
|
};
|
||||||
|
|
||||||
|
var beschreibungTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Name = "Beschreibung",
|
||||||
|
AcceptsReturn = true,
|
||||||
|
VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
|
||||||
|
Watermark = "Beschreibung",
|
||||||
|
Text = beschreibung ?? "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Event für dynamische Tab-Umbenennung
|
||||||
|
aufgabenNummerTextBox.KeyUp += (s, e) => UpdateTabHeader(TabView.SelectedItem as TabItem, aufgabenNummerTextBox, ueberschriftTextBox);
|
||||||
|
ueberschriftTextBox.KeyUp += (s, e) => UpdateTabHeader(TabView.SelectedItem as TabItem, aufgabenNummerTextBox, ueberschriftTextBox);
|
||||||
|
|
||||||
|
headerGrid.Children.Add(aufgabenNummerTextBox);
|
||||||
|
headerGrid.Children.Add(ueberschriftTextBox);
|
||||||
|
Grid.SetColumn(aufgabenNummerTextBox, 0);
|
||||||
|
Grid.SetColumn(ueberschriftTextBox, 1);
|
||||||
|
|
||||||
|
grid.Children.Add(headerGrid);
|
||||||
|
grid.Children.Add(beschreibungTextBox);
|
||||||
|
Grid.SetRow(headerGrid, 0);
|
||||||
|
Grid.SetRow(beschreibungTextBox, 1);
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTabHeader(TabItem? tab, TextBox aufgabennummer, TextBox ueberschrift)
|
||||||
|
{
|
||||||
|
if (tab == null) return;
|
||||||
|
|
||||||
|
var aufgabeText = string.IsNullOrWhiteSpace(aufgabennummer.Text) ? "Neu" : aufgabennummer.Text;
|
||||||
|
var ueberschriftText = string.IsNullOrWhiteSpace(ueberschrift.Text) ? "" : ueberschrift.Text;
|
||||||
|
|
||||||
|
tab.Header = string.IsNullOrWhiteSpace(ueberschriftText)
|
||||||
|
? aufgabeText
|
||||||
|
: $"{aufgabeText} - {ueberschriftText}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Backups
|
||||||
|
//
|
||||||
|
|
||||||
|
private const int BackupInterval = 20000; // 20 Sekunden in Millisekunden
|
||||||
|
|
||||||
|
|
||||||
|
private DispatcherTimer _backupTimer;
|
||||||
|
|
||||||
|
private void InitializeBackupTimer()
|
||||||
|
{
|
||||||
|
_backupTimer = new DispatcherTimer
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromSeconds(20)
|
||||||
|
};
|
||||||
|
_backupTimer.Tick += (sender, e) => CreateBackup();
|
||||||
|
_backupTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int MaxBackupFiles = 5; // Maximal 5 Backups
|
||||||
|
private readonly string BackupFolderPath = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
|
"PLG_Exam_Backup");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Backup erstellen
|
||||||
|
private void CreateBackup()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Ordner erstellen, falls nicht vorhanden
|
||||||
|
if (!Directory.Exists(BackupFolderPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(BackupFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstelle Dateinamen mit Zeitstempel
|
||||||
|
var timestamp = DateTime.Now.ToString("yyMMdd_HHmmss");
|
||||||
|
var backupFileName = Path.Combine(BackupFolderPath, $"{timestamp}_backup.exam");
|
||||||
|
|
||||||
|
// Aktuelle Daten in JSON-Format speichern
|
||||||
|
var jsonData = GetCurrentExamDataAsJson();
|
||||||
|
File.WriteAllText(backupFileName, jsonData);
|
||||||
|
|
||||||
|
// Alte Backups löschen, wenn mehr als MaxBackupFiles vorhanden sind
|
||||||
|
CleanupOldBackups();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Backup konnte nicht erstellt werden: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Löscht alte Backups, wenn die maximale Anzahl überschritten wird
|
||||||
|
private void CleanupOldBackups()
|
||||||
|
{
|
||||||
|
var backupFiles = Directory.GetFiles(BackupFolderPath, "*_backup.exam")
|
||||||
|
.OrderBy(File.GetCreationTime)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
while (backupFiles.Count > MaxBackupFiles)
|
||||||
|
{
|
||||||
|
var oldestFile = backupFiles.First();
|
||||||
|
File.Delete(oldestFile);
|
||||||
|
backupFiles.RemoveAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Löscht alle Backups unwiderruflich (mit Überschreiben)
|
||||||
|
private void DeleteAllBackups()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Directory.Exists(BackupFolderPath))
|
||||||
|
{
|
||||||
|
foreach (var file in Directory.GetFiles(BackupFolderPath, "*_backup.exam"))
|
||||||
|
{
|
||||||
|
OverwriteAndDeleteFile(file);
|
||||||
|
}
|
||||||
|
Directory.Delete(BackupFolderPath, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Backups konnten nicht gelöscht werden: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Überschreibt eine Datei mit Zufallsdaten und löscht sie anschließend
|
||||||
|
private void OverwriteAndDeleteFile(string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath)) return;
|
||||||
|
|
||||||
|
var fileLength = new FileInfo(filePath).Length;
|
||||||
|
|
||||||
|
// Datei mit Zufallsdaten überschreiben
|
||||||
|
using (var stream = new FileStream(filePath, FileMode.Open))
|
||||||
|
{
|
||||||
|
var randomData = new byte[fileLength];
|
||||||
|
new Random().NextBytes(randomData);
|
||||||
|
stream.Write(randomData, 0, randomData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datei löschen
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Datei konnte nicht überschrieben und gelöscht werden: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holt die aktuelle Exam-Daten als JSON
|
||||||
|
private string GetCurrentExamDataAsJson()
|
||||||
|
{
|
||||||
|
_currentExam.Name = NameField.Text;
|
||||||
|
_currentExam.Vorname = VornameField.Text;
|
||||||
|
_currentExam.Datum = DatumField.SelectedDate?.UtcDateTime;
|
||||||
|
|
||||||
|
_currentExam.Tabs = TabView.Items.OfType<TabItem>()
|
||||||
|
.Select(tab =>
|
||||||
|
{
|
||||||
|
var grid = tab.Content as Grid;
|
||||||
|
if (grid == null) return null;
|
||||||
|
|
||||||
|
var aufgabennummer = (grid.Children[0] as Grid)?.Children[0] as TextBox;
|
||||||
|
var ueberschrift = (grid.Children[0] as Grid)?.Children[1] as TextBox;
|
||||||
|
var beschreibung = grid.Children[1] as TextBox;
|
||||||
|
|
||||||
|
return new ExamTab
|
||||||
|
{
|
||||||
|
Aufgabennummer = aufgabennummer?.Text ?? "",
|
||||||
|
Überschrift = ueberschrift?.Text ?? "",
|
||||||
|
Inhalt = beschreibung?.Text ?? ""
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.Where(tab => tab != null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return System.Text.Json.JsonSerializer.Serialize(_currentExam, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
MessageBox.axaml
Normal file
18
MessageBox.axaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" SizeToContent="WidthAndHeight"
|
||||||
|
x:Class="PLG_Exam.MessageBox"
|
||||||
|
Title="MessageBox">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Name="Text" Margin="10" TextWrapping="Wrap"/>
|
||||||
|
<StackPanel HorizontalAlignment="Right" Margin="5" Orientation="Horizontal" Name="Buttons">
|
||||||
|
<StackPanel.Styles>
|
||||||
|
<Style Selector="Button">
|
||||||
|
<Setter Property="Margin" Value="5"/>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Styles>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Window>
|
||||||
76
MessageBox.axaml.cs
Normal file
76
MessageBox.axaml.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PLG_Exam;
|
||||||
|
|
||||||
|
public partial class MessageBox : Window
|
||||||
|
{
|
||||||
|
public MessageBox()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<MessageBoxResult> Show(Window? parent, string text, string title, MessageBoxButton buttons = MessageBoxButton.Ok)
|
||||||
|
{
|
||||||
|
var msgbox = new MessageBox()
|
||||||
|
{
|
||||||
|
Title = title
|
||||||
|
};
|
||||||
|
var tb = msgbox.FindControl<TextBlock>("Text");
|
||||||
|
if(tb != null) tb.Text = text;
|
||||||
|
var buttonPanel = msgbox.FindControl<StackPanel>("Buttons");
|
||||||
|
|
||||||
|
var res = MessageBoxResult.Ok;
|
||||||
|
|
||||||
|
void AddButton(string caption, MessageBoxResult r, bool def = false)
|
||||||
|
{
|
||||||
|
var btn = new Button { Content = caption };
|
||||||
|
btn.Click += (_, __) =>
|
||||||
|
{
|
||||||
|
res = r;
|
||||||
|
msgbox.Close();
|
||||||
|
};
|
||||||
|
if(buttonPanel == null) return;
|
||||||
|
buttonPanel.Children.Add(btn);
|
||||||
|
if (def)
|
||||||
|
res = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttons == MessageBoxButton.Ok || buttons == MessageBoxButton.OkCancel)
|
||||||
|
AddButton("Ok", MessageBoxResult.Ok, true);
|
||||||
|
if (buttons == MessageBoxButton.YesNo || buttons == MessageBoxButton.YesNoCancel)
|
||||||
|
{
|
||||||
|
AddButton("Yes", MessageBoxResult.Yes);
|
||||||
|
AddButton("No", MessageBoxResult.No, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttons == MessageBoxButton.OkCancel || buttons == MessageBoxButton.YesNoCancel)
|
||||||
|
AddButton("Cancel", MessageBoxResult.Cancel, true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<MessageBoxResult>();
|
||||||
|
msgbox.Closed += delegate { tcs.TrySetResult(res); };
|
||||||
|
if (parent != null)
|
||||||
|
msgbox.ShowDialog(parent);
|
||||||
|
else msgbox.Show();
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public enum MessageBoxButton
|
||||||
|
{
|
||||||
|
Ok,
|
||||||
|
OkCancel,
|
||||||
|
YesNo,
|
||||||
|
YesNoCancel
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MessageBoxResult
|
||||||
|
{
|
||||||
|
Ok,
|
||||||
|
Cancel,
|
||||||
|
Yes,
|
||||||
|
No
|
||||||
|
}
|
||||||
25
PLG-Exam.sln
Normal file
25
PLG-Exam.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.002.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PLG-Exam", "PLG-Exam.csproj", "{F120D7C1-1272-40DA-9CBB-0EE2DC779632}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{F120D7C1-1272-40DA-9CBB-0EE2DC779632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F120D7C1-1272-40DA-9CBB-0EE2DC779632}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F120D7C1-1272-40DA-9CBB-0EE2DC779632}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F120D7C1-1272-40DA-9CBB-0EE2DC779632}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {357C5A9A-CDAF-4061-ABC2-8F768DA586D2}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
Reference in New Issue
Block a user