diff --git a/App.xaml.cs b/App.xaml.cs
index c9d2066..96b4f8c 100644
--- a/App.xaml.cs
+++ b/App.xaml.cs
@@ -28,6 +28,12 @@ namespace 自救器呼吸器综合检验仪
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
+ if (AppShutdownCoordinator.ShouldSuppressDuringShutdown(e.Exception))
+ {
+ e.Handled = true;
+ return;
+ }
+
LogError(e.Exception, "UI线程异常");
e.Handled = true; // 标记为已处理,避免崩溃(仅用于调试)
}
@@ -49,6 +55,11 @@ namespace 自救器呼吸器综合检验仪
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log");
string logContent = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {type}:\n{ex.ToString()}\n\n";
File.AppendAllText(logPath, logContent);
+ if (AppShutdownCoordinator.IsShutdownInProgress)
+ {
+ return;
+ }
+
MessageBox.Show($"发生错误,详情已记录到 {logPath}", "错误");
}
diff --git a/AppShutdownCoordinator.cs b/AppShutdownCoordinator.cs
new file mode 100644
index 0000000..a55d4d0
--- /dev/null
+++ b/AppShutdownCoordinator.cs
@@ -0,0 +1,65 @@
+using System;
+using System.ComponentModel;
+using System.Threading;
+using System.Windows;
+using System.Windows.Threading;
+
+namespace 自救器呼吸器综合检验仪
+{
+ internal static class AppShutdownCoordinator
+ {
+ private static int _shutdownRequested;
+
+ public static bool IsShutdownInProgress
+ {
+ get
+ {
+ Application application = Application.Current;
+ if (application == null)
+ {
+ return Interlocked.CompareExchange(ref _shutdownRequested, 0, 0) == 1;
+ }
+
+ return Interlocked.CompareExchange(ref _shutdownRequested, 0, 0) == 1
+ || application.Dispatcher.HasShutdownStarted
+ || application.Dispatcher.HasShutdownFinished;
+ }
+ }
+
+ public static void RequestShutdown()
+ {
+ Application application = Application.Current;
+ if (application == null)
+ {
+ return;
+ }
+
+ if (Interlocked.Exchange(ref _shutdownRequested, 1) == 1)
+ {
+ return;
+ }
+
+ application.Dispatcher.BeginInvoke(
+ DispatcherPriority.ApplicationIdle,
+ new Action(() =>
+ {
+ if (application.Dispatcher.HasShutdownStarted || application.Dispatcher.HasShutdownFinished)
+ {
+ return;
+ }
+
+ application.Shutdown();
+ }));
+ }
+
+ public static bool ShouldSuppressDuringShutdown(Exception exception)
+ {
+ if (!IsShutdownInProgress || exception == null)
+ {
+ return false;
+ }
+
+ return exception is InvalidOperationException || exception is Win32Exception;
+ }
+ }
+}
diff --git a/Data/BaseWindow.xaml b/Data/BaseWindow.xaml
index 33dabf5..fc94515 100644
--- a/Data/BaseWindow.xaml
+++ b/Data/BaseWindow.xaml
@@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:自救器呼吸器综合检验仪.Data"
mc:Ignorable="d"
- Title="BaseWindow" Height="450" Width="800">
+ Title="BaseWindow" Height="600" Width="1024" WindowState="Maximized">
diff --git a/DetectionMenuWindow.xaml b/DetectionMenuWindow.xaml
index 50b435e..674999e 100644
--- a/DetectionMenuWindow.xaml
+++ b/DetectionMenuWindow.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:自救器呼吸器综合检验仪"
- Title="检验功能选择" Height="700" Width="1200"
+ Title="检验功能选择" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen"
FontFamily="Segoe UI"
Background="#F5F7FA">
@@ -127,4 +127,4 @@
-
\ No newline at end of file
+
diff --git a/LoadingOverlayWindow.xaml b/LoadingOverlayWindow.xaml
new file mode 100644
index 0000000..2a505bb
--- /dev/null
+++ b/LoadingOverlayWindow.xaml
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LoadingOverlayWindow.xaml.cs b/LoadingOverlayWindow.xaml.cs
new file mode 100644
index 0000000..1632c67
--- /dev/null
+++ b/LoadingOverlayWindow.xaml.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Windows;
+using System.Windows.Media.Animation;
+using System.Windows.Threading;
+
+namespace 自救器呼吸器综合检验仪
+{
+ public partial class LoadingOverlayWindow : Window
+ {
+ private static readonly TimeSpan MinimumDisplayDuration = TimeSpan.FromMilliseconds(700);
+ private static readonly TimeSpan DismissAnimationDuration = TimeSpan.FromMilliseconds(240);
+
+ private readonly DateTime _shownAtUtc;
+ private bool _closeRequested;
+
+ public LoadingOverlayWindow()
+ {
+ InitializeComponent();
+ _shownAtUtc = DateTime.UtcNow;
+ }
+
+ public void AlignTo(Window referenceWindow)
+ {
+ if (referenceWindow == null)
+ {
+ return;
+ }
+
+ WindowState = WindowState.Normal;
+
+ double width = referenceWindow.ActualWidth > 0 ? referenceWindow.ActualWidth : referenceWindow.Width;
+ double height = referenceWindow.ActualHeight > 0 ? referenceWindow.ActualHeight : referenceWindow.Height;
+
+ if (width <= 0 || height <= 0)
+ {
+ Rect restoreBounds = referenceWindow.RestoreBounds;
+ width = restoreBounds.Width > 0 ? restoreBounds.Width : width;
+ height = restoreBounds.Height > 0 ? restoreBounds.Height : height;
+ }
+
+ Left = referenceWindow.Left;
+ Top = referenceWindow.Top;
+ Width = width;
+ Height = height;
+ }
+
+ public void MatchTopmost(Window referenceWindow)
+ {
+ if (referenceWindow == null)
+ {
+ return;
+ }
+
+ Topmost = referenceWindow.Topmost;
+ }
+
+ public void SetMessage(string title, string subtitle)
+ {
+ if (!string.IsNullOrWhiteSpace(title))
+ {
+ TitleText.Text = title;
+ }
+
+ if (!string.IsNullOrWhiteSpace(subtitle))
+ {
+ SubtitleText.Text = subtitle;
+ }
+ }
+
+ public void CloseWhenReady()
+ {
+ if (_closeRequested)
+ {
+ return;
+ }
+
+ _closeRequested = true;
+ TimeSpan remaining = MinimumDisplayDuration - (DateTime.UtcNow - _shownAtUtc);
+ if (remaining <= TimeSpan.Zero)
+ {
+ CloseOverlay();
+ return;
+ }
+
+ DispatcherTimer timer = new DispatcherTimer(
+ remaining,
+ DispatcherPriority.Background,
+ OnCloseTimerTick,
+ Dispatcher);
+ timer.Start();
+ }
+
+ private void OnCloseTimerTick(object sender, EventArgs e)
+ {
+ DispatcherTimer timer = sender as DispatcherTimer;
+ if (timer != null)
+ {
+ timer.Stop();
+ timer.Tick -= OnCloseTimerTick;
+ }
+
+ CloseOverlay();
+ }
+
+ private void CloseOverlay()
+ {
+ if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished || !IsLoaded)
+ {
+ return;
+ }
+
+ DoubleAnimation fadeOutAnimation = new DoubleAnimation
+ {
+ From = Opacity,
+ To = 0,
+ Duration = new Duration(DismissAnimationDuration),
+ FillBehavior = FillBehavior.Stop
+ };
+
+ fadeOutAnimation.Completed += (sender, args) =>
+ {
+ if (Dispatcher.HasShutdownStarted || Dispatcher.HasShutdownFinished || !IsLoaded)
+ {
+ return;
+ }
+
+ Close();
+ };
+
+ BeginAnimation(OpacityProperty, fadeOutAnimation);
+ }
+ }
+}
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 759a98c..310366d 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:自救器呼吸器综合检验仪"
- Title="自救器呼吸器综合检验仪" Height="900" Width="1280"
+ Title="自救器呼吸器综合检验仪" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen"
Background="Transparent"
FontFamily="Microsoft YaHeui" Loaded="Window_Loaded" Closed="Window_Closed" Closing="Window_Closing">
@@ -11,7 +11,7 @@
-
+