DeskCore 1.0.0
Desktop-Icons in eigene Kategorien sortieren: Windows-11-Design (hell/dunkel), Desktop-Boxen, automatisches Einsortieren nach Regeln, Rückgängig, Suche, Infobereich mit Tastenkürzel, Sicherung als ZIP und Installer ohne Adminrechte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<Application x:Class="DeskCoreSetup.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- [0] wird je nach Hell/Dunkel ersetzt -->
|
||||
<ResourceDictionary Source="Themes/Colors.Light.xaml"/>
|
||||
<ResourceDictionary Source="Themes/Controls.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<SolidColorBrush x:Key="AccentBrush" Color="#0067C0"/>
|
||||
<SolidColorBrush x:Key="AccentHoverBrush" Color="#1975C5"/>
|
||||
<SolidColorBrush x:Key="OnAccentBrush" Color="White"/>
|
||||
<SolidColorBrush x:Key="AccentSubtleBrush" Color="#220067C0"/>
|
||||
<SolidColorBrush x:Key="AccentSubtleStrokeBrush" Color="#550067C0"/>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace DeskCoreSetup;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
bool uninstall = e.Args.Any(a => a.Equals("--uninstall", StringComparison.OrdinalIgnoreCase));
|
||||
bool silent = e.Args.Any(a => a.Equals("/S", StringComparison.OrdinalIgnoreCase));
|
||||
var dirArg = e.Args.FirstOrDefault(a => a.StartsWith("/D=", StringComparison.OrdinalIgnoreCase))?[3..];
|
||||
|
||||
// Stille Installation/Deinstallation für Skripte: DeskCore-Setup.exe /S [/D=Ordner] bzw. --uninstall /S
|
||||
if (silent)
|
||||
{
|
||||
var progress = new Progress<(double, string)>(_ => { });
|
||||
try
|
||||
{
|
||||
if (uninstall) Installer.Uninstall(removeData: false, progress);
|
||||
else Installer.Install(dirArg ?? Installer.InstalledDir ?? Installer.DefaultDir,
|
||||
desktopLink: true, autostart: Installer.AutostartEnabled, progress);
|
||||
Shutdown(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ohne Oberfläche: Fehler für Skripte nachvollziehbar ablegen
|
||||
try { System.IO.File.WriteAllText(System.IO.Path.Combine(System.IO.Path.GetTempPath(), "DeskCore-Setup.log"), $"[{DateTime.Now}] {ex}"); } catch { }
|
||||
Shutdown(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyTheme();
|
||||
new MainWindow(uninstall).Show();
|
||||
}
|
||||
|
||||
/// <summary>Hell/Dunkel und Akzentfarbe wie in Windows eingestellt.</summary>
|
||||
private void ApplyTheme()
|
||||
{
|
||||
bool light = ReadDword(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme") != 0;
|
||||
Resources.MergedDictionaries[0] = new ResourceDictionary
|
||||
{
|
||||
Source = new Uri($"pack://application:,,,/Themes/Colors.{(light ? "Light" : "Dark")}.xaml"),
|
||||
};
|
||||
int abgr = ReadDword(@"Software\Microsoft\Windows\DWM", "AccentColor") is var v and not -1 ? v : unchecked((int)0xFFD77800);
|
||||
var accent = Color.FromRgb((byte)abgr, (byte)(abgr >> 8), (byte)(abgr >> 16));
|
||||
var a = light ? Mix(accent, Colors.Black, 0.12) : Mix(accent, Colors.White, 0.45);
|
||||
Resources["AccentBrush"] = new SolidColorBrush(a);
|
||||
Resources["AccentHoverBrush"] = new SolidColorBrush(light ? Mix(a, Colors.White, 0.1) : Mix(a, Colors.Black, 0.1));
|
||||
Resources["OnAccentBrush"] = new SolidColorBrush(light ? Colors.White : Colors.Black);
|
||||
Resources["AccentSubtleBrush"] = new SolidColorBrush(Color.FromArgb(light ? (byte)0x22 : (byte)0x30, a.R, a.G, a.B));
|
||||
Resources["AccentSubtleStrokeBrush"] = new SolidColorBrush(Color.FromArgb(light ? (byte)0x55 : (byte)0x60, a.R, a.G, a.B));
|
||||
}
|
||||
|
||||
private static int ReadDword(string key, string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var k = Registry.CurrentUser.OpenSubKey(key);
|
||||
return k?.GetValue(name) is int i ? i : -1;
|
||||
}
|
||||
catch { return -1; }
|
||||
}
|
||||
|
||||
private static Color Mix(Color c, Color with, double t) => Color.FromRgb(
|
||||
(byte)(c.R + (with.R - c.R) * t), (byte)(c.G + (with.G - c.G) * t), (byte)(c.B + (with.B - c.B) * t));
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AssemblyName>DeskCore-Setup</AssemblyName>
|
||||
<RootNamespace>DeskCoreSetup</RootNamespace>
|
||||
<ApplicationIcon>..\app.ico</ApplicationIcon>
|
||||
<Product>DeskCore Setup</Product>
|
||||
<AssemblyTitle>DeskCore Setup</AssemblyTitle>
|
||||
<Description>Installiert DeskCore</Description>
|
||||
<Company>M_Viper</Company>
|
||||
<Authors>M_Viper</Authors>
|
||||
<Copyright>Copyright (c) M_Viper. Alle Rechte vorbehalten.</Copyright>
|
||||
<NeutralLanguage>de-DE</NeutralLanguage>
|
||||
<!-- fertig veröffentlichte DeskCore.exe, die eingepackt wird (build.ps1 legt sie hier ab) -->
|
||||
<AppPayload Condition="'$(AppPayload)' == ''">..\artifacts\app\DeskCore.exe</AppPayload>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- gleiches Design wie die App -->
|
||||
<Page Include="..\Themes\Colors.Light.xaml" Link="Themes\Colors.Light.xaml" />
|
||||
<Page Include="..\Themes\Colors.Dark.xaml" Link="Themes\Colors.Dark.xaml" />
|
||||
<Page Include="..\Themes\Controls.xaml" Link="Themes\Controls.xaml" />
|
||||
<Resource Include="..\app.ico" Link="app.ico" />
|
||||
<Resource Include="..\app.png" Link="app.png" />
|
||||
<!-- Nutzlast -->
|
||||
<EmbeddedResource Include="$(AppPayload)" LogicalName="payload.DeskCore.exe" />
|
||||
<EmbeddedResource Include="..\LICENSE.txt" LogicalName="payload.LICENSE.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,368 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace DeskCoreSetup;
|
||||
|
||||
/// <summary>Installation/Deinstallation – nur für den aktuellen Benutzer, ohne Administratorrechte.</summary>
|
||||
public static class Installer
|
||||
{
|
||||
public const string AppName = "DeskCore";
|
||||
public const string Publisher = "M_Viper";
|
||||
public const string ExeName = "DeskCore.exe";
|
||||
public const string UninstallerName = "DeskCore-Uninstall.exe";
|
||||
private const string UninstallKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall\DeskCore";
|
||||
private const string RunKey = @"Software\Microsoft\Windows\CurrentVersion\Run";
|
||||
|
||||
public static string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
var v = typeof(Installer).Assembly.GetName().Version!;
|
||||
return $"{v.Major}.{v.Minor}.{v.Build}";
|
||||
}
|
||||
}
|
||||
|
||||
public static string DefaultDir =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", AppName);
|
||||
|
||||
private static string StartMenuLink =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), AppName + ".lnk");
|
||||
|
||||
private static string DesktopLink =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), AppName + ".lnk");
|
||||
|
||||
/// <summary>Ordner einer vorhandenen Installation (oder null).</summary>
|
||||
public static string? InstalledDir
|
||||
{
|
||||
get
|
||||
{
|
||||
using var k = Registry.CurrentUser.OpenSubKey(UninstallKey);
|
||||
return k?.GetValue("InstallLocation") as string is { } d && File.Exists(Path.Combine(d, ExeName)) ? d : null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string? InstalledVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
using var k = Registry.CurrentUser.OpenSubKey(UninstallKey);
|
||||
return k?.GetValue("DisplayVersion") as string;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AutostartEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
using var k = Registry.CurrentUser.OpenSubKey(RunKey);
|
||||
return k?.GetValue(AppName) is string;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DesktopLinkExists => File.Exists(DesktopLink);
|
||||
|
||||
public static string LicenseText => ReadResourceText("payload.LICENSE.txt");
|
||||
|
||||
// ---------- Installieren ----------
|
||||
|
||||
public static void Install(string dir, bool desktopLink, bool autostart, IProgress<(double, string)> progress)
|
||||
{
|
||||
progress.Report((0.05, "DeskCore wird beendet …"));
|
||||
CloseRunningApp();
|
||||
RemoveLegacyInstall();
|
||||
|
||||
progress.Report((0.2, "Dateien werden kopiert …"));
|
||||
Directory.CreateDirectory(dir);
|
||||
var exe = Path.Combine(dir, ExeName);
|
||||
WriteResource("payload.DeskCore.exe", exe);
|
||||
WriteResource("payload.LICENSE.txt", Path.Combine(dir, "LICENSE.txt"));
|
||||
|
||||
// Setup selbst als Deinstallationsprogramm ablegen
|
||||
var self = Environment.ProcessPath!;
|
||||
var uninstaller = Path.Combine(dir, UninstallerName);
|
||||
if (!string.Equals(Path.GetFullPath(self), Path.GetFullPath(uninstaller), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MoveAside(uninstaller);
|
||||
File.Copy(self, uninstaller, overwrite: true);
|
||||
}
|
||||
|
||||
progress.Report((0.55, "Verknüpfungen werden angelegt …"));
|
||||
CreateShortcut(StartMenuLink, exe);
|
||||
if (desktopLink) CreateShortcut(DesktopLink, exe);
|
||||
|
||||
progress.Report((0.75, "Windows wird eingerichtet …"));
|
||||
using (var run = Registry.CurrentUser.CreateSubKey(RunKey))
|
||||
{
|
||||
if (autostart) run.SetValue(AppName, $"\"{exe}\" --minimized");
|
||||
else run.DeleteValue(AppName, throwOnMissingValue: false);
|
||||
}
|
||||
|
||||
using (var k = Registry.CurrentUser.CreateSubKey(UninstallKey))
|
||||
{
|
||||
long size = new[] { exe, uninstaller, Path.Combine(dir, "LICENSE.txt") }
|
||||
.Where(File.Exists).Sum(f => new FileInfo(f).Length) / 1024;
|
||||
k.SetValue("DisplayName", AppName);
|
||||
k.SetValue("DisplayVersion", Version);
|
||||
k.SetValue("Publisher", Publisher);
|
||||
k.SetValue("DisplayIcon", $"{exe},0");
|
||||
k.SetValue("InstallLocation", dir);
|
||||
k.SetValue("UninstallString", $"\"{uninstaller}\" --uninstall");
|
||||
k.SetValue("QuietUninstallString", $"\"{uninstaller}\" --uninstall /S");
|
||||
k.SetValue("URLInfoAbout", "https://m-viper.de");
|
||||
k.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd"));
|
||||
k.SetValue("EstimatedSize", (int)size, RegistryValueKind.DWord);
|
||||
k.SetValue("NoModify", 1, RegistryValueKind.DWord);
|
||||
k.SetValue("NoRepair", 1, RegistryValueKind.DWord);
|
||||
}
|
||||
|
||||
progress.Report((1.0, "Fertig"));
|
||||
}
|
||||
|
||||
// ---------- Frühere Version "DeskHub" entfernen ----------
|
||||
|
||||
private const string LegacyName = "DeskHub";
|
||||
private const string LegacyUninstallKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall\DeskHub";
|
||||
|
||||
/// <summary>Ordner einer alten Installation unter dem früheren Namen "DeskHub" – nur unsere eigene.</summary>
|
||||
public static string? LegacyInstallDir
|
||||
{
|
||||
get
|
||||
{
|
||||
using var k = Registry.CurrentUser.OpenSubKey(LegacyUninstallKey);
|
||||
// "DeskHub" heißt auch ein anderes Programm → nur mit unserem Herausgeber anfassen
|
||||
if (k?.GetValue("Publisher") as string != Publisher) return null;
|
||||
return k.GetValue("InstallLocation") as string;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveLegacyInstall()
|
||||
{
|
||||
var dir = LegacyInstallDir;
|
||||
if (dir is null) return;
|
||||
var exe = Path.Combine(dir, LegacyName + ".exe");
|
||||
|
||||
// nur Prozesse beenden, die wirklich aus dem alten Ordner laufen
|
||||
foreach (var p in Process.GetProcessesByName(LegacyName))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (p.MainModule?.FileName is { } f && Storage_IsUnder(f, dir))
|
||||
{
|
||||
p.Kill(entireProcessTree: true);
|
||||
p.WaitForExit(10000);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
DeleteIfPointsTo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), LegacyName + ".lnk"), exe);
|
||||
DeleteIfPointsTo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), LegacyName + ".lnk"), exe);
|
||||
using (var run = Registry.CurrentUser.OpenSubKey(RunKey, writable: true))
|
||||
{
|
||||
if (run?.GetValue(LegacyName) is string cmd && cmd.Contains(exe, StringComparison.OrdinalIgnoreCase))
|
||||
run.DeleteValue(LegacyName, throwOnMissingValue: false);
|
||||
}
|
||||
|
||||
foreach (var f in new[] { exe, Path.Combine(dir, "LICENSE.txt"), Path.Combine(dir, LegacyName + "-Uninstall.exe") })
|
||||
MoveAside(f);
|
||||
CleanupOldFiles(dir);
|
||||
try { Directory.Delete(dir); } catch { } // nur wenn leer
|
||||
Registry.CurrentUser.DeleteSubKeyTree(LegacyUninstallKey, throwOnMissingSubKey: false);
|
||||
}
|
||||
|
||||
// ---------- Deinstallieren ----------
|
||||
|
||||
public static readonly string[] DataDirs =
|
||||
[
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), AppName), // Kategorien
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), AppName), // Einstellungen
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), AppName), // Rückgängig-Zwischenablage
|
||||
];
|
||||
|
||||
public static void Uninstall(bool removeData, IProgress<(double, string)> progress)
|
||||
{
|
||||
// Nur im eingetragenen Installationsordner löschen – nie dort, wo das Setup zufällig liegt (z. B. Downloads)
|
||||
var dir = InstalledDir;
|
||||
var exe = dir is null ? "" : Path.Combine(dir, ExeName);
|
||||
|
||||
progress.Report((0.1, "DeskCore wird beendet …"));
|
||||
CloseRunningApp();
|
||||
|
||||
progress.Report((0.3, "Verknüpfungen werden entfernt …"));
|
||||
DeleteIfPointsTo(StartMenuLink, exe);
|
||||
DeleteIfPointsTo(DesktopLink, exe);
|
||||
|
||||
progress.Report((0.45, "Windows-Einträge werden entfernt …"));
|
||||
using (var run = Registry.CurrentUser.OpenSubKey(RunKey, writable: true))
|
||||
run?.DeleteValue(AppName, throwOnMissingValue: false);
|
||||
Registry.CurrentUser.DeleteSubKeyTree(UninstallKey, throwOnMissingSubKey: false);
|
||||
|
||||
if (removeData)
|
||||
{
|
||||
progress.Report((0.6, "Kategorien und Einstellungen werden in den Papierkorb gelegt …"));
|
||||
foreach (var d in DataDirs.Where(Directory.Exists))
|
||||
{
|
||||
try
|
||||
{
|
||||
Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory(d,
|
||||
Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
|
||||
Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
progress.Report((0.85, "Dateien werden entfernt …"));
|
||||
if (dir is null) { progress.Report((1.0, "Fertig")); return; }
|
||||
TryDelete(exe);
|
||||
TryDelete(Path.Combine(dir, "LICENSE.txt"));
|
||||
CleanupOldFiles(dir);
|
||||
|
||||
// Das laufende Deinstallationsprogramm kann sich nicht selbst löschen → kurz nach dem Beenden per cmd.
|
||||
// rmdir ohne /s entfernt den Ordner nur, wenn er danach leer ist.
|
||||
var self = Environment.ProcessPath!;
|
||||
if (Storage_IsUnder(self, dir))
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("cmd.exe",
|
||||
$"/c ping 127.0.0.1 -n 3 > nul & del /f /q \"{self}\" & del /f /q \"{Path.Combine(dir, "*.old")}\" & rmdir \"{dir}\"")
|
||||
{
|
||||
CreateNoWindow = true, UseShellExecute = false, WindowStyle = ProcessWindowStyle.Hidden,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
TryDelete(Path.Combine(dir, UninstallerName));
|
||||
try { Directory.Delete(dir); } catch { } // nur wenn leer
|
||||
}
|
||||
progress.Report((1.0, "Fertig"));
|
||||
}
|
||||
|
||||
// ---------- Helfer ----------
|
||||
|
||||
/// <summary>Laufendes DeskCore beenden (das X versteckt es nur im Infobereich).</summary>
|
||||
private static void CloseRunningApp()
|
||||
{
|
||||
foreach (var p in Process.GetProcessesByName("DeskCore"))
|
||||
{
|
||||
try
|
||||
{
|
||||
p.Kill(entireProcessTree: true);
|
||||
p.WaitForExit(10000);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteResource(string name, string path)
|
||||
{
|
||||
CleanupOldFiles(Path.GetDirectoryName(path)!);
|
||||
MoveAside(path);
|
||||
for (int attempt = 1; ; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var s = typeof(Installer).Assembly.GetManifestResourceStream(name)
|
||||
?? throw new InvalidOperationException($"Installationsdatei {name} fehlt.");
|
||||
using var f = File.Create(path);
|
||||
s.CopyTo(f);
|
||||
return;
|
||||
}
|
||||
catch (IOException) when (attempt < 10)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
catch (IOException) when (attempt >= 10)
|
||||
{
|
||||
throw new IOException($"„{Path.GetFileName(path)}“ wird noch verwendet. Bitte DeskCore beenden (Rechtsklick auf das Symbol im Infobereich → Beenden) und erneut versuchen.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vorhandene Datei beiseite umbenennen. Das klappt unter Windows auch dann, wenn sie noch geöffnet ist
|
||||
/// (z. B. vom Explorer für das Icon oder vom Virenscanner) – überschreiben würde dagegen fehlschlagen.
|
||||
/// </summary>
|
||||
private static void MoveAside(string path)
|
||||
{
|
||||
if (!File.Exists(path)) return;
|
||||
try { File.Delete(path); return; } catch { }
|
||||
try { File.Move(path, $"{path}.{Guid.NewGuid():N}.old"); } catch { }
|
||||
}
|
||||
|
||||
/// <summary>Beiseitegelegte Dateien früherer Installationen wegräumen (sofern nicht mehr in Benutzung).</summary>
|
||||
private static void CleanupOldFiles(string dir)
|
||||
{
|
||||
if (!Directory.Exists(dir)) return;
|
||||
foreach (var f in Directory.GetFiles(dir, "*.old"))
|
||||
try { File.Delete(f); } catch { }
|
||||
}
|
||||
|
||||
private static string ReadResourceText(string name)
|
||||
{
|
||||
using var s = typeof(Installer).Assembly.GetManifestResourceStream(name);
|
||||
if (s is null) return "";
|
||||
using var r = new StreamReader(s);
|
||||
return r.ReadToEnd().Trim();
|
||||
}
|
||||
|
||||
private static void CreateShortcut(string link, string target)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(link)!);
|
||||
dynamic shell = Activator.CreateInstance(Type.GetTypeFromProgID("WScript.Shell")!)!;
|
||||
dynamic lnk = shell.CreateShortcut(link);
|
||||
lnk.TargetPath = target;
|
||||
lnk.WorkingDirectory = Path.GetDirectoryName(target);
|
||||
lnk.IconLocation = $"{target},0";
|
||||
lnk.Description = "Desktop-Icons in Kategorien sortieren";
|
||||
lnk.Save();
|
||||
}
|
||||
|
||||
private static void DeleteIfPointsTo(string link, string target)
|
||||
{
|
||||
if (!File.Exists(link)) return;
|
||||
try
|
||||
{
|
||||
dynamic shell = Activator.CreateInstance(Type.GetTypeFromProgID("WScript.Shell")!)!;
|
||||
string t = shell.CreateShortcut(link).TargetPath;
|
||||
if (!string.Equals(t, target, StringComparison.OrdinalIgnoreCase)) return;
|
||||
}
|
||||
catch { return; }
|
||||
// Explorer/Indexdienst halten Verknüpfungen manchmal kurz fest → mehrmals versuchen
|
||||
for (int i = 0; i < 10 && File.Exists(link); i++)
|
||||
{
|
||||
try { File.Delete(link); }
|
||||
catch { Thread.Sleep(300); }
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryDelete(string path)
|
||||
{
|
||||
try { if (File.Exists(path)) File.Delete(path); } catch { }
|
||||
}
|
||||
|
||||
private static bool Storage_IsUnder(string path, string dir) =>
|
||||
(Path.GetFullPath(path)).StartsWith(Path.GetFullPath(dir).TrimEnd('\\') + "\\", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static long DataSize()
|
||||
{
|
||||
long sum = 0;
|
||||
foreach (var d in DataDirs.Where(Directory.Exists))
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var f in Directory.EnumerateFiles(d, "*", SearchOption.AllDirectories))
|
||||
try { sum += new FileInfo(f).Length; } catch { }
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static string FormatSize(long bytes) => bytes switch
|
||||
{
|
||||
< 1024 * 1024 => $"{Math.Max(1, bytes / 1024)} KB",
|
||||
< 1024L * 1024 * 1024 => $"{bytes / 1024.0 / 1024:0.#} MB",
|
||||
_ => $"{bytes / 1024.0 / 1024 / 1024:0.##} GB",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<Window x:Class="DeskCoreSetup.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="DeskCore Setup" Icon="app.ico" Width="720" Height="500" ResizeMode="CanMinimize"
|
||||
Background="{DynamicResource BaseBrush}" Foreground="{DynamicResource TextBrush}"
|
||||
FontFamily="{DynamicResource UiFont}" FontSize="14"
|
||||
TextOptions.TextFormattingMode="Display" UseLayoutRounding="True"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="36" ResizeBorderThickness="0" GlassFrameThickness="0,0,0,1"
|
||||
CornerRadius="0" UseAeroCaptionButtons="False"/>
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Window.Resources>
|
||||
<Style x:Key="StepText" TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="0,0,0,14"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextTertiaryBrush}"/>
|
||||
</Style>
|
||||
<Style x:Key="PageTitle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="{DynamicResource DisplayFont}"/>
|
||||
<Setter Property="FontSize" Value="24"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||
</Style>
|
||||
<Style x:Key="Body" TargetType="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
<Setter Property="LineHeight" Value="21"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextSecondaryBrush}"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="36"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="220"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Titelleiste -->
|
||||
<Grid Grid.ColumnSpan="2">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="14,0,0,0">
|
||||
<Image Source="app.ico" Width="16" Height="16" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
<TextBlock x:Name="WindowTitle" Text="DeskCore Setup" FontSize="12" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
|
||||
<Button Style="{StaticResource CaptionButton}" Content="" Click="Minimize_Click"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"/>
|
||||
<Button x:Name="CloseButton" Style="{StaticResource CloseCaptionButton}" Content="" Click="Cancel_Click"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Seitenleiste mit Schritten -->
|
||||
<StackPanel Grid.Row="1" Grid.RowSpan="2" Margin="28,18,20,20">
|
||||
<Image Source="app.png" Width="72" Height="72" HorizontalAlignment="Left" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
<TextBlock Text="DeskCore" FontFamily="{DynamicResource DisplayFont}" FontSize="22" FontWeight="SemiBold" Margin="0,14,0,0"/>
|
||||
<TextBlock x:Name="VersionText" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12" Margin="0,2,0,28"/>
|
||||
<StackPanel x:Name="Steps">
|
||||
<TextBlock x:Name="Step0" Text="Willkommen" Style="{StaticResource StepText}"/>
|
||||
<TextBlock x:Name="Step1" Text="Lizenz" Style="{StaticResource StepText}"/>
|
||||
<TextBlock x:Name="Step2" Text="Optionen" Style="{StaticResource StepText}"/>
|
||||
<TextBlock x:Name="Step3" Text="Installation" Style="{StaticResource StepText}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Inhalt -->
|
||||
<Border Grid.Row="1" Grid.Column="1" Background="{DynamicResource LayerBrush}" BorderBrush="{DynamicResource LayerStrokeBrush}"
|
||||
BorderThickness="1,1,0,1" CornerRadius="8,0,0,8" Padding="32,28,32,20">
|
||||
<Grid>
|
||||
<!-- 0: Willkommen -->
|
||||
<StackPanel x:Name="PageWelcome">
|
||||
<TextBlock Text="Willkommen bei DeskCore" Style="{StaticResource PageTitle}"/>
|
||||
<TextBlock Style="{StaticResource Body}"
|
||||
Text="DeskCore sortiert deine Desktop-Icons in eigene Kategorien – mit Suche, Desktop-Boxen, automatischem Einsortieren und Rückgängig-Funktion."/>
|
||||
<TextBlock Style="{StaticResource Body}" Margin="0,14,0,0"
|
||||
Text="Der Assistent installiert DeskCore nur für dein Benutzerkonto. Administratorrechte sind nicht nötig."/>
|
||||
<Border x:Name="UpdateHint" Margin="0,22,0,0" Padding="14,10" CornerRadius="6" Visibility="Collapsed"
|
||||
Background="{DynamicResource AccentSubtleBrush}" BorderBrush="{DynamicResource AccentSubtleStrokeBrush}" BorderThickness="1">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Left" Text="" FontFamily="{DynamicResource IconFont}" FontSize="16"
|
||||
Foreground="{DynamicResource AccentBrush}" Margin="0,2,12,0" VerticalAlignment="Top"/>
|
||||
<TextBlock x:Name="UpdateHintText" TextWrapping="Wrap" LineHeight="20"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 1: Lizenz -->
|
||||
<DockPanel x:Name="PageLicense" Visibility="Collapsed">
|
||||
<TextBlock DockPanel.Dock="Top" Text="Lizenz- und Nutzungsbedingungen" Style="{StaticResource PageTitle}"/>
|
||||
<CheckBox x:Name="AcceptLicense" DockPanel.Dock="Bottom" Margin="0,14,0,0" Click="AcceptLicense_Click"
|
||||
Content="Ich akzeptiere die Lizenz- und Nutzungsbedingungen"/>
|
||||
<Border Background="{DynamicResource ControlFillBrush}" BorderBrush="{DynamicResource ControlStrokeBrush}"
|
||||
BorderThickness="1" CornerRadius="6">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock x:Name="LicenseText" TextWrapping="Wrap" FontSize="12.5" LineHeight="18" Margin="14,10"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
|
||||
<!-- 2: Optionen -->
|
||||
<StackPanel x:Name="PageOptions" Visibility="Collapsed">
|
||||
<TextBlock Text="Optionen" Style="{StaticResource PageTitle}"/>
|
||||
<TextBlock Text="Installationsordner" FontWeight="SemiBold" FontSize="13" Margin="0,4,0,8"/>
|
||||
<DockPanel>
|
||||
<Button DockPanel.Dock="Right" Content="Ändern …" Margin="8,0,0,0" Click="ChangeDir_Click"/>
|
||||
<TextBox x:Name="DirBox"/>
|
||||
</DockPanel>
|
||||
<CheckBox x:Name="DesktopLinkBox" Content="Verknüpfung auf dem Desktop" Margin="0,24,0,0" IsChecked="True"/>
|
||||
<CheckBox x:Name="StartMenuBox" Content="Eintrag im Startmenü" Margin="0,12,0,0" IsChecked="True" IsEnabled="False"/>
|
||||
<CheckBox x:Name="AutostartBox" Content="Mit Windows starten (unauffällig im Infobereich)" Margin="0,12,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 3: Fortschritt / Fertig -->
|
||||
<StackPanel x:Name="PageProgress" Visibility="Collapsed">
|
||||
<TextBlock x:Name="ProgressTitle" Text="DeskCore wird installiert …" Style="{StaticResource PageTitle}"/>
|
||||
<TextBlock x:Name="ProgressText" Style="{StaticResource Body}" Text="Einen Moment bitte."/>
|
||||
<Border x:Name="ProgressTrack" Height="4" CornerRadius="2" Margin="0,22,0,0" Background="{DynamicResource SubtleSelectedBrush}">
|
||||
<Border x:Name="ProgressFill" HorizontalAlignment="Left" Width="0" CornerRadius="2" Background="{DynamicResource AccentBrush}"/>
|
||||
</Border>
|
||||
<StackPanel x:Name="DonePanel" Visibility="Collapsed" Margin="0,26,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Border Width="28" Height="28" CornerRadius="14" Background="{DynamicResource SuccessBrush}">
|
||||
<TextBlock Text="" FontFamily="{DynamicResource IconFont}" FontSize="13" Foreground="White"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock x:Name="DoneText" VerticalAlignment="Center" Margin="12,0,0,0" FontWeight="SemiBold"/>
|
||||
</StackPanel>
|
||||
<CheckBox x:Name="LaunchBox" Content="DeskCore jetzt starten" IsChecked="True" Margin="0,22,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Deinstallation -->
|
||||
<StackPanel x:Name="PageUninstall" Visibility="Collapsed">
|
||||
<TextBlock Text="DeskCore deinstallieren" Style="{StaticResource PageTitle}"/>
|
||||
<TextBlock Style="{StaticResource Body}"
|
||||
Text="DeskCore wird von deinem PC entfernt: Programm, Verknüpfungen, Autostart und der Eintrag in den Windows-Einstellungen."/>
|
||||
<Border Margin="0,22,0,0" Padding="16,14" CornerRadius="6" Background="{DynamicResource ControlFillBrush}"
|
||||
BorderBrush="{DynamicResource ControlStrokeBrush}" BorderThickness="1">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Deine Kategorien bleiben standardmäßig erhalten" FontWeight="SemiBold"/>
|
||||
<TextBlock x:Name="DataInfo" Style="{StaticResource Body}" FontSize="12.5" Margin="0,4,0,12"/>
|
||||
<CheckBox x:Name="RemoveDataBox" Content="Auch Kategorien und Einstellungen entfernen (Papierkorb)"
|
||||
Click="RemoveDataBox_Click"/>
|
||||
<TextBlock x:Name="RemoveDataWarning" Visibility="Collapsed" Margin="30,8,0,0" TextWrapping="Wrap" FontSize="12.5"
|
||||
Foreground="{DynamicResource DangerBrush}"
|
||||
Text="Achtung: Dateien und Ordner, die nur noch in deinen Kategorien liegen, wandern damit in den Papierkorb."/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Fußleiste -->
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,14,20,18">
|
||||
<Button x:Name="BackButton" Content="Zurück" Width="110" Margin="0,0,8,0" Click="Back_Click"/>
|
||||
<Button x:Name="NextButton" Content="Weiter" Width="130" Margin="0,0,8,0" Style="{StaticResource AccentButton}" IsDefault="True" Click="Next_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Width="110" Click="Cancel_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,249 @@
|
||||
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 (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 (!Path.IsPathFullyQualified(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)
|
||||
{
|
||||
var dlg = new Microsoft.Win32.OpenFolderDialog { Title = "Installationsordner wählen" };
|
||||
if (dlg.ShowDialog(this) != true) return;
|
||||
var chosen = dlg.FolderName;
|
||||
// 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;
|
||||
try
|
||||
{
|
||||
await Task.Run(() => Installer.Install(dir, desktop, autostart, progress));
|
||||
_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;
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user