using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shell; using System.Windows.Threading; namespace DeskCore; /// /// Eine Box auf dem Desktop: zeigt eine oder mehrere Kategorien bzw. Ordner (Tabs), /// mit eigenem Aussehen, Einklappen, Sperren, Chamäleon und Peek. /// public partial class FenceWindow : Window { private const double CollapsedHeight = 38; private readonly DispatcherTimer _saveTimer; private readonly DispatcherTimer _collapseTimer; private FileSystemWatcher? _portalWatcher; private DispatcherTimer? _portalRefresh; private bool _applying; private bool _hoverExpanded; private bool _mouseInside; /// Unterordner, in den im Ordner-Portal navigiert wurde (null = Wurzel). private string? _portalSub; public FenceDef Def { get; } private static AppSettings S => AppSettings.Current; public FenceWindow(FenceDef def) { InitializeComponent(); Def = def; _saveTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(700), DispatcherPriority.Background, (_, _) => { _saveTimer!.Stop(); S.Save(); }, Dispatcher); _saveTimer.Stop(); _collapseTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) }; _collapseTimer.Tick += (_, _) => { _collapseTimer.Stop(); if (!_mouseInside && _hoverExpanded && !ItemMenu.IsOpen && !FenceMenu.IsOpen) CollapseAfterHover(); }; ApplyBounds(); LocationChanged += (_, _) => RememberBounds(); SizeChanged += (_, _) => RememberBounds(); SourceInitialized += (_, _) => PinToDesktop(); MouseEnter += (_, _) => OnHover(true); MouseLeave += (_, _) => OnHover(false); Closed += (_, _) => _portalWatcher?.Dispose(); Refresh(); } // ===================================================================== // Position, Größe, Einklappen // ===================================================================== private void ApplyBounds() { _applying = true; Width = Math.Max(MinWidth, Def.W); Height = Def.Collapsed && !_hoverExpanded ? CollapsedHeight : Math.Max(120, Def.H); var wa = Screens.PrimaryWorkArea; double left = Def.X ?? wa.Right - Width - 24, top = Def.Y ?? wa.Top + 24; // Bildschirm abgesteckt? → zurück auf den Hauptbildschirm (oben rechts) if (!Screens.IsReachable(new Rect(left, top, Width, Height))) { left = wa.Right - Width - 24; top = wa.Top + 24; Def.X = left; Def.Y = top; } Left = left; Top = top; Body.Visibility = Def.Collapsed && !_hoverExpanded ? Visibility.Collapsed : Visibility.Visible; _applying = false; } /// Position/Größe aus den Einstellungen neu übernehmen (z. B. nach Snapshot). public void Reapply() { ApplyBounds(); ApplyLook(); Refresh(); } public void EnsureOnScreen() { if (Screens.IsReachable(new Rect(Left, Top, ActualWidth, ActualHeight))) return; Def.X = null; Def.Y = null; ApplyBounds(); S.Save(); } private void RememberBounds() { if (_applying || !IsLoaded || _hoverExpanded) return; Def.X = Left; Def.Y = Top; Def.W = Width; if (!Def.Collapsed) Def.H = Height; _saveTimer.Stop(); _saveTimer.Start(); } private void ToggleCollapsed() { _hoverExpanded = false; Def.Collapsed = !Def.Collapsed; ApplyBounds(); S.Save(); } /// Eingeklappte Box klappt beim Überfahren kurz auf; Chamäleon blendet ein/aus. private void OnHover(bool inside) { _mouseInside = inside; if (inside) { _collapseTimer.Stop(); if (Def.Collapsed && !_hoverExpanded) { _hoverExpanded = true; ApplyBounds(); } } else if (_hoverExpanded) _collapseTimer.Start(); ApplyChameleon(); } private void CollapseAfterHover() { _hoverExpanded = false; ApplyBounds(); } // ===================================================================== // Aussehen // ===================================================================== public void ApplyLook() { // Hintergrund: eigene Farbe oder Design-Farbe, mit eingestellter Deckkraft var baseColor = Def.Background is { } hex && CategoryColors.Brush(hex) is SolidColorBrush custom ? custom.Color : ((SolidColorBrush)FindResource("FenceBrush")).Color; double opacity = Math.Clamp(Def.Opacity, 0.2, 1); if (Def.Blur) opacity = Math.Min(opacity, 0.75); // sonst sieht man vom Milchglas nichts var bg = new SolidColorBrush(Color.FromArgb((byte)(opacity * 255), baseColor.R, baseColor.G, baseColor.B)); bg.Freeze(); Root.Background = bg; Root.CornerRadius = new CornerRadius(Def.Blur ? 0 : 8); // Milchglas wirkt nur eckig sauber // Icon-Größe var (w, h, icon) = Def.IconSize switch { ViewMode.Small => (64.0, 66.0, 24.0), ViewMode.Large => (98.0, 104.0, 48.0), _ => (78.0, 80.0, 32.0), }; Resources["FTileW"] = w; Resources["FTileH"] = h; Resources["FIcon"] = icon; Resources["FTint"] = CategoryColors.Brush(Def.Tint) ?? Brushes.Transparent; // Sperren: nicht verschieb-/vergrößerbar Chrome.CaptionHeight = Def.Locked ? 0 : 36; Chrome.ResizeBorderThickness = Def.Locked ? new Thickness(0) : new Thickness(6); LockGlyph.Visibility = Def.Locked ? Visibility.Visible : Visibility.Collapsed; if (IsLoaded) SetBlur(Def.Blur); ApplyChameleon(); } /// Chamäleon: Box ist fast unsichtbar, bis die Maus darüberfährt (nicht während Peek). private void ApplyChameleon() { double target = S.FenceChameleon && !_mouseInside && !FenceManager.Peeking ? 0.08 : 1; Root.BeginAnimation(OpacityProperty, new DoubleAnimation(target, TimeSpan.FromMilliseconds(target < 1 ? 600 : 150))); } // ===================================================================== // Inhalt & Tabs // ===================================================================== private FenceTab? Tab => Def.Active; /// Ordner des aktiven Tabs (bei Ordner-Portalen ggf. ein Unterordner). private string? CurrentFolder => Tab switch { null => null, { IsFolder: true } t => _portalSub ?? t.Value, { } t => Storage.CategoryPath(t.Value), }; public void Refresh() { ApplyLook(); BuildTabs(); var tab = Tab; var folder = CurrentFolder; bool portal = tab?.IsFolder == true; PortalBar.Visibility = portal && _portalSub is not null ? Visibility.Visible : Visibility.Collapsed; if (portal && _portalSub is not null) PortalPath.Text = Path.GetRelativePath(tab!.Value, _portalSub); WatchPortal(portal ? folder : null); List items = []; if (folder is not null && Directory.Exists(folder)) items = (portal ? PortalEntries(folder) : Storage.Entries(folder)).Select(p => new IconItem { Path = p }).ToList(); items = Sort(items); int px = (int)Math.Round((double)Resources["FIcon"] * VisualTreeHelper.GetDpi(this).DpiScaleX); foreach (var i in items) IconLoader.Load(i, px); Items.ItemsSource = items; CountText.Text = items.Count.ToString(); EmptyText.Text = folder is not null && !Directory.Exists(folder) ? "Ordner nicht gefunden" : "Leer – Icons hierher ziehen"; EmptyText.Visibility = items.Count == 0 ? Visibility.Visible : Visibility.Collapsed; } /// Ordner-Portal zeigt alles außer versteckten/System-Dateien. private static IEnumerable PortalEntries(string dir) => Storage.Entries(dir); private List Sort(List items) { var folders = items.Where(i => Directory.Exists(i.Path)); var files = items.Where(i => !Directory.Exists(i.Path)); IEnumerable Order(IEnumerable x) => Def.Sort switch { "Typ" => x.OrderBy(i => Path.GetExtension(i.Path), StringComparer.OrdinalIgnoreCase).ThenBy(i => i.Name, StringComparer.CurrentCultureIgnoreCase), "Datum" => x.OrderByDescending(i => { try { return File.GetLastWriteTime(i.Path); } catch { return DateTime.MinValue; } }), _ => x.OrderBy(i => i.Name, StringComparer.CurrentCultureIgnoreCase), }; return Order(folders).Concat(Order(files)).ToList(); } private void BuildTabs() { TabStrip.Children.Clear(); for (int i = 0; i < Def.Tabs.Count; i++) { var t = Def.Tabs[i]; bool active = i == Math.Clamp(Def.ActiveTab, 0, Def.Tabs.Count - 1); var meta = t.IsFolder ? null : S.Categories.GetValueOrDefault(t.Value); var glyph = new TextBlock { Text = t.IsFolder ? "" : meta?.Glyph ?? "", FontFamily = (FontFamily)FindResource("IconFont"), FontSize = 13, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 7, 0), Foreground = CategoryColors.Brush(meta?.Color) ?? (Brush)FindResource(t.IsFolder ? "AccentBrush" : "TextBrush"), }; var label = new TextBlock { Text = t.Title, VerticalAlignment = VerticalAlignment.Center, TextTrimming = TextTrimming.CharacterEllipsis, FontSize = 13, MaxWidth = Def.Tabs.Count == 1 ? 220 : 120, FontWeight = active ? FontWeights.SemiBold : FontWeights.Normal, Foreground = (Brush)FindResource(active ? "TextBrush" : "TextSecondaryBrush"), }; var content = new StackPanel { Orientation = Orientation.Horizontal, Children = { glyph, label } }; var underline = new Border { Height = 2, CornerRadius = new CornerRadius(1), Margin = new Thickness(4, 0, 4, 0), VerticalAlignment = VerticalAlignment.Bottom, Background = active && Def.Tabs.Count > 1 ? (Brush)FindResource("AccentBrush") : Brushes.Transparent, }; var btn = new Button { Style = (Style)FindResource("SubtleButton"), Padding = new Thickness(6, 4, 6, 4), MinHeight = 0, Tag = i, Content = new Grid { Children = { content, underline } }, Margin = new Thickness(0, 0, 2, 0), ToolTip = t.IsFolder ? t.Value : $"Kategorie „{t.Value}“", }; WindowChrome.SetIsHitTestVisibleInChrome(btn, Def.Tabs.Count > 1); // bei nur einem Tab: Titel = Ziehfläche btn.Click += (_, _) => SwitchTab((int)btn.Tag); btn.MouseRightButtonUp += (_, ev) => { ShowTabMenu((int)btn.Tag); ev.Handled = true; }; TabStrip.Children.Add(btn); } } private void SwitchTab(int index) { if (index == Def.ActiveTab) return; Def.ActiveTab = index; _portalSub = null; S.Save(); Refresh(); } // ---------- Ordner-Portal ---------- private void WatchPortal(string? folder) { if (string.Equals(_portalWatcher?.Path, folder, StringComparison.OrdinalIgnoreCase)) return; _portalWatcher?.Dispose(); _portalWatcher = null; if (folder is null || !Directory.Exists(folder)) return; _portalRefresh ??= new DispatcherTimer(TimeSpan.FromMilliseconds(400), DispatcherPriority.Background, (_, _) => { _portalRefresh!.Stop(); Refresh(); }, Dispatcher); try { _portalWatcher = new FileSystemWatcher(folder) { NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite, EnableRaisingEvents = true, }; FileSystemEventHandler poke = (_, _) => Dispatcher.BeginInvoke(() => { _portalRefresh.Stop(); _portalRefresh.Start(); }); _portalWatcher.Created += poke; _portalWatcher.Deleted += poke; _portalWatcher.Renamed += (s, e) => poke(s, e); } catch { } } private void PortalUp_Click(object sender, RoutedEventArgs e) { if (Tab is not { IsFolder: true } t || _portalSub is null) return; var parent = Path.GetDirectoryName(_portalSub.TrimEnd('\\')); _portalSub = parent is null || Storage.SamePath(parent, t.Value) || !Storage.IsUnder(parent, t.Value) ? null : parent; Refresh(); } // ===================================================================== // Icons // ===================================================================== private List Selected => Items.SelectedItems.Cast().ToList(); private void Open(IconItem i) { // Im Ordner-Portal öffnen Ordner sich innerhalb der Box if (Tab?.IsFolder == true && Directory.Exists(i.Path) && !i.Path.EndsWith(".lnk", StringComparison.OrdinalIgnoreCase)) { _portalSub = i.Path; Refresh(); return; } try { Process.Start(new ProcessStartInfo(i.Path) { UseShellExecute = true }); } catch { } } private void Items_MouseDoubleClick(object sender, MouseButtonEventArgs e) { if (MainWindow.FindParent(e.OriginalSource as DependencyObject)?.DataContext is IconItem i) Open(i); } private void Items_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Escape && FenceManager.Peeking) { FenceManager.EndPeek(); e.Handled = true; return; } var sel = Selected; if (sel.Count == 0 || App.Win is not { } win) return; switch (e.Key) { case Key.Enter: sel.ForEach(Open); e.Handled = true; break; case Key.Delete: win.DeleteIcons(sel); e.Handled = true; break; case Key.F2 when sel.Count == 1: win.RenameIcon(sel[0]); e.Handled = true; break; case Key.Back when _portalSub is not null: PortalUp_Click(this, e); e.Handled = true; break; } } private void Items_ContextMenuOpening(object sender, ContextMenuEventArgs e) { var sel = Selected; if (sel.Count == 0 || App.Win is not { } win) { e.Handled = true; return; } ItemMenu.Items.Clear(); ItemMenu.Items.Add(MainWindow.MenuItem("Öffnen", "", () => sel.ForEach(Open), bold: true, gesture: "Enter")); if (sel.Count == 1) { ItemMenu.Items.Add(MainWindow.MenuItem("Dateispeicherort öffnen", "", () => Process.Start("explorer.exe", $"/select,\"{sel[0].Path}\""))); ItemMenu.Items.Add(MainWindow.MenuItem("Umbenennen", "", () => win.RenameIcon(sel[0]), gesture: "F2")); } ItemMenu.Items.Add(new Separator()); bool inStorage = sel.All(i => Storage.IsInStorage(i.Path)); var moveTo = MainWindow.MenuItem(inStorage ? "Verschieben nach" : "Sichern in", inStorage ? "" : "", null); var ownCategory = Tab is { IsFolder: false } t ? t.Value : null; foreach (var c in Storage.Categories().Where(c => !string.Equals(c, ownCategory, StringComparison.OrdinalIgnoreCase))) moveTo.Items.Add(MainWindow.MenuItem(c, S.Categories.GetValueOrDefault(c)?.Glyph ?? "", () => win.TransferAll(sel.Select(i => i.Path), Storage.CategoryPath(c)))); if (moveTo.Items.Count > 0) ItemMenu.Items.Add(moveTo); ItemMenu.Items.Add(win.LaunchGroupSubmenu(sel.Select(i => i.Path).ToList())); if (inStorage) ItemMenu.Items.Add(MainWindow.MenuItem("Zurück auf den Desktop legen", "", () => win.TransferAll(sel.Select(i => i.Path), Storage.UserDesktop))); ItemMenu.Items.Add(MainWindow.MenuItem("Löschen", "", () => win.DeleteIcons(sel), gesture: "Entf")); } // ---------- Ziehen ---------- private Point _dragStart; private bool _dragging; private void Items_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => _dragStart = e.GetPosition(null); private void Items_PreviewMouseMove(object sender, MouseEventArgs e) { if (e.LeftButton != MouseButtonState.Pressed || _dragging) return; var d = e.GetPosition(null) - _dragStart; if (Math.Abs(d.X) < SystemParameters.MinimumHorizontalDragDistance && Math.Abs(d.Y) < SystemParameters.MinimumVerticalDragDistance) return; if (MainWindow.FindParent(e.OriginalSource as DependencyObject) is null) return; var paths = Selected.Select(i => i.Path).ToArray(); if (paths.Length == 0) return; _dragging = true; try { // nur "Kopieren" erlauben: der Explorer soll die Datei nicht aus der Kategorie entfernen. // In eine andere Box/Kategorie gezogen, verschiebt DeskCore selbst. DragDrop.DoDragDrop(Items, new DataObject(DataFormats.FileDrop, paths), DragDropEffects.Copy | DragDropEffects.Link); } finally { _dragging = false; } } private void Items_DragOver(object sender, DragEventArgs e) { e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop) && !_dragging && CurrentFolder is not null ? (e.AllowedEffects.HasFlag(DragDropEffects.Copy) ? DragDropEffects.Copy : DragDropEffects.Move) : DragDropEffects.None; e.Handled = true; } private void Items_Drop(object sender, DragEventArgs e) { if (!_dragging && CurrentFolder is { } folder && e.Data.GetData(DataFormats.FileDrop) is string[] files) App.Win?.TransferAll(files, folder); } // ===================================================================== // Menüs // ===================================================================== private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { // gesperrte Box: Titelleiste ist normale Fläche → Doppelklick selbst auswerten if (e.ClickCount == 2) { ToggleCollapsed(); e.Handled = true; } } private void MenuButton_Click(object sender, RoutedEventArgs e) { FenceMenu.Items.Clear(); if (Tab is { IsFolder: false } t) FenceMenu.Items.Add(MainWindow.MenuItem("In DeskCore öffnen", "", () => App.Win?.ShowCategory(t.Value))); else if (Tab is { IsFolder: true } pt) FenceMenu.Items.Add(MainWindow.MenuItem("Im Explorer öffnen", "", () => Process.Start("explorer.exe", $"\"{CurrentFolder ?? pt.Value}\""))); // Tabs var add = MainWindow.MenuItem("Tab hinzufügen", "", null); foreach (var c in Storage.Categories().Where(c => !Def.Tabs.Any(x => !x.IsFolder && string.Equals(x.Value, c, StringComparison.OrdinalIgnoreCase)))) add.Items.Add(MainWindow.MenuItem(c, S.Categories.GetValueOrDefault(c)?.Glyph ?? "", () => AddTab(new FenceTab { Value = c }))); if (add.Items.Count > 0) add.Items.Add(new Separator()); add.Items.Add(MainWindow.MenuItem("Ordner-Portal…", "", () => { if (FenceManager.PickFolder() is { } folder) AddTab(new FenceTab { Kind = FenceTab.FolderKind, Value = folder }); })); FenceMenu.Items.Add(add); if (Def.Tabs.Count > 1) FenceMenu.Items.Add(MainWindow.MenuItem($"Tab „{Tab?.Title}“ entfernen", "", () => RemoveTab(Def.ActiveTab))); FenceMenu.Items.Add(new Separator()); FenceMenu.Items.Add(MainWindow.MenuItem("Aussehen…", "", () => FenceManager.EditLook(this))); FenceMenu.Items.Add(MainWindow.MenuItem(Def.Collapsed ? "Ausklappen" : "Einklappen", Def.Collapsed ? "" : "", ToggleCollapsed)); FenceMenu.Items.Add(MainWindow.MenuItem(Def.Locked ? "Entsperren" : "Sperren (nicht mehr verschiebbar)", Def.Locked ? "" : "", () => { Def.Locked = !Def.Locked; S.Save(); ApplyLook(); })); if (S.Pages.Count > 1) { var move = MainWindow.MenuItem("Auf Seite verschieben", "\uE8AB", null); for (int p = 0; p < S.Pages.Count; p++) { int page = p; var mi = MainWindow.MenuItem(S.Pages[p], "\uE8A1", () => FenceManager.MoveToPage(Def, page)); mi.IsEnabled = page != Def.Page; move.Items.Add(mi); } FenceMenu.Items.Add(move); } FenceMenu.Items.Add(new Separator()); FenceMenu.Items.Add(MainWindow.MenuItem("Box schließen", "", () => FenceManager.Remove(Def))); FenceMenu.PlacementTarget = MenuButton; FenceMenu.IsOpen = true; } private void ShowTabMenu(int index) { if (index < 0 || index >= Def.Tabs.Count) return; var t = Def.Tabs[index]; var menu = new ContextMenu(); if (!t.IsFolder) menu.Items.Add(MainWindow.MenuItem("In DeskCore öffnen", "", () => App.Win?.ShowCategory(t.Value))); else menu.Items.Add(MainWindow.MenuItem("Im Explorer öffnen", "", () => Process.Start("explorer.exe", $"\"{t.Value}\""))); if (Def.Tabs.Count > 1) { menu.Items.Add(MainWindow.MenuItem("Als eigene Box lösen", "", () => FenceManager.DetachTab(Def, index))); menu.Items.Add(MainWindow.MenuItem("Tab entfernen", "", () => RemoveTab(index))); } menu.IsOpen = true; } private void AddTab(FenceTab tab) { // Kategorie steckt schon in einer anderen Box? Dann dorthin verschieben statt doppelt anzeigen if (!tab.IsFolder) FenceManager.RemoveCategoryTab(tab.Value, except: Def); Def.Tabs.Add(tab); Def.ActiveTab = Def.Tabs.Count - 1; _portalSub = null; S.Save(); FenceManager.Sync(); Refresh(); } private void RemoveTab(int index) { if (index < 0 || index >= Def.Tabs.Count) return; Def.Tabs.RemoveAt(index); Def.ActiveTab = Math.Clamp(Def.ActiveTab, 0, Math.Max(0, Def.Tabs.Count - 1)); _portalSub = null; S.Save(); if (Def.Tabs.Count == 0) FenceManager.Remove(Def); else Refresh(); } // ===================================================================== // Auf dem Desktop festhalten, Peek, Milchglas // ===================================================================== private IntPtr Hwnd => new WindowInteropHelper(this).Handle; private void PinToDesktop() { var hwnd = Hwnd; // Besitzer = Desktop (Progman): bleibt bei Win+D sichtbar und liegt über den Desktop-Icons var progman = FindWindow("Progman", null); if (progman != IntPtr.Zero) SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, progman); // Nicht in Alt+Tab var ex = GetWindowLongPtr(hwnd, GWL_EXSTYLE).ToInt64(); SetWindowLongPtr(hwnd, GWL_EXSTYLE, new IntPtr((ex | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW)); HwndSource.FromHwnd(hwnd).AddHook(Hook); SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); SetBlur(Def.Blur); } /// Peek: Box vor alle Fenster holen bzw. wieder ganz nach hinten. public void SetPeek(bool on) { var hwnd = Hwnd; if (hwnd == IntPtr.Zero) return; SetWindowPos(hwnd, on ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); if (!on) SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); ApplyChameleon(); } private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case WM_WINDOWPOSCHANGING when !FenceManager.Peeking: // immer ganz unten bleiben, nie über anderen Fenstern (außer während Peek) var wp = Marshal.PtrToStructure(lParam); wp.hwndInsertAfter = HWND_BOTTOM; wp.flags &= ~SWP_NOZORDER; Marshal.StructureToPtr(wp, lParam, false); break; case WM_NCLBUTTONDBLCLK: ToggleCollapsed(); handled = true; break; case WM_SYSCOMMAND when (wParam.ToInt32() & 0xFFF0) == SC_MAXIMIZE: handled = true; break; } return IntPtr.Zero; } /// Milchglas (verschwommener Hintergrund) über die Windows-Composition-Schnittstelle. private void SetBlur(bool on) { var hwnd = Hwnd; if (hwnd == IntPtr.Zero) return; var accent = new AccentPolicy { AccentState = on ? 3 : 0 }; // 3 = ACCENT_ENABLE_BLURBEHIND int size = Marshal.SizeOf(accent); var ptr = Marshal.AllocHGlobal(size); try { Marshal.StructureToPtr(accent, ptr, false); var data = new WindowCompositionAttributeData { Attribute = 19, Data = ptr, SizeOfData = size }; // 19 = WCA_ACCENT_POLICY SetWindowCompositionAttribute(hwnd, ref data); } catch { } finally { Marshal.FreeHGlobal(ptr); } } private const int GWL_EXSTYLE = -20, GWLP_HWNDPARENT = -8; private const long WS_EX_TOOLWINDOW = 0x80, WS_EX_APPWINDOW = 0x40000; private const int WM_WINDOWPOSCHANGING = 0x46, WM_NCLBUTTONDBLCLK = 0xA3, WM_SYSCOMMAND = 0x112, SC_MAXIMIZE = 0xF030; private const uint SWP_NOSIZE = 0x1, SWP_NOMOVE = 0x2, SWP_NOZORDER = 0x4, SWP_NOACTIVATE = 0x10; private static readonly IntPtr HWND_BOTTOM = new(1), HWND_TOPMOST = new(-1), HWND_NOTOPMOST = new(-2); [StructLayout(LayoutKind.Sequential)] private struct WINDOWPOS { public IntPtr hwnd, hwndInsertAfter; public int x, y, cx, cy; public uint flags; } [StructLayout(LayoutKind.Sequential)] private struct AccentPolicy { public int AccentState, AccentFlags, GradientColor, AnimationId; } [StructLayout(LayoutKind.Sequential)] private struct WindowCompositionAttributeData { public int Attribute; public IntPtr Data; public int SizeOfData; } [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr FindWindow(string? cls, string? title); [DllImport("user32.dll")] private static extern IntPtr GetWindowLongPtr(IntPtr h, int i); [DllImport("user32.dll")] private static extern IntPtr SetWindowLongPtr(IntPtr h, int i, IntPtr v); [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr h, IntPtr after, int x, int y, int cx, int cy, uint flags); [DllImport("user32.dll")] private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); }