From 2ecf8f933833fcc3f1c50314e5f264257955cc25 Mon Sep 17 00:00:00 2001 From: Xiangxin <1934109821@qq.com> Date: Fri, 13 Feb 2026 15:53:41 +0800 Subject: [PATCH 1/5] Update TaskbarForm.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 传递配置参数,添加任务栏位置缓存清理函数 --- src/UI/TaskbarForm.cs | 489 +++++++++++++++++++++++++----------------- 1 file changed, 295 insertions(+), 194 deletions(-) diff --git a/src/UI/TaskbarForm.cs b/src/UI/TaskbarForm.cs index fa3972e..924bc1b 100644 --- a/src/UI/TaskbarForm.cs +++ b/src/UI/TaskbarForm.cs @@ -1,265 +1,366 @@ -using LiteMonitor.src.Core; -using LiteMonitor.src.UI.Helpers; +using Microsoft.Win32; using System; -using System.Collections.Generic; using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Windows.Forms; +using LiteMonitor.src.Core; using static LiteMonitor.src.UI.Helpers.NativeMethods; -namespace LiteMonitor +namespace LiteMonitor.src.UI.Helpers { - public class TaskbarForm : Form + /// + /// 任务栏集成策略接口 + /// 定义了不同系统版本下任务栏挂载和布局的统一行为 + /// + public interface ITaskbarStrategy + { + /// + /// 是否准备就绪 + /// + bool IsReady { get; } + + /// + /// 挂载到任务栏 + /// + void Attach(IntPtr taskbarHandle); + + /// + /// 设置位置和大小 + /// + void SetPosition(IntPtr taskbarHandle, int left, int top, int w, int h, int manualOffset, bool alignLeft); + + /// + /// 恢复任务栏原始布局(如果做过修改) + /// + void Restore(); + + /// + /// 获取期望的父窗口句柄(用于检测是否脱离) + /// + IntPtr GetExpectedParent(IntPtr taskbarHandle); + + /// + /// 是否拥有内部布局逻辑(如 Win10 挤占模式) + /// + bool HasInternalLayout { get; } + } + + internal static class NativeMethods { + public const int GWL_STYLE = -16; + public const int GWL_EXSTYLE = -20; + public const int WS_CHILD = 0x40000000; + public const int WS_VISIBLE = 0x10000000; + public const int WS_CLIPSIBLINGS = 0x04000000; + public const int WS_EX_LAYERED = 0x80000; + public const int WS_EX_TOOLWINDOW = 0x00000080; + public const uint LWA_COLORKEY = 0x00000001; + public const uint SWP_NOZORDER = 0x0004; + public const uint SWP_NOACTIVATE = 0x0010; + public const int WS_EX_TRANSPARENT = 0x00000020; + public const int DWMWA_EXTENDED_FRAME_BOUNDS = 9; + + [StructLayout(LayoutKind.Sequential)] + public struct POINT { public int X, Y; } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT { public int left, top, right, bottom; } + + [DllImport("user32.dll")] public static extern IntPtr FindWindow(string cls, string? name); + [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string cls, string? name); + [DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr hWnd, int idx); + [DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr hWnd, int idx, int value); + [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect); + [DllImport("user32.dll")] public static extern bool ScreenToClient(IntPtr hWnd, ref POINT pt); + [DllImport("user32.dll")] public static extern IntPtr SetParent(IntPtr child, IntPtr parent); + [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint flags); + [DllImport("user32.dll")] public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); + [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] public static extern IntPtr GetParent(IntPtr hWnd); + [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); + [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindow(IntPtr hWnd); + [DllImport("dwmapi.dll")] public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); + [DllImport("user32.dll")] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); + [DllImport("user32.dll")] public static extern uint GetDpiForWindow(IntPtr hWnd); + } + + /// + /// 任务栏窗口底层助手 (Windows Helper) - Facade + /// 职责:作为统一入口,根据系统版本委托给具体的策略 (Strategy) 处理挂载和布局 + /// + public class TaskbarWinHelper + { + private readonly Form _form; private readonly Settings _cfg; - private readonly UIController _ui; - private readonly MainForm _mainForm; - private readonly System.Windows.Forms.Timer _timer = new(); + private readonly ITaskbarStrategy _strategy; - // ★★★ 双助手架构 ★★★ - private readonly TaskbarWinHelper _winHelper; - private readonly TaskbarBizHelper _bizHelper; - - private HorizontalLayout _layout; - private List? _cols; - private ContextMenuStrip? _currentMenu; - private DateTime _lastFindHandleTime = DateTime.MinValue; - private string _lastLayoutSignature = ""; - private readonly TaskbarTooltipHelper _tooltipHelper; - - // 公开属性 - public string TargetDevice { get; private set; } = ""; + // ★★★ 性能优化缓存 ★★★ + private Rectangle _lastWindowRect = Rectangle.Empty; + private Rectangle _cachedResult = Rectangle.Empty; + private bool _isCacheValid = false; - // 判断菜单是否打开 - public bool IsMenuOpen => _currentMenu != null && !_currentMenu.IsDisposed && _currentMenu.Visible; - - private const int WM_RBUTTONDOWN = 0x0204; - private const int WM_RBUTTONUP = 0x0205; - private const int WM_LBUTTONDBLCLK = 0x0203; - private bool _isWin11; + // [Optimization] 静态缓存系统版本检测结果 + private static readonly bool _isWin11 = Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 22000; - public TaskbarForm(Settings cfg, UIController ui, MainForm mainForm) + public bool UsesInternalLayout => _strategy.HasInternalLayout; + + public TaskbarWinHelper(Form form, Settings cfg) { + _form = form; _cfg = cfg; - _ui = ui; - _mainForm = mainForm; - TargetDevice = _cfg.TaskbarMonitorDevice; - - _isWin11 = Environment.OSVersion.Version >= new Version(10, 0, 22000); - - // 初始化组件 - _winHelper = new TaskbarWinHelper(this); - _bizHelper = new TaskbarBizHelper(this, _cfg, _winHelper); - - // 窗体属性 - FormBorderStyle = FormBorderStyle.None; - ShowInTaskbar = false; - ControlBox = false; - TopMost = false; - DoubleBuffered = true; - - ReloadLayout(); - - _bizHelper.CheckTheme(true); - _bizHelper.FindHandles(); - - _bizHelper.AttachToTaskbar(); - _winHelper.ApplyLayeredStyle(_bizHelper.TransparentKey, _cfg.TaskbarClickThrough); - - _timer.Interval = Math.Max(_cfg.RefreshMs, 60); - _timer.Tick += (_, __) => Tick(); - _timer.Start(); - - // 鼠标悬浮提示初始化 - _tooltipHelper = new TaskbarTooltipHelper(this, _cfg, _ui); - - Tick(); + // 策略工厂模式:根据系统版本选择合适的集成策略 + if (_isWin11) + { + _strategy = new TaskbarStrategyWin11(form); + } + else + { + _strategy = new TaskbarStrategyWin10(form); + } } - public void ReloadLayout() + // ================================================================= + // 样式与图层 + // ================================================================= + public void ApplyLayeredStyle(Color transparentKey, bool clickThrough) { - _layout = new HorizontalLayout(ThemeManager.Current, 300, LayoutMode.Taskbar, _cfg); - _lastLayoutSignature = ""; // 重置签名,强制重算 - _winHelper.ApplyLayeredStyle(_bizHelper.TransparentKey, _cfg.TaskbarClickThrough); - _bizHelper.CheckTheme(true); - - // 更新悬浮窗模式 (支持热切换) - _tooltipHelper?.ReloadMode(); - - // 注意:这里仍然可能因为 _cols 为空而暂时不 Build, - // 但随后的 Tick 会在获取到新数据后自动 Build - if (_cols != null && _cols.Count > 0) + _form.BackColor = transparentKey; + + if (_form.IsHandleCreated) { - _layout.Build(_cols, _bizHelper.Height); - Width = _layout.PanelWidth; - _bizHelper.UpdatePlacement(Width); + uint colorKey = (uint)(transparentKey.R | (transparentKey.G << 8) | (transparentKey.B << 16)); + SetLayeredWindowAttributes(_form.Handle, colorKey, 0, LWA_COLORKEY); } - Invalidate(); + + int exStyle = GetWindowLong(_form.Handle, GWL_EXSTYLE); + if (clickThrough) exStyle |= WS_EX_TRANSPARENT; + else exStyle &= ~WS_EX_TRANSPARENT; + SetWindowLong(_form.Handle, GWL_EXSTYLE, exStyle); + + _form.Invalidate(); } - protected override void Dispose(bool disposing) + public bool IsSystemLightTheme() { - if (disposing) + try { - _winHelper?.RestoreTaskbar(); - _timer.Stop(); - _timer.Dispose(); - _currentMenu?.Dispose(); - _tooltipHelper?.Dispose(); + using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"); + if (key != null) + { + object? val = key.GetValue("SystemUsesLightTheme"); + if (val is int i) return i == 1; + } } - base.Dispose(disposing); + catch { } + return false; } - protected override void WndProc(ref Message m) + // ================================================================= + // 挂载逻辑 (委托给策略) + // ================================================================= + public void AttachToTaskbar(IntPtr taskbarHandle) { - // [Fix] 兼容性修复:在 Win11 25H2 + StartAllBack 环境下, - // 右键事件会穿透到原生任务栏。 - // 因此不再区分系统版本,统一拦截右键按下和抬起消息。 - if (m.Msg == WM_RBUTTONDOWN) - { - return; // 吞掉按下事件,防止穿透 - } + _strategy.Attach(taskbarHandle); + } - if (m.Msg == WM_RBUTTONUP) - { - this.BeginInvoke(new Action(ShowContextMenu)); - return; - } + public void SetPosition(IntPtr taskbarHandle, int left, int top, int w, int h, int manualOffset = 0, bool alignLeft = true) + { + // 检查父窗口是否正确,防止脱离 + IntPtr currentParent = GetParent(_form.Handle); + IntPtr expectedParent = _strategy.GetExpectedParent(taskbarHandle); - // [Fix] 强制拦截双击事件 - // 当悬浮窗(Tooltip)显示时,WinForms 的标准双击事件可能因为焦点/激活状态的微妙变化而失效。 - // 这里直接在消息层处理 WM_LBUTTONDBLCLK,确保双击动作始终能被触发。 - if (m.Msg == WM_LBUTTONDBLCLK) + if (currentParent != expectedParent) { - _bizHelper.HandleDoubleClick(_mainForm, _ui); - return; + AttachToTaskbar(taskbarHandle); } - base.WndProc(ref m); + _strategy.SetPosition(taskbarHandle, left, top, w, h, manualOffset, alignLeft); } - private void ShowContextMenu() + public void RestoreTaskbar() { - if (_currentMenu != null) - { - _currentMenu.Dispose(); - _currentMenu = null; - } + _strategy.Restore(); + } - _currentMenu = MenuManager.Build(_mainForm, _cfg, _ui, "Taskbar"); - - TaskbarWinHelper.ActivateWindow(this.Handle); - _currentMenu.Show(Cursor.Position); + public void InvalidateCache() + { + _isCacheValid = false; } - protected override void OnMouseUp(MouseEventArgs e) + // ================================================================= + // 句柄与信息获取 (通用逻辑) + // ================================================================= + public (IntPtr hTaskbar, IntPtr hTray) FindHandles(string targetDevice) { - base.OnMouseUp(e); - if (e.Button == MouseButtons.Right) + Screen target = Screen.PrimaryScreen; + if (!string.IsNullOrEmpty(targetDevice)) { - ShowContextMenu(); + target = Screen.AllScreens.FirstOrDefault(s => s.DeviceName == targetDevice) ?? Screen.PrimaryScreen; } - } - protected override void OnMouseDoubleClick(MouseEventArgs e) - { - base.OnMouseDoubleClick(e); - if (e.Button == MouseButtons.Left) + if (target.Primary) { - _bizHelper.HandleDoubleClick(_mainForm, _ui); + IntPtr hTaskbar = FindWindow("Shell_TrayWnd", null); + IntPtr hTray = FindWindowEx(hTaskbar, IntPtr.Zero, "TrayNotifyWnd", null); + return (hTaskbar, hTray); + } + else + { + IntPtr hTaskbar = FindSecondaryTaskbar(target); + return (hTaskbar, IntPtr.Zero); } } - private void Tick() + private IntPtr FindSecondaryTaskbar(Screen screen) { - // [Fix] 周期性检查句柄,防止 Explorer 重启后句柄失效 - // 优化:仅在重试期或句柄无效时调用 FindHandles,且限制调用频率 - bool isHandleInvalid = !_bizHelper.IsTaskbarValid(); - - // 如果处于重试期,或者句柄无效且距离上次查找超过2秒(防止无Explorer时高频空转) - if (isHandleInvalid && (DateTime.Now - _lastFindHandleTime).TotalSeconds > 2) + IntPtr hWnd = IntPtr.Zero; + while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "Shell_SecondaryTrayWnd", null)) != IntPtr.Zero) { - _bizHelper.FindHandles(); - _lastFindHandleTime = DateTime.Now; + GetWindowRect(hWnd, out RECT rect); + Rectangle r = Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); + if (screen.Bounds.Contains(r.Location) || screen.Bounds.IntersectsWith(r)) + return hWnd; } + return FindWindow("Shell_TrayWnd", null); + } - if (Math.Abs(Environment.TickCount) % 5000 < _cfg.RefreshMs) _bizHelper.CheckTheme(); + public Rectangle GetTaskbarRect(IntPtr hTaskbar, string targetDevice) + { + if (hTaskbar == IntPtr.Zero) return Rectangle.Empty; - // [Fix Part 1] 防空数据保护 - // 使用临时变量接收,先判断数据有效性,再赋值给成员变量 _cols - // 防止在 UI 重建期间(RebuildLayout)获取到空列表导致任务栏闪烁或清空 - var nextCols = _ui.GetTaskbarColumns(); - if (nextCols == null || nextCols.Count == 0) return; - - _cols = nextCols; // 确认有效后再更新引用 + // 1. 获取物理矩形 + if (!GetWindowRect(hTaskbar, out RECT r)) return Rectangle.Empty; + var rectPhys = Rectangle.FromLTRB(r.left, r.top, r.right, r.bottom); - _bizHelper.UpdateTaskbarRect(); - - // [Fix Part 2] 布局变更检测 - if (_bizHelper.IsVertical()) - { - // 垂直模式逻辑简单且无测量开销,直接重算即可 - _bizHelper.BuildVerticalLayout(_cols); - _lastLayoutSignature = "vertical"; - } - else - { - // [优化] 智能判断更新条件 - // 1. 必须检查:如果列表是新生成的(坐标还没算过,Bounds为空),必须重算! - // 这解决了“主界面显隐导致任务栏消失”的问题 - bool isUninitialized = (_cols.Count > 0 && _cols[0].Bounds.IsEmpty); + // 缓存检查 + if (_isCacheValid && rectPhys == _lastWindowRect) return _cachedResult; - // 2. 常规检查:如果内容长度/结构变了(签名变了),也要重算 - string currentSig = _layout.GetLayoutSignature(_cols) + "_" + _bizHelper.Height; - bool isContentChanged = (currentSig != _lastLayoutSignature); + Rectangle finalRect = rectPhys; + + // ========================================================================= + // [SIMPLIFIED FIX] 极简稳定方案:基于 WorkingArea 和 强制高度修正 (DPI适配版) + // ========================================================================= + try + { + Screen screen = null; + if (!string.IsNullOrEmpty(targetDevice)) + screen = Screen.AllScreens.FirstOrDefault(s => s.DeviceName == targetDevice); + if (screen == null) + screen = Screen.FromHandle(hTaskbar); - if (isUninitialized || isContentChanged) + if (screen != null) { - _layout.Build(_cols, _bizHelper.Height); - Width = _layout.PanelWidth; - Height = _bizHelper.Height; - - _lastLayoutSignature = currentSig; + Rectangle workArea = screen.WorkingArea; + Rectangle screenBounds = screen.Bounds; + int reservedBottom = screenBounds.Bottom - workArea.Bottom; + + // 场景 A: 锚定模式 + if (reservedBottom > 2) + { + finalRect = new Rectangle(rectPhys.Left, workArea.Bottom, rectPhys.Width, reservedBottom); + } + // 场景 B: 悬浮模式 + else + { + if (_isWin11) + { + // [Fix] 增加对垂直任务栏的判断,防止误判 + // StartAllBack 等软件可能启用垂直任务栏,此时不应应用底部水平任务栏的高度修正 + bool isVertical = rectPhys.Height > rectPhys.Width; + + if (!isVertical) + { + int dpi = GetTaskbarDpi(); + int baseHeight = _cfg?.Win11TaskbarHeight ?? 48; + int standardHeight = (int)Math.Round((double)baseHeight * dpi / 96.0); + + if (rectPhys.Height > (standardHeight * 0.8)) + { + finalRect = new Rectangle( + rectPhys.Left, + rectPhys.Bottom - standardHeight, + rectPhys.Width, + standardHeight); + } + } + } + } } } - - _bizHelper.UpdatePlacement(Width); - - _tooltipHelper.UpdateContent(); + catch { } + + _lastWindowRect = rectPhys; + _cachedResult = finalRect; + _isCacheValid = true; - Invalidate(); + return _cachedResult; } - protected override void OnPaintBackground(PaintEventArgs e) + public bool GetWindowRectWrapper(IntPtr hWnd, out Rectangle rect) { - e.Graphics.Clear(_bizHelper.TransparentKey); + if (GetWindowRect(hWnd, out RECT r)) + { + rect = Rectangle.FromLTRB(r.left, r.top, r.right, r.bottom); + return true; + } + rect = Rectangle.Empty; + return false; } - protected override void OnPaint(PaintEventArgs e) + public static bool IsCenterAligned() { - // ★ 调试验证用:如果消失时出现了一个红块,说明 OnPaint 被调用但 _cols 为空 - // e.Graphics.FillRectangle(Brushes.Red, 0, 0, 20, 20); - - if (_cols == null) return; - var g = e.Graphics; - g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; - g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; - g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; - TaskbarRenderer.Render(g, _cols, _bizHelper.LastIsLightTheme); + if (Environment.OSVersion.Version.Major < 10 || Environment.OSVersion.Version.Build < 22000) + return false; + try + { + using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"); + return ((int)(key?.GetValue("TaskbarAl", 1) ?? 1)) == 1; + } + catch { return false; } } - protected override CreateParams CreateParams + public static int GetTaskbarDpi() { - get + IntPtr taskbar = FindWindow("Shell_TrayWnd", null); + if (taskbar != IntPtr.Zero) { - CreateParams cp = base.CreateParams; - cp.ExStyle |= WS_EX_LAYERED | WS_EX_TOOLWINDOW; - cp.ExStyle |= 0x08000000; // WS_EX_NOACTIVATE (防止点击激活窗口,避免抢占焦点) - if (_cfg != null && _cfg.TaskbarClickThrough) - { - cp.ExStyle |= WS_EX_TRANSPARENT; - } - return cp; + try { return (int)GetDpiForWindow(taskbar); } catch { } } + return 96; + } + + public static int GetWidgetsWidth() + { + int dpi = GetTaskbarDpi(); + if (_isWin11) + { + string local = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string pkg = Path.Combine(local, "Packages"); + bool hasWidgetPkg = false; + try { hasWidgetPkg = Directory.GetDirectories(pkg, "MicrosoftWindows.Client.WebExperience*").Any(); } catch {} + + if (!hasWidgetPkg) return 0; + + using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"); + if (key == null) return 0; + + object? val = key.GetValue("TaskbarDa"); + if (val is int i && i != 0) return 150 * dpi / 96; + } + return 0; + } + + public static void ActivateWindow(IntPtr handle) => SetForegroundWindow(handle); + + public static bool IsWindow(IntPtr hWnd) => NativeMethods.IsWindow(hWnd); + + public static void ApplyChildWindowStyle(IntPtr hWnd) + { + int style = GetWindowLong(hWnd, GWL_STYLE); + style &= (int)~0x80000000; + style |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; + SetWindowLong(hWnd, GWL_STYLE, style); } } } From 3a24b034aedcf612b505a59faf48b35cefa97373 Mon Sep 17 00:00:00 2001 From: Xiangxin <1934109821@qq.com> Date: Fri, 13 Feb 2026 15:54:58 +0800 Subject: [PATCH 2/5] Update TaskbarForm.cs --- src/UI/TaskbarForm.cs | 490 +++++++++++++++++------------------------- 1 file changed, 195 insertions(+), 295 deletions(-) diff --git a/src/UI/TaskbarForm.cs b/src/UI/TaskbarForm.cs index 924bc1b..e545ff5 100644 --- a/src/UI/TaskbarForm.cs +++ b/src/UI/TaskbarForm.cs @@ -1,366 +1,266 @@ -using Microsoft.Win32; +using LiteMonitor.src.Core; +using LiteMonitor.src.UI.Helpers; using System; +using System.Collections.Generic; using System.Drawing; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; using System.Windows.Forms; -using LiteMonitor.src.Core; using static LiteMonitor.src.UI.Helpers.NativeMethods; -namespace LiteMonitor.src.UI.Helpers +namespace LiteMonitor { - /// - /// 任务栏集成策略接口 - /// 定义了不同系统版本下任务栏挂载和布局的统一行为 - /// - public interface ITaskbarStrategy - { - /// - /// 是否准备就绪 - /// - bool IsReady { get; } - - /// - /// 挂载到任务栏 - /// - void Attach(IntPtr taskbarHandle); - - /// - /// 设置位置和大小 - /// - void SetPosition(IntPtr taskbarHandle, int left, int top, int w, int h, int manualOffset, bool alignLeft); - - /// - /// 恢复任务栏原始布局(如果做过修改) - /// - void Restore(); - - /// - /// 获取期望的父窗口句柄(用于检测是否脱离) - /// - IntPtr GetExpectedParent(IntPtr taskbarHandle); - - /// - /// 是否拥有内部布局逻辑(如 Win10 挤占模式) - /// - bool HasInternalLayout { get; } - } - - internal static class NativeMethods + public class TaskbarForm : Form { - public const int GWL_STYLE = -16; - public const int GWL_EXSTYLE = -20; - public const int WS_CHILD = 0x40000000; - public const int WS_VISIBLE = 0x10000000; - public const int WS_CLIPSIBLINGS = 0x04000000; - public const int WS_EX_LAYERED = 0x80000; - public const int WS_EX_TOOLWINDOW = 0x00000080; - public const uint LWA_COLORKEY = 0x00000001; - public const uint SWP_NOZORDER = 0x0004; - public const uint SWP_NOACTIVATE = 0x0010; - public const int WS_EX_TRANSPARENT = 0x00000020; - public const int DWMWA_EXTENDED_FRAME_BOUNDS = 9; - - [StructLayout(LayoutKind.Sequential)] - public struct POINT { public int X, Y; } - - [StructLayout(LayoutKind.Sequential)] - public struct RECT { public int left, top, right, bottom; } - - [DllImport("user32.dll")] public static extern IntPtr FindWindow(string cls, string? name); - [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string cls, string? name); - [DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr hWnd, int idx); - [DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr hWnd, int idx, int value); - [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect); - [DllImport("user32.dll")] public static extern bool ScreenToClient(IntPtr hWnd, ref POINT pt); - [DllImport("user32.dll")] public static extern IntPtr SetParent(IntPtr child, IntPtr parent); - [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint flags); - [DllImport("user32.dll")] public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); - [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] public static extern IntPtr GetParent(IntPtr hWnd); - [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); - [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindow(IntPtr hWnd); - [DllImport("dwmapi.dll")] public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); - [DllImport("user32.dll")] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); - [DllImport("user32.dll")] public static extern uint GetDpiForWindow(IntPtr hWnd); - } - - /// - /// 任务栏窗口底层助手 (Windows Helper) - Facade - /// 职责:作为统一入口,根据系统版本委托给具体的策略 (Strategy) 处理挂载和布局 - /// - public class TaskbarWinHelper - { - private readonly Form _form; private readonly Settings _cfg; - private readonly ITaskbarStrategy _strategy; + private readonly UIController _ui; + private readonly MainForm _mainForm; + private readonly System.Windows.Forms.Timer _timer = new(); - // ★★★ 性能优化缓存 ★★★ - private Rectangle _lastWindowRect = Rectangle.Empty; - private Rectangle _cachedResult = Rectangle.Empty; - private bool _isCacheValid = false; - - // [Optimization] 静态缓存系统版本检测结果 - private static readonly bool _isWin11 = Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 22000; + // ★★★ 双助手架构 ★★★ + private readonly TaskbarWinHelper _winHelper; + private readonly TaskbarBizHelper _bizHelper; + + private HorizontalLayout _layout; + private List? _cols; + private ContextMenuStrip? _currentMenu; + private DateTime _lastFindHandleTime = DateTime.MinValue; + private string _lastLayoutSignature = ""; + private readonly TaskbarTooltipHelper _tooltipHelper; + + // 公开属性 + public string TargetDevice { get; private set; } = ""; - public bool UsesInternalLayout => _strategy.HasInternalLayout; + // 判断菜单是否打开 + public bool IsMenuOpen => _currentMenu != null && !_currentMenu.IsDisposed && _currentMenu.Visible; - public TaskbarWinHelper(Form form, Settings cfg) + private const int WM_RBUTTONDOWN = 0x0204; + private const int WM_RBUTTONUP = 0x0205; + private const int WM_LBUTTONDBLCLK = 0x0203; + private bool _isWin11; + + public TaskbarForm(Settings cfg, UIController ui, MainForm mainForm) { - _form = form; _cfg = cfg; - // 策略工厂模式:根据系统版本选择合适的集成策略 - if (_isWin11) - { - _strategy = new TaskbarStrategyWin11(form); - } - else - { - _strategy = new TaskbarStrategyWin10(form); - } - } + _ui = ui; + _mainForm = mainForm; + TargetDevice = _cfg.TaskbarMonitorDevice; - // ================================================================= - // 样式与图层 - // ================================================================= - public void ApplyLayeredStyle(Color transparentKey, bool clickThrough) - { - _form.BackColor = transparentKey; - - if (_form.IsHandleCreated) - { - uint colorKey = (uint)(transparentKey.R | (transparentKey.G << 8) | (transparentKey.B << 16)); - SetLayeredWindowAttributes(_form.Handle, colorKey, 0, LWA_COLORKEY); - } + _isWin11 = Environment.OSVersion.Version >= new Version(10, 0, 22000); + + // 初始化组件 + _winHelper = new TaskbarWinHelper(this, _cfg); + _bizHelper = new TaskbarBizHelper(this, _cfg, _winHelper); + + // 窗体属性 + FormBorderStyle = FormBorderStyle.None; + ShowInTaskbar = false; + ControlBox = false; + TopMost = false; + DoubleBuffered = true; + + ReloadLayout(); - int exStyle = GetWindowLong(_form.Handle, GWL_EXSTYLE); - if (clickThrough) exStyle |= WS_EX_TRANSPARENT; - else exStyle &= ~WS_EX_TRANSPARENT; - SetWindowLong(_form.Handle, GWL_EXSTYLE, exStyle); + _bizHelper.CheckTheme(true); + _bizHelper.FindHandles(); - _form.Invalidate(); + _bizHelper.AttachToTaskbar(); + _winHelper.ApplyLayeredStyle(_bizHelper.TransparentKey, _cfg.TaskbarClickThrough); + + _timer.Interval = Math.Max(_cfg.RefreshMs, 60); + _timer.Tick += (_, __) => Tick(); + _timer.Start(); + + // 鼠标悬浮提示初始化 + _tooltipHelper = new TaskbarTooltipHelper(this, _cfg, _ui); + + Tick(); } - public bool IsSystemLightTheme() + public void ReloadLayout() { - try + _winHelper?.InvalidateCache(); // 强制清除位置缓存,确保高度设置立即生效 + _layout = new HorizontalLayout(ThemeManager.Current, 300, LayoutMode.Taskbar, _cfg); + _lastLayoutSignature = ""; // 重置签名,强制重算 + _winHelper.ApplyLayeredStyle(_bizHelper.TransparentKey, _cfg.TaskbarClickThrough); + _bizHelper.CheckTheme(true); + + // 更新悬浮窗模式 (支持热切换) + _tooltipHelper?.ReloadMode(); + + // 注意:这里仍然可能因为 _cols 为空而暂时不 Build, + // 但随后的 Tick 会在获取到新数据后自动 Build + if (_cols != null && _cols.Count > 0) { - using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"); - if (key != null) - { - object? val = key.GetValue("SystemUsesLightTheme"); - if (val is int i) return i == 1; - } + _layout.Build(_cols, _bizHelper.Height); + Width = _layout.PanelWidth; + _bizHelper.UpdatePlacement(Width); } - catch { } - return false; + Invalidate(); } - // ================================================================= - // 挂载逻辑 (委托给策略) - // ================================================================= - public void AttachToTaskbar(IntPtr taskbarHandle) + protected override void Dispose(bool disposing) { - _strategy.Attach(taskbarHandle); + if (disposing) + { + _winHelper?.RestoreTaskbar(); + _timer.Stop(); + _timer.Dispose(); + _currentMenu?.Dispose(); + _tooltipHelper?.Dispose(); + } + base.Dispose(disposing); } - public void SetPosition(IntPtr taskbarHandle, int left, int top, int w, int h, int manualOffset = 0, bool alignLeft = true) + protected override void WndProc(ref Message m) { - // 检查父窗口是否正确,防止脱离 - IntPtr currentParent = GetParent(_form.Handle); - IntPtr expectedParent = _strategy.GetExpectedParent(taskbarHandle); - - if (currentParent != expectedParent) + // [Fix] 兼容性修复:在 Win11 25H2 + StartAllBack 环境下, + // 右键事件会穿透到原生任务栏。 + // 因此不再区分系统版本,统一拦截右键按下和抬起消息。 + if (m.Msg == WM_RBUTTONDOWN) { - AttachToTaskbar(taskbarHandle); + return; // 吞掉按下事件,防止穿透 } - _strategy.SetPosition(taskbarHandle, left, top, w, h, manualOffset, alignLeft); - } + if (m.Msg == WM_RBUTTONUP) + { + this.BeginInvoke(new Action(ShowContextMenu)); + return; + } - public void RestoreTaskbar() - { - _strategy.Restore(); - } + // [Fix] 强制拦截双击事件 + // 当悬浮窗(Tooltip)显示时,WinForms 的标准双击事件可能因为焦点/激活状态的微妙变化而失效。 + // 这里直接在消息层处理 WM_LBUTTONDBLCLK,确保双击动作始终能被触发。 + if (m.Msg == WM_LBUTTONDBLCLK) + { + _bizHelper.HandleDoubleClick(_mainForm, _ui); + return; + } - public void InvalidateCache() - { - _isCacheValid = false; + base.WndProc(ref m); } - // ================================================================= - // 句柄与信息获取 (通用逻辑) - // ================================================================= - public (IntPtr hTaskbar, IntPtr hTray) FindHandles(string targetDevice) + private void ShowContextMenu() { - Screen target = Screen.PrimaryScreen; - if (!string.IsNullOrEmpty(targetDevice)) + if (_currentMenu != null) { - target = Screen.AllScreens.FirstOrDefault(s => s.DeviceName == targetDevice) ?? Screen.PrimaryScreen; + _currentMenu.Dispose(); + _currentMenu = null; } - if (target.Primary) - { - IntPtr hTaskbar = FindWindow("Shell_TrayWnd", null); - IntPtr hTray = FindWindowEx(hTaskbar, IntPtr.Zero, "TrayNotifyWnd", null); - return (hTaskbar, hTray); - } - else + _currentMenu = MenuManager.Build(_mainForm, _cfg, _ui, "Taskbar"); + + TaskbarWinHelper.ActivateWindow(this.Handle); + _currentMenu.Show(Cursor.Position); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + if (e.Button == MouseButtons.Right) { - IntPtr hTaskbar = FindSecondaryTaskbar(target); - return (hTaskbar, IntPtr.Zero); + ShowContextMenu(); } } - private IntPtr FindSecondaryTaskbar(Screen screen) + protected override void OnMouseDoubleClick(MouseEventArgs e) { - IntPtr hWnd = IntPtr.Zero; - while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "Shell_SecondaryTrayWnd", null)) != IntPtr.Zero) + base.OnMouseDoubleClick(e); + if (e.Button == MouseButtons.Left) { - GetWindowRect(hWnd, out RECT rect); - Rectangle r = Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); - if (screen.Bounds.Contains(r.Location) || screen.Bounds.IntersectsWith(r)) - return hWnd; + _bizHelper.HandleDoubleClick(_mainForm, _ui); } - return FindWindow("Shell_TrayWnd", null); } - public Rectangle GetTaskbarRect(IntPtr hTaskbar, string targetDevice) + private void Tick() { - if (hTaskbar == IntPtr.Zero) return Rectangle.Empty; - - // 1. 获取物理矩形 - if (!GetWindowRect(hTaskbar, out RECT r)) return Rectangle.Empty; - var rectPhys = Rectangle.FromLTRB(r.left, r.top, r.right, r.bottom); + // [Fix] 周期性检查句柄,防止 Explorer 重启后句柄失效 + // 优化:仅在重试期或句柄无效时调用 FindHandles,且限制调用频率 + bool isHandleInvalid = !_bizHelper.IsTaskbarValid(); + + // 如果处于重试期,或者句柄无效且距离上次查找超过2秒(防止无Explorer时高频空转) + if (isHandleInvalid && (DateTime.Now - _lastFindHandleTime).TotalSeconds > 2) + { + _bizHelper.FindHandles(); + _lastFindHandleTime = DateTime.Now; + } - // 缓存检查 - if (_isCacheValid && rectPhys == _lastWindowRect) return _cachedResult; + if (Math.Abs(Environment.TickCount) % 5000 < _cfg.RefreshMs) _bizHelper.CheckTheme(); - Rectangle finalRect = rectPhys; + // [Fix Part 1] 防空数据保护 + // 使用临时变量接收,先判断数据有效性,再赋值给成员变量 _cols + // 防止在 UI 重建期间(RebuildLayout)获取到空列表导致任务栏闪烁或清空 + var nextCols = _ui.GetTaskbarColumns(); + if (nextCols == null || nextCols.Count == 0) return; + + _cols = nextCols; // 确认有效后再更新引用 - // ========================================================================= - // [SIMPLIFIED FIX] 极简稳定方案:基于 WorkingArea 和 强制高度修正 (DPI适配版) - // ========================================================================= - try + _bizHelper.UpdateTaskbarRect(); + + // [Fix Part 2] 布局变更检测 + if (_bizHelper.IsVertical()) { - Screen screen = null; - if (!string.IsNullOrEmpty(targetDevice)) - screen = Screen.AllScreens.FirstOrDefault(s => s.DeviceName == targetDevice); - if (screen == null) - screen = Screen.FromHandle(hTaskbar); + // 垂直模式逻辑简单且无测量开销,直接重算即可 + _bizHelper.BuildVerticalLayout(_cols); + _lastLayoutSignature = "vertical"; + } + else + { + // [优化] 智能判断更新条件 + // 1. 必须检查:如果列表是新生成的(坐标还没算过,Bounds为空),必须重算! + // 这解决了“主界面显隐导致任务栏消失”的问题 + bool isUninitialized = (_cols.Count > 0 && _cols[0].Bounds.IsEmpty); + + // 2. 常规检查:如果内容长度/结构变了(签名变了),也要重算 + string currentSig = _layout.GetLayoutSignature(_cols) + "_" + _bizHelper.Height; + bool isContentChanged = (currentSig != _lastLayoutSignature); - if (screen != null) + if (isUninitialized || isContentChanged) { - Rectangle workArea = screen.WorkingArea; - Rectangle screenBounds = screen.Bounds; - int reservedBottom = screenBounds.Bottom - workArea.Bottom; - - // 场景 A: 锚定模式 - if (reservedBottom > 2) - { - finalRect = new Rectangle(rectPhys.Left, workArea.Bottom, rectPhys.Width, reservedBottom); - } - // 场景 B: 悬浮模式 - else - { - if (_isWin11) - { - // [Fix] 增加对垂直任务栏的判断,防止误判 - // StartAllBack 等软件可能启用垂直任务栏,此时不应应用底部水平任务栏的高度修正 - bool isVertical = rectPhys.Height > rectPhys.Width; - - if (!isVertical) - { - int dpi = GetTaskbarDpi(); - int baseHeight = _cfg?.Win11TaskbarHeight ?? 48; - int standardHeight = (int)Math.Round((double)baseHeight * dpi / 96.0); - - if (rectPhys.Height > (standardHeight * 0.8)) - { - finalRect = new Rectangle( - rectPhys.Left, - rectPhys.Bottom - standardHeight, - rectPhys.Width, - standardHeight); - } - } - } - } + _layout.Build(_cols, _bizHelper.Height); + Width = _layout.PanelWidth; + Height = _bizHelper.Height; + + _lastLayoutSignature = currentSig; } } - catch { } - - _lastWindowRect = rectPhys; - _cachedResult = finalRect; - _isCacheValid = true; + + _bizHelper.UpdatePlacement(Width); + + _tooltipHelper.UpdateContent(); - return _cachedResult; + Invalidate(); } - public bool GetWindowRectWrapper(IntPtr hWnd, out Rectangle rect) + protected override void OnPaintBackground(PaintEventArgs e) { - if (GetWindowRect(hWnd, out RECT r)) - { - rect = Rectangle.FromLTRB(r.left, r.top, r.right, r.bottom); - return true; - } - rect = Rectangle.Empty; - return false; + e.Graphics.Clear(_bizHelper.TransparentKey); } - public static bool IsCenterAligned() + protected override void OnPaint(PaintEventArgs e) { - if (Environment.OSVersion.Version.Major < 10 || Environment.OSVersion.Version.Build < 22000) - return false; - try - { - using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"); - return ((int)(key?.GetValue("TaskbarAl", 1) ?? 1)) == 1; - } - catch { return false; } + // ★ 调试验证用:如果消失时出现了一个红块,说明 OnPaint 被调用但 _cols 为空 + // e.Graphics.FillRectangle(Brushes.Red, 0, 0, 20, 20); + + if (_cols == null) return; + var g = e.Graphics; + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; + g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + TaskbarRenderer.Render(g, _cols, _bizHelper.LastIsLightTheme); } - public static int GetTaskbarDpi() + protected override CreateParams CreateParams { - IntPtr taskbar = FindWindow("Shell_TrayWnd", null); - if (taskbar != IntPtr.Zero) + get { - try { return (int)GetDpiForWindow(taskbar); } catch { } - } - return 96; - } - - public static int GetWidgetsWidth() - { - int dpi = GetTaskbarDpi(); - if (_isWin11) - { - string local = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - string pkg = Path.Combine(local, "Packages"); - bool hasWidgetPkg = false; - try { hasWidgetPkg = Directory.GetDirectories(pkg, "MicrosoftWindows.Client.WebExperience*").Any(); } catch {} - - if (!hasWidgetPkg) return 0; - - using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"); - if (key == null) return 0; - - object? val = key.GetValue("TaskbarDa"); - if (val is int i && i != 0) return 150 * dpi / 96; + CreateParams cp = base.CreateParams; + cp.ExStyle |= WS_EX_LAYERED | WS_EX_TOOLWINDOW; + cp.ExStyle |= 0x08000000; // WS_EX_NOACTIVATE (防止点击激活窗口,避免抢占焦点) + if (_cfg != null && _cfg.TaskbarClickThrough) + { + cp.ExStyle |= WS_EX_TRANSPARENT; + } + return cp; } - return 0; - } - - public static void ActivateWindow(IntPtr handle) => SetForegroundWindow(handle); - - public static bool IsWindow(IntPtr hWnd) => NativeMethods.IsWindow(hWnd); - - public static void ApplyChildWindowStyle(IntPtr hWnd) - { - int style = GetWindowLong(hWnd, GWL_STYLE); - style &= (int)~0x80000000; - style |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; - SetWindowLong(hWnd, GWL_STYLE, style); } } } From af30343cd92bb11cd376ac6b720bfae862f9e744 Mon Sep 17 00:00:00 2001 From: Xiangxin <1934109821@qq.com> Date: Fri, 13 Feb 2026 15:55:37 +0800 Subject: [PATCH 3/5] Add Win11 taskbar height adjustment setting --- src/UI/Settings/TaskbarPage.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/UI/Settings/TaskbarPage.cs b/src/UI/Settings/TaskbarPage.cs index 60010df..907b4e0 100644 --- a/src/UI/Settings/TaskbarPage.cs +++ b/src/UI/Settings/TaskbarPage.cs @@ -160,6 +160,14 @@ private void CreateGeneralGroup() () => Config?.TaskbarManualOffset ?? 0, v => { if(Config!=null) Config.TaskbarManualOffset = v; }); + // [新增] Win11 高度修正 + if (Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Build >= 22000) + { + group.AddInt(this, "Win11 任务栏高度", "px", + () => Config?.Win11TaskbarHeight ?? 48, + v => { if(Config!=null) Config.Win11TaskbarHeight = v; }); + } + group.AddHint(LanguageManager.T("Menu.TaskbarAlignTip")); AddGroupToPage(group); } @@ -290,4 +298,4 @@ private void AddGroupToPage(LiteSettingsGroup group) _container.Controls.SetChildIndex(wrapper, 0); } } -} \ No newline at end of file +} From 16db1f19406c0f67b5756a7ce83ace787e7c601b Mon Sep 17 00:00:00 2001 From: Xiangxin <1934109821@qq.com> Date: Fri, 13 Feb 2026 15:56:17 +0800 Subject: [PATCH 4/5] Refactor TaskbarWinHelper to include Settings and cache invalidation Added a Settings parameter to the TaskbarWinHelper constructor and introduced a method to invalidate the cache. --- src/UI/Helpers/TaskbarWinHelper.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/UI/Helpers/TaskbarWinHelper.cs b/src/UI/Helpers/TaskbarWinHelper.cs index 371fcf5..924bc1b 100644 --- a/src/UI/Helpers/TaskbarWinHelper.cs +++ b/src/UI/Helpers/TaskbarWinHelper.cs @@ -92,6 +92,7 @@ public struct RECT { public int left, top, right, bottom; } public class TaskbarWinHelper { private readonly Form _form; + private readonly Settings _cfg; private readonly ITaskbarStrategy _strategy; // ★★★ 性能优化缓存 ★★★ @@ -104,9 +105,10 @@ public class TaskbarWinHelper public bool UsesInternalLayout => _strategy.HasInternalLayout; - public TaskbarWinHelper(Form form) + public TaskbarWinHelper(Form form, Settings cfg) { _form = form; + _cfg = cfg; // 策略工厂模式:根据系统版本选择合适的集成策略 if (_isWin11) { @@ -181,6 +183,11 @@ public void RestoreTaskbar() _strategy.Restore(); } + public void InvalidateCache() + { + _isCacheValid = false; + } + // ================================================================= // 句柄与信息获取 (通用逻辑) // ================================================================= @@ -265,7 +272,8 @@ public Rectangle GetTaskbarRect(IntPtr hTaskbar, string targetDevice) if (!isVertical) { int dpi = GetTaskbarDpi(); - int standardHeight = (int)Math.Round(48.0 * dpi / 96.0); + int baseHeight = _cfg?.Win11TaskbarHeight ?? 48; + int standardHeight = (int)Math.Round((double)baseHeight * dpi / 96.0); if (rectPhys.Height > (standardHeight * 0.8)) { From 135e62e4418fba407696211e09e403692791fb9d Mon Sep 17 00:00:00 2001 From: Xiangxin <1934109821@qq.com> Date: Fri, 13 Feb 2026 15:56:51 +0800 Subject: [PATCH 5/5] Add Win11 taskbar height property to settings --- src/Core/Settings.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Core/Settings.cs b/src/Core/Settings.cs index a21252d..103a1e0 100644 --- a/src/Core/Settings.cs +++ b/src/Core/Settings.cs @@ -91,6 +91,9 @@ public class Settings public bool TaskbarSingleLine { get; set; } = false;// 单行显示 public bool TaskbarHoverShowAll { get; set; } = true; // [新增] 悬浮显示所有监控项 public int TaskbarManualOffset { get; set; } = 0;// 手动偏移量 (像素) + + // ★★★ [新增] Win11 任务栏修正高度 ★★★ + public int Win11TaskbarHeight { get; set; } = 48; // 默认 48px (96dpi) // ====== 任务栏:高级自定义外观 ====== public bool TaskbarCustomStyle { get; set; } = false; // 总开关