Fortschritt, .NET-Prüfung, Updates, Fenster merken, Box-Funktionen

- Große Kopien laufen im Hintergrund mit Fortschritt und Abbrechen;
  abgebrochene Kopien werden aufgeräumt, das Original bleibt unverändert
- Sichern/Wiederherstellen mit Fortschritt, keine halben ZIP-Dateien
- Installer auf .NET Framework 4.8: erkennt fehlende .NET 9 Desktop Runtime
  und installiert sie auf Wunsch (Microsoft-Signatur wird geprüft)
- Update-Hinweis über Gitea-Releases, Update mit automatischem Neustart
- Fenstergröße und -position werden gemerkt
- Desktop-Boxen: umbenennen, löschen, verschieben, herausziehen
- Fenster und Boxen kehren auf sichtbare Bildschirme zurück
- README ergänzt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 20:46:48 +02:00
co-authored by Claude Opus 5.5
parent 77bb3e6b9d
commit 1ae6e1abc2
19 changed files with 1098 additions and 95 deletions
+35 -5
View File
@@ -26,6 +26,16 @@ public partial class MainWindow : Window
DesktopLinkBox.IsChecked = Installer.InstalledDir is null || Installer.DesktopLinkExists;
AutostartBox.IsChecked = Installer.AutostartEnabled;
if (!Runtime.IsInstalled)
{
RuntimeHint.Visibility = Visibility.Visible;
RuntimeBox.Visibility = Visibility.Visible;
RuntimeHintText.Text = Runtime.Is64BitWindows
? "DeskCore benötigt die kostenlose „Microsoft .NET 9 Desktop Runtime“. Sie fehlt auf diesem PC und wird bei der " +
"Installation automatisch von Microsoft geladen (ca. 55 MB). Windows fragt dabei einmal nach Administratorrechten."
: "DeskCore benötigt ein 64-Bit-Windows (Windows 10 oder 11) und kann auf diesem PC leider nicht installiert werden.";
}
if (Installer.InstalledDir is null && Installer.LegacyInstallDir is not null)
{
UpdateHint.Visibility = Visibility.Visible;
@@ -102,7 +112,7 @@ public partial class MainWindow : Window
case 1 when AcceptLicense.IsChecked == true: ShowPage(2); break;
case 2:
var dir = DirBox.Text.Trim();
if (!Path.IsPathFullyQualified(dir) || dir.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
if (!IsFullPath(dir) || dir.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
MessageBox.Show(this, "Bitte einen gültigen Ordner angeben, z. B. C:\\Programme\\DeskCore.", "DeskCore Setup");
return;
@@ -119,9 +129,14 @@ public partial class MainWindow : Window
private void ChangeDir_Click(object sender, RoutedEventArgs e)
{
var dlg = new Microsoft.Win32.OpenFolderDialog { Title = "Installationsordner wählen" };
if (dlg.ShowDialog(this) != true) return;
var chosen = dlg.FolderName;
using var dlg = new System.Windows.Forms.FolderBrowserDialog
{
Description = "Installationsordner wählen",
SelectedPath = Directory.Exists(DirBox.Text) ? DirBox.Text : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
ShowNewFolderButton = true,
};
if (dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
var chosen = dlg.SelectedPath;
// wie bei üblichen Installern: Unterordner "DeskCore" anhängen
DirBox.Text = Path.GetFileName(chosen.TrimEnd('\\')).Equals("DeskCore", StringComparison.OrdinalIgnoreCase)
? chosen : Path.Combine(chosen, "DeskCore");
@@ -136,15 +151,24 @@ public partial class MainWindow : Window
SetButtons(false);
var progress = new Progress<(double Value, string Text)>(p => SetProgress(p.Value, p.Text));
bool desktop = DesktopLinkBox.IsChecked == true, autostart = AutostartBox.IsChecked == true;
bool runtime = RuntimeBox.Visibility == Visibility.Visible && RuntimeBox.IsChecked == true;
try
{
await Task.Run(() => Installer.Install(dir, desktop, autostart, progress));
await Task.Run(() => Installer.Install(dir, desktop, autostart, progress, runtime));
_installedExe = Path.Combine(dir, Installer.ExeName);
_done = true;
ProgressTitle.Text = "Installation abgeschlossen";
ProgressText.Text = $"DeskCore wurde in {dir} installiert. Du findest es im Startmenü" + (desktop ? " und auf dem Desktop." : ".");
DoneText.Text = "Alles bereit!";
DonePanel.Visibility = Visibility.Visible;
if (!Runtime.IsInstalled)
{
// Laufzeit fehlt weiterhin (abgewählt) → Hinweis statt Startversuch
DoneText.Text = "DeskCore ist installiert";
RuntimeMissing.Visibility = Visibility.Visible;
LaunchBox.IsChecked = false;
LaunchBox.IsEnabled = false;
}
ShowPage(3);
}
catch (Exception ex)
@@ -230,6 +254,12 @@ public partial class MainWindow : Window
new DoubleAnimation(target, TimeSpan.FromMilliseconds(250)) { EasingFunction = new CubicEase() });
}
private static bool IsFullPath(string p) =>
p.Length >= 3 && char.IsLetter(p[0]) && p[1] == ':' && (p[2] == '\\' || p[2] == '/');
private void OpenRuntimePage_Click(object sender, RoutedEventArgs e) =>
Process.Start(new ProcessStartInfo(Runtime.DownloadPage) { UseShellExecute = true });
private void Minimize_Click(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
private void Cancel_Click(object sender, RoutedEventArgs e)