From bf2b8fec35b9678fa5ac8372494556d231ec0ad2 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 15 Nov 2025 21:14:08 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E8=AE=A1=E6=97=B6=E5=99=A8UI=E4=B8=8E?= =?UTF-8?q?=E7=82=B9=E5=90=8DUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改进视觉反馈和按钮逻辑及窗口置顶 --- Ink Canvas/Windows/CountdownTimerWindow.xaml | 1 + .../Windows/FullscreenTimerWindow.xaml.cs | 60 ++++++++++++++++ Ink Canvas/Windows/MinimizedTimerWindow.xaml | 1 + .../Windows/MinimizedTimerWindow.xaml.cs | 60 ++++++++++++++++ Ink Canvas/Windows/NewStyleRollCallWindow.cs | 71 +++++++++++++++++++ Ink Canvas/Windows/NewStyleTimerWindow.cs | 60 ++++++++++++++-- Ink Canvas/Windows/NewStyleTimerWindow.xaml | 10 ++- 7 files changed, 254 insertions(+), 9 deletions(-) diff --git a/Ink Canvas/Windows/CountdownTimerWindow.xaml b/Ink Canvas/Windows/CountdownTimerWindow.xaml index 093f416b..63008641 100644 --- a/Ink Canvas/Windows/CountdownTimerWindow.xaml +++ b/Ink Canvas/Windows/CountdownTimerWindow.xaml @@ -8,6 +8,7 @@ xmlns:processbars="clr-namespace:Ink_Canvas.ProcessBars" ui:ThemeManager.RequestedTheme="Light" Topmost="True" Background="Transparent" mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True" + ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing" WindowStartupLocation="CenterScreen" Title="Ink Canvas 画板 - 计时器" Height="700" Width="1100"> diff --git a/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs b/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs index cee0ce8a..07df43e9 100644 --- a/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs +++ b/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs @@ -1,7 +1,9 @@ using System; +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.Shapes; using System.Timers; @@ -33,7 +35,65 @@ namespace Ink_Canvas updateTimer.Start(); parentWindow.TimerCompleted += ParentWindow_TimerCompleted; + + // 确保窗口置顶 + Loaded += FullscreenTimerWindow_Loaded; } + + private void FullscreenTimerWindow_Loaded(object sender, RoutedEventArgs e) + { + // 使用延迟确保窗口完全加载后再应用置顶 + Dispatcher.BeginInvoke(new Action(() => + { + ApplyTopmost(); + }), System.Windows.Threading.DispatcherPriority.Loaded); + } + + #region Win32 API 声明和置顶管理 + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + private const int GWL_EXSTYLE = -20; + private const int WS_EX_TOPMOST = 0x00000008; + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint SWP_NOACTIVATE = 0x0010; + private const uint SWP_SHOWWINDOW = 0x0040; + + /// + /// 应用全屏窗口置顶 + /// + private void ApplyTopmost() + { + try + { + var hwnd = new WindowInteropHelper(this).Handle; + if (hwnd == IntPtr.Zero) return; + + // 设置WPF的Topmost属性 + Topmost = true; + + // 使用Win32 API强制置顶 + int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); + + // 使用SetWindowPos确保窗口在最顶层 + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"应用全屏窗口置顶失败: {ex.Message}"); + } + } + #endregion private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) { diff --git a/Ink Canvas/Windows/MinimizedTimerWindow.xaml b/Ink Canvas/Windows/MinimizedTimerWindow.xaml index 08b6f91c..ecd6e333 100644 --- a/Ink Canvas/Windows/MinimizedTimerWindow.xaml +++ b/Ink Canvas/Windows/MinimizedTimerWindow.xaml @@ -7,6 +7,7 @@ xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" Topmost="True" Background="Transparent" mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True" + ResizeMode="NoResize" WindowStartupLocation="Manual" Title="计时器" Height="200" Width="600" MouseLeftButtonDown="Window_MouseLeftButtonDown" MouseLeftButtonUp="Window_MouseLeftButtonUp" MouseEnter="Window_MouseEnter" MouseLeave="Window_MouseLeave" MouseMove="Window_MouseMove"> diff --git a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs index 10b8a3aa..c8ad4457 100644 --- a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs +++ b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs @@ -1,7 +1,9 @@ using System; +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.Shapes; using System.Windows.Media.Animation; @@ -40,7 +42,65 @@ namespace Ink_Canvas // 应用主题 ApplyTheme(); + + // 确保窗口置顶 + Loaded += MinimizedTimerWindow_Loaded; } + + private void MinimizedTimerWindow_Loaded(object sender, RoutedEventArgs e) + { + // 使用延迟确保窗口完全加载后再应用置顶 + Dispatcher.BeginInvoke(new Action(() => + { + ApplyTopmost(); + }), System.Windows.Threading.DispatcherPriority.Loaded); + } + + #region Win32 API 声明和置顶管理 + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + private const int GWL_EXSTYLE = -20; + private const int WS_EX_TOPMOST = 0x00000008; + private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint SWP_NOACTIVATE = 0x0010; + private const uint SWP_SHOWWINDOW = 0x0040; + + /// + /// 应用最小化窗口置顶 + /// + private void ApplyTopmost() + { + try + { + var hwnd = new WindowInteropHelper(this).Handle; + if (hwnd == IntPtr.Zero) return; + + // 设置WPF的Topmost属性 + Topmost = true; + + // 使用Win32 API强制置顶 + int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); + + // 使用SetWindowPos确保窗口在最顶层 + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"应用最小化窗口置顶失败: {ex.Message}"); + } + } + #endregion /// /// 根据屏幕分辨率和 DPI 缩放窗口大小(保持原始尺寸,使用Transform缩放) diff --git a/Ink Canvas/Windows/NewStyleRollCallWindow.cs b/Ink Canvas/Windows/NewStyleRollCallWindow.cs index ee70c659..34c60b73 100644 --- a/Ink Canvas/Windows/NewStyleRollCallWindow.cs +++ b/Ink Canvas/Windows/NewStyleRollCallWindow.cs @@ -67,6 +67,25 @@ namespace Ink_Canvas // 初始化点名相关变量 InitializeRollCallData(); + if (isSingleDrawMode) + { + if (ControlOptionsGrid != null) + { + ControlOptionsGrid.Opacity = 0.4; + ControlOptionsGrid.IsHitTestVisible = false; + } + if (StartRollCallBtn != null) + { + StartRollCallBtn.Opacity = 0.4; + StartRollCallBtn.IsEnabled = false; + } + if (ResetBtn != null) + { + ResetBtn.Opacity = 0.4; + ResetBtn.IsEnabled = false; + } + } + // 单次抽模式:自动开始抽选 if (isSingleDrawMode) { @@ -102,6 +121,27 @@ namespace Ink_Canvas // 初始化点名相关变量 InitializeRollCallData(); + // 单次抽模式:禁用控制面板,阻止用户点击按钮 + if (isSingleDrawMode) + { + if (ControlOptionsGrid != null) + { + ControlOptionsGrid.Opacity = 0.4; + ControlOptionsGrid.IsHitTestVisible = false; + } + // 禁用开始点名和重置按钮 + if (StartRollCallBtn != null) + { + StartRollCallBtn.Opacity = 0.4; + StartRollCallBtn.IsEnabled = false; + } + if (ResetBtn != null) + { + ResetBtn.Opacity = 0.4; + ResetBtn.IsEnabled = false; + } + } + // 单次抽模式:自动开始抽选 if (isSingleDrawMode) { @@ -1671,6 +1711,21 @@ namespace Ink_Canvas System.Threading.Thread.Sleep(autoCloseWaitTime); Application.Current.Dispatcher.Invoke(() => { + if (ControlOptionsGrid != null) + { + ControlOptionsGrid.Opacity = 1; + ControlOptionsGrid.IsHitTestVisible = true; + } + if (StartRollCallBtn != null) + { + StartRollCallBtn.Opacity = 1; + StartRollCallBtn.IsEnabled = true; + } + if (ResetBtn != null) + { + ResetBtn.Opacity = 1; + ResetBtn.IsEnabled = true; + } Close(); }); }).Start(); @@ -1743,6 +1798,22 @@ namespace Ink_Canvas System.Threading.Thread.Sleep(autoCloseWaitTime); Application.Current.Dispatcher.Invoke(() => { + if (ControlOptionsGrid != null) + { + ControlOptionsGrid.Opacity = 1; + ControlOptionsGrid.IsHitTestVisible = true; + } + // 恢复开始点名和重置按钮 + if (StartRollCallBtn != null) + { + StartRollCallBtn.Opacity = 1; + StartRollCallBtn.IsEnabled = true; + } + if (ResetBtn != null) + { + ResetBtn.Opacity = 1; + ResetBtn.IsEnabled = true; + } Close(); }); }).Start(); diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.cs b/Ink Canvas/Windows/NewStyleTimerWindow.cs index 34c7e928..ff837f38 100644 --- a/Ink Canvas/Windows/NewStyleTimerWindow.cs +++ b/Ink Canvas/Windows/NewStyleTimerWindow.cs @@ -52,6 +52,26 @@ namespace Ink_Canvas // 添加窗口加载事件处理,确保置顶 Loaded += TimerWindow_Loaded; + + // 暂停主窗口的置顶维护 + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null) + { + mainWindow.PauseTopmostMaintenance(); + } + + // 窗口关闭时恢复主窗口的置顶维护 + Closing += TimerWindow_Closing; + } + + private void TimerWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + // 恢复主窗口的置顶维护 + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null) + { + mainWindow.ResumeTopmostMaintenance(); + } } @@ -116,6 +136,12 @@ namespace Ink_Canvas StartPauseIcon.Data = Geometry.Parse(PlayIconData); PlayTimerSound(); + // 禁用全屏按钮 + if (FullscreenBtn != null) + { + FullscreenBtn.IsEnabled = false; + } + TimerCompleted?.Invoke(this, EventArgs.Empty); HandleTimerCompletion(); } @@ -337,7 +363,7 @@ namespace Ink_Canvas minimizedWindow = new MinimizedTimerWindow(this); minimizedWindow.Show(); - // 确保最小化窗口也置顶 + // 确保最小化窗口也置顶(窗口加载时会自动应用) minimizedWindow.Topmost = true; // 隐藏主窗口 @@ -793,6 +819,12 @@ namespace Ink_Canvas // 保存到最近计时记录 SaveRecentTimer(); + + // 启用全屏按钮 + if (FullscreenBtn != null) + { + FullscreenBtn.IsEnabled = true; + } } } @@ -808,6 +840,12 @@ namespace Ink_Canvas isOvertimeMode = false; UpdateDigitDisplays(); StartPauseIcon.Data = Geometry.Parse(PlayIconData); + + // 禁用全屏按钮 + if (FullscreenBtn != null) + { + FullscreenBtn.IsEnabled = false; + } } private void PlayTimerSound() @@ -884,6 +922,13 @@ namespace Ink_Canvas private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { isTimerRunning = false; + + // 恢复主窗口的置顶维护 + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null) + { + mainWindow.ResumeTopmostMaintenance(); + } } private void CloseButton_Click(object sender, RoutedEventArgs e) @@ -1292,6 +1337,10 @@ namespace Ink_Canvas private void Fullscreen_Click(object sender, RoutedEventArgs e) { + if (!isTimerRunning) + { + return; + } ShowFullscreenTimer(); } @@ -1304,7 +1353,7 @@ namespace Ink_Canvas fullscreenWindow = new FullscreenTimerWindow(this); fullscreenWindow.Show(); - // 确保全屏窗口也置顶 + // 确保全屏窗口也置顶(窗口加载时会自动应用) fullscreenWindow.Topmost = true; // 隐藏主窗口 @@ -1384,10 +1433,6 @@ namespace Ink_Canvas var hwnd = new WindowInteropHelper(this).Handle; if (hwnd == IntPtr.Zero) return; - // 强制激活窗口 - Activate(); - Focus(); - // 设置WPF的Topmost属性 Topmost = true; @@ -1397,8 +1442,9 @@ namespace Ink_Canvas SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST); // 2. 使用SetWindowPos确保窗口在最顶层 + // 使用HWND_TOPMOST确保窗口始终在所有其他窗口之上,包括主窗口 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER); + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW); LogHelper.WriteLogToFile("计时器窗口已应用置顶", LogHelper.LogType.Trace); } diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.xaml b/Ink Canvas/Windows/NewStyleTimerWindow.xaml index 06b76a1a..e23e1a8c 100644 --- a/Ink Canvas/Windows/NewStyleTimerWindow.xaml +++ b/Ink Canvas/Windows/NewStyleTimerWindow.xaml @@ -8,6 +8,7 @@ xmlns:controls="clr-namespace:Ink_Canvas.Windows.Controls" Topmost="True" Background="Transparent" mc:Ignorable="d" WindowStyle="None" AllowsTransparency="True" + ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing" WindowStartupLocation="CenterScreen" MouseMove="Window_MouseMove" MouseEnter="Window_MouseEnter" Title="Ink Canvas 画板 - 计时器" Height="450" Width="900"> @@ -714,12 +715,17 @@