- 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>
280 lines
11 KiB
C#
280 lines
11 KiB
C#
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Animation;
|
|
|
|
namespace DeskCoreSetup;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
private readonly bool _uninstall;
|
|
private int _page;
|
|
private bool _busy;
|
|
private bool _done;
|
|
private string? _installedExe;
|
|
|
|
public MainWindow(bool uninstall)
|
|
{
|
|
InitializeComponent();
|
|
_uninstall = uninstall;
|
|
VersionText.Text = $"Version {Installer.Version}";
|
|
LicenseText.Text = Installer.LicenseText;
|
|
|
|
DirBox.Text = Installer.InstalledDir ?? Installer.DefaultDir;
|
|
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;
|
|
UpdateHintText.Text = "Die bisherige Version „DeskHub“ wird durch DeskCore ersetzt. Deine Kategorien und Einstellungen werden übernommen.";
|
|
}
|
|
else if (Installer.InstalledDir is not null)
|
|
{
|
|
UpdateHint.Visibility = Visibility.Visible;
|
|
UpdateHintText.Text = Installer.InstalledVersion == Installer.Version
|
|
? $"DeskCore {Installer.Version} ist bereits installiert und wird neu installiert. Deine Kategorien und Einstellungen bleiben erhalten."
|
|
: $"Die vorhandene Installation (Version {Installer.InstalledVersion}) wird durch Version {Installer.Version} ersetzt. Deine Kategorien und Einstellungen bleiben erhalten.";
|
|
}
|
|
|
|
if (_uninstall)
|
|
{
|
|
Title = WindowTitle.Text = "DeskCore deinstallieren";
|
|
Steps.Visibility = Visibility.Collapsed;
|
|
ShowOnly(PageUninstall);
|
|
NextButton.Content = "Deinstallieren";
|
|
BackButton.Visibility = Visibility.Collapsed;
|
|
LoadDataInfo();
|
|
}
|
|
else ShowPage(0);
|
|
}
|
|
|
|
// ---------- Seiten ----------
|
|
|
|
private void ShowOnly(UIElement page)
|
|
{
|
|
foreach (var p in new UIElement[] { PageWelcome, PageLicense, PageOptions, PageProgress, PageUninstall })
|
|
p.Visibility = p == page ? Visibility.Visible : Visibility.Collapsed;
|
|
}
|
|
|
|
private void ShowPage(int page)
|
|
{
|
|
_page = page;
|
|
ShowOnly(page switch { 0 => PageWelcome, 1 => PageLicense, 2 => PageOptions, _ => PageProgress });
|
|
|
|
var steps = new[] { Step0, Step1, Step2, Step3 };
|
|
for (int i = 0; i < steps.Length; i++)
|
|
{
|
|
var label = ((string)steps[i].Tag ?? (string)(steps[i].Tag = steps[i].Text));
|
|
steps[i].Text = i < page || (_done && i == page) ? $"✓ {label}" : label;
|
|
steps[i].FontWeight = i == page ? FontWeights.SemiBold : FontWeights.Normal;
|
|
steps[i].Foreground = (Brush)FindResource(i == page ? "TextBrush" : i < page ? "TextSecondaryBrush" : "TextTertiaryBrush");
|
|
}
|
|
|
|
BackButton.Visibility = page is 1 or 2 ? Visibility.Visible : Visibility.Hidden;
|
|
NextButton.Content = page switch { 2 => "Installieren", 3 => "Fertigstellen", _ => "Weiter" };
|
|
NextButton.IsEnabled = page != 1 || AcceptLicense.IsChecked == true;
|
|
}
|
|
|
|
private void AcceptLicense_Click(object sender, RoutedEventArgs e) => NextButton.IsEnabled = AcceptLicense.IsChecked == true;
|
|
|
|
private void Back_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (!_busy && _page > 0) ShowPage(_page - 1);
|
|
}
|
|
|
|
private async void Next_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_busy) return;
|
|
|
|
if (_uninstall)
|
|
{
|
|
if (_done) { Close(); return; }
|
|
await RunUninstall();
|
|
return;
|
|
}
|
|
|
|
switch (_page)
|
|
{
|
|
case 0: ShowPage(1); break;
|
|
case 1 when AcceptLicense.IsChecked == true: ShowPage(2); break;
|
|
case 2:
|
|
var dir = DirBox.Text.Trim();
|
|
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;
|
|
}
|
|
await RunInstall(dir);
|
|
break;
|
|
case 3:
|
|
if (LaunchBox.IsChecked == true && _installedExe is not null)
|
|
Process.Start(new ProcessStartInfo(_installedExe) { UseShellExecute = true });
|
|
Close();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ChangeDir_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
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");
|
|
}
|
|
|
|
// ---------- Installation ----------
|
|
|
|
private async Task RunInstall(string dir)
|
|
{
|
|
_busy = true;
|
|
ShowPage(3);
|
|
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, 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)
|
|
{
|
|
ProgressTitle.Text = "Installation fehlgeschlagen";
|
|
ProgressText.Text = ex.Message;
|
|
ProgressText.Foreground = (Brush)FindResource("DangerBrush");
|
|
NextButton.Content = "Schließen";
|
|
_installedExe = null;
|
|
}
|
|
_busy = false;
|
|
NextButton.IsEnabled = true;
|
|
CloseButton.IsEnabled = true;
|
|
BackButton.Visibility = Visibility.Hidden;
|
|
CancelButton.Visibility = Visibility.Collapsed;
|
|
}
|
|
|
|
// ---------- Deinstallation ----------
|
|
|
|
private async void LoadDataInfo()
|
|
{
|
|
var root = Installer.DataDirs[0];
|
|
if (!Directory.Exists(root))
|
|
{
|
|
DataInfo.Text = "Es wurden keine Kategorien gefunden.";
|
|
return;
|
|
}
|
|
DataInfo.Text = $"Sie liegen in {root}. Größe wird berechnet …";
|
|
var size = await Task.Run(Installer.DataSize);
|
|
DataInfo.Text = $"Sie liegen in {root} ({Installer.FormatSize(size)}) und bleiben dort, falls du DeskCore später wieder installierst.";
|
|
}
|
|
|
|
private void RemoveDataBox_Click(object sender, RoutedEventArgs e) =>
|
|
RemoveDataWarning.Visibility = RemoveDataBox.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
private async Task RunUninstall()
|
|
{
|
|
_busy = true;
|
|
bool removeData = RemoveDataBox.IsChecked == true;
|
|
ShowOnly(PageProgress);
|
|
ProgressTitle.Text = "DeskCore wird entfernt …";
|
|
LaunchBox.Visibility = Visibility.Collapsed;
|
|
SetButtons(false);
|
|
var progress = new Progress<(double Value, string Text)>(p => SetProgress(p.Value, p.Text));
|
|
try
|
|
{
|
|
await Task.Run(() => Installer.Uninstall(removeData, progress));
|
|
ProgressTitle.Text = "Deinstallation abgeschlossen";
|
|
ProgressText.Text = removeData
|
|
? "DeskCore, deine Kategorien und Einstellungen wurden entfernt (die Daten liegen im Papierkorb)."
|
|
: "DeskCore wurde entfernt. Deine Kategorien liegen weiterhin in " + Installer.DataDirs[0] + ".";
|
|
DoneText.Text = "Fertig";
|
|
DonePanel.Visibility = Visibility.Visible;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ProgressTitle.Text = "Deinstallation fehlgeschlagen";
|
|
ProgressText.Text = ex.Message;
|
|
}
|
|
_done = true;
|
|
_busy = false;
|
|
CloseButton.IsEnabled = true;
|
|
NextButton.Content = "Schließen";
|
|
NextButton.IsEnabled = true;
|
|
CancelButton.Visibility = Visibility.Collapsed;
|
|
}
|
|
|
|
// ---------- Helfer ----------
|
|
|
|
private void SetButtons(bool enabled)
|
|
{
|
|
NextButton.IsEnabled = enabled;
|
|
BackButton.IsEnabled = enabled;
|
|
CancelButton.IsEnabled = enabled;
|
|
CloseButton.IsEnabled = enabled;
|
|
}
|
|
|
|
private void SetProgress(double value, string text)
|
|
{
|
|
if (!_done) ProgressText.Text = text;
|
|
var target = Math.Max(0, ProgressTrack.ActualWidth * value);
|
|
ProgressFill.BeginAnimation(WidthProperty,
|
|
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)
|
|
{
|
|
if (_busy) return;
|
|
if (!_done && !_uninstall && _page > 0 &&
|
|
MessageBox.Show(this, "Installation wirklich abbrechen?", "DeskCore Setup", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
|
return;
|
|
Close();
|
|
}
|
|
|
|
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
|
|
{
|
|
if (_busy) e.Cancel = true; // während der Installation nicht schließen
|
|
base.OnClosing(e);
|
|
}
|
|
}
|