This commit is contained in:
GukSang.Jin
2026-03-16 19:10:33 +08:00
parent 34083df6de
commit 88a65233a7
26 changed files with 839 additions and 235 deletions

View File

@@ -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}", "错误");
}

65
AppShutdownCoordinator.cs Normal file
View File

@@ -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;
}
}
}

View File

@@ -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">
<Grid>
</Grid>

View File

@@ -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">

187
LoadingOverlayWindow.xaml Normal file
View File

@@ -0,0 +1,187 @@
<Window x:Class="自救器呼吸器综合检验仪.LoadingOverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStyle="None"
AllowsTransparency="True"
Background="#5C0F2238"
ShowInTaskbar="False"
ShowActivated="False"
ResizeMode="NoResize"
Topmost="False"
WindowStartupLocation="Manual">
<Window.Resources>
<Storyboard x:Key="CardIntroStoryboard">
<DoubleAnimation Storyboard.TargetName="LoadingCard"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
Duration="0:0:0.18" />
<DoubleAnimation Storyboard.TargetName="CardTranslate"
Storyboard.TargetProperty="Y"
From="18"
To="0"
Duration="0:0:0.18" />
</Storyboard>
<Storyboard x:Key="SpinnerStoryboard" RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="SpinnerRotate"
Storyboard.TargetProperty="Angle"
From="0"
To="360"
Duration="0:0:0.95" />
<DoubleAnimation Storyboard.TargetName="SpinnerGlowScale"
Storyboard.TargetProperty="ScaleX"
From="0.88"
To="1.08"
Duration="0:0:0.65"
AutoReverse="True"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="SpinnerGlowScale"
Storyboard.TargetProperty="ScaleY"
From="0.88"
To="1.08"
Duration="0:0:0.65"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
<Storyboard x:Key="SubtitlePulseStoryboard" RepeatBehavior="Forever" AutoReverse="True">
<DoubleAnimation Storyboard.TargetName="SubtitleText"
Storyboard.TargetProperty="Opacity"
From="0.62"
To="1"
Duration="0:0:0.8" />
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource CardIntroStoryboard}" />
<BeginStoryboard Storyboard="{StaticResource SpinnerStoryboard}" />
<BeginStoryboard Storyboard="{StaticResource SubtitlePulseStoryboard}" />
</EventTrigger>
</Window.Triggers>
<Grid IsHitTestVisible="False">
<Border x:Name="LoadingCard"
Width="390"
Padding="32"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="18"
Background="#F7FAFD"
BorderBrush="#CFE0F1"
BorderThickness="1"
Opacity="0">
<Border.Effect>
<DropShadowEffect BlurRadius="28"
Opacity="0.24"
ShadowDepth="0"
Color="#0F2238" />
</Border.Effect>
<Border.RenderTransform>
<TranslateTransform x:Name="CardTranslate" Y="18" />
</Border.RenderTransform>
<StackPanel>
<Border HorizontalAlignment="Center"
Margin="0,0,0,14"
Padding="12,5"
CornerRadius="999"
Background="#E6F0FA">
<TextBlock Text="页面切换中"
FontSize="12"
FontWeight="SemiBold"
Foreground="#245E95" />
</Border>
<Grid Width="92"
Height="92"
Margin="0,0,0,20"
HorizontalAlignment="Center">
<Ellipse Width="92"
Height="92"
Fill="#EAF2FB" />
<Ellipse Width="72"
Height="72"
Fill="#F7FAFD">
<Ellipse.Effect>
<DropShadowEffect BlurRadius="16"
Opacity="0.12"
ShadowDepth="0"
Color="#2A7FD1" />
</Ellipse.Effect>
</Ellipse>
<Canvas Width="56"
Height="56"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5">
<Canvas.RenderTransform>
<RotateTransform x:Name="SpinnerRotate" />
</Canvas.RenderTransform>
<Canvas.Effect>
<DropShadowEffect BlurRadius="10"
Opacity="0.26"
ShadowDepth="0"
Color="#2A7FD1" />
</Canvas.Effect>
<Canvas.LayoutTransform>
<ScaleTransform x:Name="SpinnerGlowScale" />
</Canvas.LayoutTransform>
<Ellipse Width="12" Height="12" Fill="#2A7FD1" Canvas.Left="22" Canvas.Top="0" />
<Ellipse Width="10" Height="10" Fill="#4F98DE" Canvas.Left="39" Canvas.Top="7" />
<Ellipse Width="8" Height="8" Fill="#76B0E8" Canvas.Left="48" Canvas.Top="23" />
<Ellipse Width="7" Height="7" Fill="#A8D1F4" Canvas.Left="43" Canvas.Top="40" />
<Ellipse Width="6" Height="6" Fill="#D3E8FA" Canvas.Left="25" Canvas.Top="49" />
<Ellipse Width="7" Height="7" Fill="#9BC9F0" Canvas.Left="9" Canvas.Top="42" />
<Ellipse Width="8" Height="8" Fill="#69A8E4" Canvas.Left="1" Canvas.Top="24" />
<Ellipse Width="10" Height="10" Fill="#3B8BD7" Canvas.Left="8" Canvas.Top="7" />
</Canvas>
</Grid>
<TextBlock x:Name="TitleText"
Text="正在切换检测页面"
FontSize="26"
FontWeight="Bold"
Foreground="#17324D"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock x:Name="SubtitleText"
Text="正在打开检测页面,请稍候..."
Margin="0,10,0,6"
FontSize="14"
Foreground="#4C637A"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock x:Name="HintText"
Text="系统正在加载界面资源并同步设备状态,请稍候..."
Margin="0,0,0,18"
FontSize="12"
Foreground="#6B7F93"
HorizontalAlignment="Center"
TextAlignment="Center"
TextWrapping="Wrap" />
<Border Height="8"
CornerRadius="999"
Background="#DCE8F4">
<Grid Width="110"
HorizontalAlignment="Left">
<Rectangle RadiusX="999"
RadiusY="999"
Fill="#2A7FD1" />
<Rectangle Margin="12,0"
RadiusX="999"
RadiusY="999"
Fill="#72B1EA"
Opacity="0.68" />
</Grid>
</Border>
</StackPanel>
</Border>
</Grid>
</Window>

View File

@@ -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);
}
}
}

View File

@@ -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">
@@ -71,7 +71,7 @@
<!--<TextBlock Text="定量供应检验系统"
FontSize="14" Foreground="#CCFFFFFF" Margin="0,5,0,0"/>-->
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="2" HorizontalAlignment="Center">
<StackPanel Grid.Column="3" Orientation="Horizontal" Margin="2" HorizontalAlignment="Center">
<TextBlock Text="样品编号:" FontSize="26" FontWeight="Bold" Foreground="White" Style="{StaticResource LabelStyle}"/>
<TextBox x:Name="pressureDiff2" Text="" Width="180" Style="{StaticResource TextBoxStyle}" LostFocus="pressureDiff2_GotFocus"/>
@@ -98,7 +98,7 @@
<!-- 主内容区域 -->
<Grid Grid.Row="1" Margin="0,0,0,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
@@ -119,39 +119,51 @@
<!-- 检验时间配置 -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,15">
<TextBlock Text="检验时间:" Width="100" VerticalAlignment="Center"
<Grid Margin="0,0,0,15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Text="检验时间:" VerticalAlignment="Center"
FontSize="14" FontWeight="SemiBold" Foreground="#2C3E50"/>
<TextBox x:Name="settingtime" Text="0" Width="100" Height="35"
<TextBox Grid.Column="1" x:Name="settingtime" Text="0" Width="100" Height="35"
VerticalContentAlignment="Center" TextAlignment="Center"
BorderBrush="#DCDFE6" BorderThickness="1" Background="#FAFBFC"
FontSize="14" TextChanged="settingtime_TextChanged"/>
<TextBlock Text="秒" Margin="15,0,40,0" VerticalAlignment="Center"
<TextBlock Grid.Column="2" Text="秒" Margin="15,0,20,0" VerticalAlignment="Center"
FontSize="14" Foreground="#7F8C8D"/>
<Button Content="🔧 设置参数" Click="BtnWrite401_Click"
Background="#3498DB" Foreground="White" FontSize="13" FontWeight="Bold"
Width="120" Height="38" BorderThickness="0" Margin="0,0,25,0"
Cursor="Hand"/>
Grid.Row="1" Grid.Column="0"
Background="#3498DB" Foreground="White" FontSize="13" FontWeight="Bold" Height="38" BorderThickness="0" Margin="0,12,53,0"
Cursor="Hand" Grid.ColumnSpan="2"/>
<Button x:Name="btn_flow2" Content="定量供氧L/min" Click="BtnWrite400_Click"
Background="#3498DB" Foreground="White" FontSize="13" FontWeight="Bold"
Width="120" Height="38" BorderThickness="0" Margin="0,0,25,0"
Cursor="Hand"/>
Grid.Row="1" Grid.Column="1"
Background="#3498DB" Foreground="White" FontSize="13" FontWeight="Bold" Height="38" BorderThickness="0" Margin="91,12,8,0"
Cursor="Hand" Grid.ColumnSpan="3"/>
<Button x:Name="btn_flow3" Content="自动/手动补给供氧(L/min)" Click="BtnWrite400_Click1"
Background="#3498DB" Foreground="White" FontSize="13" FontWeight="Bold"
Width="174" Height="38" BorderThickness="0" Margin="0,0,25,0"
Cursor="Hand"/>
Grid.Row="1" Grid.Column="4" Grid.ColumnSpan="2"
Background="#3498DB" Foreground="White" FontSize="12" FontWeight="Bold"
Height="38" BorderThickness="0" Padding="12,0" Margin="44,12,24,0" Cursor="Hand"/>
<!--<Border Background="#F8F9FA" Padding="10,6" BorderBrush="#E1E8ED" BorderThickness="1">-->
<!--<TextBlock x:Name="label_flow" Text="0-100L/min" VerticalAlignment="Center"
<!--<TextBlock x:Name="label_flow" Text="0-100L/min" VerticalAlignment="Center"
FontSize="12" FontWeight="Bold" Foreground="#3498DB"/>-->
<!--</Border>-->
</StackPanel>
</Grid>
<!-- 试验时间显示 -->
<!--<StackPanel Orientation="Horizontal">

View File

@@ -37,6 +37,10 @@ namespace 自救器呼吸器综合检验仪
private bool _wasRunning = false; // 上次检验的运行状态
private bool _recordAdded = false; // 本轮测试是否已插入记录
private DateTime _testStartTime; // 测试开始时间
private Button _selectedParameterButton;
private static readonly Brush ParameterButtonDefaultBrush = CreateParameterButtonBrush("#3498DB");
private static readonly Brush ParameterButtonSelectedBrush = CreateParameterButtonBrush("#F39C12");
int retryCount = 0; // 连接失败重试次数
private TcpClient _tcpClient => ModbusResourceManager.Instance.TcpClient;
@@ -225,6 +229,7 @@ namespace 自救器呼吸器综合检验仪
{
_modbusMaster.WriteSingleCoil(1, 75, true);
isReset = true;
UpdateParameterButtonSelection(sender as Button);
}
@@ -236,6 +241,39 @@ namespace 自救器呼吸器综合检验仪
});
}
private static Brush CreateParameterButtonBrush(string colorCode)
{
return new SolidColorBrush((Color)ColorConverter.ConvertFromString(colorCode));
}
private void UpdateParameterButtonSelection(Button selectedButton)
{
if (selectedButton == null)
{
return;
}
if (_selectedParameterButton != null && !ReferenceEquals(_selectedParameterButton, selectedButton))
{
ApplyParameterButtonStyle(_selectedParameterButton, false);
}
_selectedParameterButton = selectedButton;
ApplyParameterButtonStyle(_selectedParameterButton, true);
}
private void ApplyParameterButtonStyle(Button button, bool isSelected)
{
if (button == null)
{
return;
}
button.Background = isSelected ? ParameterButtonSelectedBrush : ParameterButtonDefaultBrush;
button.BorderBrush = Brushes.Transparent;
button.BorderThickness = new Thickness(0);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//ma.BtnClickFunctionForNew(Function.ButtonType.切换型, 170);
@@ -261,22 +299,22 @@ namespace 自救器呼吸器综合检验仪
private void Button_Click_4(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow, () => new MainWindow());
SwitchWindow(ref _mainWindow, () => new MainWindow(), "正在进入定量供应检验", "正在准备定量供应检验页面...");
}
private void Button_Click_5(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow2, () => new MainWindow2());
SwitchWindow(ref _mainWindow2, () => new MainWindow2(), "正在进入负压气密性检验", "正在切换到负压气密性检验页面...");
}
private void Button_Click_6(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow3, () => new MainWindow3());
SwitchWindow(ref _mainWindow3, () => new MainWindow3(), "正在进入正压气密性检验", "正在切换到正压气密性检验页面...");
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow4, () => new MainWindow4());
SwitchWindow(ref _mainWindow4, () => new MainWindow4(), "正在进入排气阀压力测试", "正在切换到排气阀压力测试页面...");
}
private void Window_Loaded(object sender, RoutedEventArgs e)
@@ -319,11 +357,6 @@ namespace 自救器呼吸器综合检验仪
pressureDiff2.Text = ConfigurationManager.AppSettings["No"]?.ToString();
string imagePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/sleep2.jpg");
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(imagePath, UriKind.Absolute));
this.Background = brush;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
@@ -340,18 +373,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 清理其他窗口实例
//_mainWindow?.Close();
_mainWindow2?.Close();
_mainWindow3?.Close();
_mainWindow4?.Close();
_mainWindow5?.Close();
_reportWindow?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
private void BtnWrite401_Click(object sender, RoutedEventArgs e)
{
@@ -373,6 +400,7 @@ namespace 自救器呼吸器综合检验仪
System.Threading.Tasks.Task.Delay(50);
_isManualInput = false; // 写入后恢复读取
UpdateParameterButtonSelection(sender as Button);
//await ReadAddr400DataAsync(); // 刷新显示(确认写入成功)
}
@@ -382,7 +410,7 @@ namespace 自救器呼吸器综合检验仪
}
}
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc) where T : Window, new()
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc, string loadingTitle = null, string loadingSubtitle = null) where T : Window, new()
{
// 1. 停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
@@ -406,22 +434,12 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
_readTimer?.Start();
//this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this, () => _readTimer?.Start());
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
//return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
// 4. 目标窗口完成首帧渲染后再切换,避免白屏
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance, loadingTitle, loadingSubtitle);
}
// 添加重连方法
@@ -572,6 +590,7 @@ namespace 自救器呼吸器综合检验仪
{
_modbusMaster.WriteSingleCoil(1, 75, false);
isReset = false;
UpdateParameterButtonSelection(sender as Button);
}
//private void pressureDiff2_GotFocus(object sender, RoutedEventArgs e)

View File

@@ -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">

View File

@@ -419,22 +419,22 @@ namespace 自救器呼吸器综合检验仪
private void Button_Click_4(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow, () => new MainWindow());
SwitchWindow(ref _mainWindow, () => new MainWindow(), "正在进入定量供应检验", "正在准备定量供应检验页面...");
}
private void Button_Click_5(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow2, () => new MainWindow2());
SwitchWindow(ref _mainWindow2, () => new MainWindow2(), "正在进入负压气密性检验", "正在切换到负压气密性检验页面...");
}
private void Button_Click_6(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow3, () => new MainWindow3());
SwitchWindow(ref _mainWindow3, () => new MainWindow3(), "正在进入正压气密性检验", "正在切换到正压气密性检验页面...");
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow4, () => new MainWindow4());
SwitchWindow(ref _mainWindow4, () => new MainWindow4(), "正在进入排气阀压力测试", "正在切换到排气阀压力测试页面...");
}
@@ -476,7 +476,7 @@ namespace 自救器呼吸器综合检验仪
}
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc) where T : Window, new()
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc, string loadingTitle = null, string loadingSubtitle = null) where T : Window, new()
{
// 1. 停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
@@ -500,22 +500,12 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
_readTimer?.Start();
//this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this, () => _readTimer?.Start());
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
//return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
// 4. 目标窗口完成首帧渲染后再切换,避免白屏
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance, loadingTitle, loadingSubtitle);
}
// 添加重连方法
private bool TryReconnect()
@@ -668,17 +658,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 清理其他窗口实例
//_mainWindow2?.Close();
_mainWindow3?.Close();
_mainWindow4?.Close();
_mainWindow?.Close();
_reportWindow2?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
//private void number_GotFocus(object sender, RoutedEventArgs e)

View File

@@ -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"

View File

@@ -394,22 +394,22 @@ namespace 自救器呼吸器综合检验仪
private void Button_Click_4(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow, () => new MainWindow());
SwitchWindow(ref _mainWindow, () => new MainWindow(), "正在进入定量供应检验", "正在准备定量供应检验页面...");
}
private void Button_Click_5(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow2, () => new MainWindow2());
SwitchWindow(ref _mainWindow2, () => new MainWindow2(), "正在进入负压气密性检验", "正在切换到负压气密性检验页面...");
}
private void Button_Click_6(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow3, () => new MainWindow3());
SwitchWindow(ref _mainWindow3, () => new MainWindow3(), "正在进入正压气密性检验", "正在切换到正压气密性检验页面...");
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow4, () => new MainWindow4());
SwitchWindow(ref _mainWindow4, () => new MainWindow4(), "正在进入排气阀压力测试", "正在切换到排气阀压力测试页面...");
}
private void Window_Loaded(object sender, RoutedEventArgs e)
@@ -449,7 +449,7 @@ namespace 自救器呼吸器综合检验仪
System.Threading.Tasks.Task.Delay(50);
}
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc) where T : Window, new()
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc, string loadingTitle = null, string loadingSubtitle = null) where T : Window, new()
{
// 1. 停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
@@ -473,22 +473,12 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
_readTimer?.Start();
//this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this, () => _readTimer?.Start());
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
//return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
// 4. 目标窗口完成首帧渲染后再切换,避免白屏
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance, loadingTitle, loadingSubtitle);
}
// 添加重连方法
private bool TryReconnect()
@@ -671,17 +661,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 清理其他窗口实例
_mainWindow?.Close();
_mainWindow2?.Close();
//_mainWindow3?.Close();
_mainWindow4?.Close();
_reportWindow3?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
}
}

View File

@@ -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"

View File

@@ -238,22 +238,22 @@ namespace 自救器呼吸器综合检验仪
private void Button_Click_4(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow, () => new MainWindow());
SwitchWindow(ref _mainWindow, () => new MainWindow(), "正在进入定量供应检验", "正在准备定量供应检验页面...");
}
private void Button_Click_5(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow2, () => new MainWindow2());
SwitchWindow(ref _mainWindow2, () => new MainWindow2(), "正在进入负压气密性检验", "正在切换到负压气密性检验页面...");
}
private void Button_Click_6(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow3, () => new MainWindow3());
SwitchWindow(ref _mainWindow3, () => new MainWindow3(), "正在进入正压气密性检验", "正在切换到正压气密性检验页面...");
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
SwitchWindow(ref _mainWindow4, () => new MainWindow4());
SwitchWindow(ref _mainWindow4, () => new MainWindow4(), "正在进入排气阀压力测试", "正在切换到排气阀压力测试页面...");
}
private void Window_Loaded(object sender, RoutedEventArgs e)
@@ -294,7 +294,7 @@ namespace 自救器呼吸器综合检验仪
}
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc) where T : Window, new()
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc, string loadingTitle = null, string loadingSubtitle = null) where T : Window, new()
{
// 1. 停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
@@ -318,22 +318,12 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
_readTimer?.Start();
//this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this, () => _readTimer?.Start());
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
//return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
// 4. 目标窗口完成首帧渲染后再切换,避免白屏
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance, loadingTitle, loadingSubtitle);
}
private bool TryReconnect()
{
@@ -505,17 +495,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 清理其他窗口实例
_mainWindow2?.Close();
_mainWindow3?.Close();
//_mainWindow4?.Close();
_mainWindow?.Close();
_reportWindow4?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
}
}

View File

@@ -1,7 +1,7 @@
<Window x:Class="自救器呼吸器综合检验仪.ParameterSettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="参数设置" Height="768" Width="1024"
Title="参数设置" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize" Loaded="Window_Loaded" Closed="Window_Closed" Closing="Window_Closing">

View File

@@ -140,22 +140,16 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
_readTimer?.Start();
this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this, () => _readTimer?.Start());
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
}
@@ -488,18 +482,12 @@ _modbusMaster?.ReadHoldingRegisters(1, 290, 2)
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
// 清理其他窗口实例
_mainWindow?.Close();
_mainWindow2?.Close();
_mainWindow3?.Close();
//_mainWindow4?.Close();
_mainWindow4?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
// 重置为默认值
private void ResetToDefaults()

View File

@@ -1,7 +1,7 @@
<Window x:Class="自救器呼吸器综合检验仪.ReportWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="定量供氧检验报表" Height="900" Width="1280"
Title="定量供氧检验报表" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen" Loaded="Window_Loaded" Closed="Window_Closed" Closing="Window_Closing" >
<Grid>
<Grid.RowDefinitions>

View File

@@ -218,14 +218,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
//_mainWindow4?.Close();
_mainWindow?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
private void BtnPrint_Click(object sender, RoutedEventArgs e)
@@ -274,22 +272,16 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this);
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
}
private void BtnClose_Click(object sender, RoutedEventArgs e)

View File

@@ -1,7 +1,7 @@
<Window x:Class="自救器呼吸器综合检验仪.ReportWindow2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="负压气密性检验报表" Height="900" Width="1280"
Title="负压气密性检验报表" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen" Loaded="Window_Loaded" Closed="Window_Closed" Closing="Window_Closing">
<Grid>
<Grid.RowDefinitions>

View File

@@ -232,14 +232,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
//_mainWindow4?.Close();
_reportWindow2?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
//private void BtnClose_Click(object sender, RoutedEventArgs e)
@@ -294,22 +292,16 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this);
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
}
private void BtnClose_Click(object sender, RoutedEventArgs e)

View File

@@ -1,7 +1,7 @@
<Window x:Class="自救器呼吸器综合检验仪.ReportWindow3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="正压气密性检验报表" Height="900" Width="1280"
Title="正压气密性检验报表" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen" Loaded="Window_Loaded" Closed="Window_Closed" Closing="Window_Closing">
<Grid>
<Grid.RowDefinitions>

View File

@@ -230,14 +230,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
//_mainWindow4?.Close();
_reportWindow3?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
//private void BtnClose_Click(object sender, RoutedEventArgs e)
@@ -292,22 +290,16 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this);
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
}
private void BtnClose_Click(object sender, RoutedEventArgs e)

View File

@@ -1,7 +1,7 @@
<Window x:Class="自救器呼吸器综合检验仪.ReportWindow4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="排气阀检验报表" Height="900" Width="1280"
Title="排气阀检验报表" Height="600" Width="1024" WindowState="Maximized"
WindowStartupLocation="CenterScreen" Loaded="Window_Loaded" Closed="Window_Closed" Closing="Window_Closing" >
<Grid>
<Grid.RowDefinitions>

View File

@@ -271,14 +271,12 @@ namespace 自救器呼吸器综合检验仪
// 释放Modbus资源
ModbusResourceManager.Instance?.Dispose();
// 确保应用程序完全退出
Application.Current.Shutdown();
// 延后到当前窗口完成关闭后再统一关停,避免关闭重入。
AppShutdownCoordinator.RequestShutdown();
}
private void Window_Closed(object sender, EventArgs e)
{
//_mainWindow4?.Close();
_reportWindow4?.Close();
// 由应用级关停统一处理其他窗口,避免关闭链路重入。
}
//private void BtnClose_Click(object sender, RoutedEventArgs e)
@@ -333,22 +331,16 @@ namespace 自救器呼吸器综合检验仪
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
this.Show();
this.Activate();
WindowNavigationHelper.RestoreWindow(this);
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
WindowNavigationHelper.ShowWithoutWhiteFlash(this, windowInstance);
}
private void BtnClose_Click(object sender, RoutedEventArgs e)

257
WindowNavigationHelper.cs Normal file
View File

@@ -0,0 +1,257 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace
{
internal static class WindowNavigationHelper
{
private const string SlideTransformResourceKey = "__WindowNavigationHelperSlideTransform";
private static readonly TimeSpan TargetFadeDuration = TimeSpan.FromMilliseconds(420);
private static readonly TimeSpan ContentSlideDuration = TimeSpan.FromMilliseconds(360);
private static readonly CubicEase SlideEase = new CubicEase { EasingMode = EasingMode.EaseOut };
public static void ShowWithoutWhiteFlash(Window currentWindow, Window targetWindow, string loadingTitle = null, string loadingSubtitle = null)
{
if (!CanInteractWith(currentWindow) || !CanInteractWith(targetWindow))
{
return;
}
ApplyWindowPlacement(currentWindow, targetWindow);
PrepareTargetWindowForReveal(targetWindow);
if (targetWindow.IsVisible)
{
RevealTargetWindow(currentWindow, targetWindow, null);
return;
}
LoadingOverlayWindow loadingOverlay = ShowLoadingOverlay(currentWindow, loadingTitle, loadingSubtitle);
if (targetWindow.IsLoaded)
{
targetWindow.Show();
targetWindow.Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(() => RevealTargetWindow(currentWindow, targetWindow, loadingOverlay)));
return;
}
EventHandler contentRenderedHandler = null;
contentRenderedHandler = (sender, args) =>
{
targetWindow.ContentRendered -= contentRenderedHandler;
RevealTargetWindow(currentWindow, targetWindow, loadingOverlay);
};
targetWindow.ContentRendered += contentRenderedHandler;
targetWindow.Show();
}
public static void RestoreWindow(Window windowToRestore, Action beforeRestore = null)
{
if (!CanInteractWith(windowToRestore))
{
return;
}
beforeRestore?.Invoke();
if (!windowToRestore.IsVisible)
{
windowToRestore.Show();
}
windowToRestore.Activate();
}
private static void ApplyWindowPlacement(Window currentWindow, Window targetWindow)
{
targetWindow.WindowStartupLocation = WindowStartupLocation.Manual;
targetWindow.WindowState = currentWindow.WindowState;
if (currentWindow.WindowState != WindowState.Normal)
{
return;
}
targetWindow.Left = currentWindow.Left;
targetWindow.Top = currentWindow.Top;
targetWindow.Width = currentWindow.ActualWidth > 0 ? currentWindow.ActualWidth : currentWindow.Width;
targetWindow.Height = currentWindow.ActualHeight > 0 ? currentWindow.ActualHeight : currentWindow.Height;
}
private static void PrepareTargetWindowForReveal(Window targetWindow)
{
targetWindow.Opacity = 0;
FrameworkElement contentRoot = targetWindow.Content as FrameworkElement;
if (contentRoot == null)
{
return;
}
TranslateTransform slideTransform = GetOrCreateSlideTransform(contentRoot);
slideTransform.X = 72;
contentRoot.Opacity = 0.9;
}
private static void RevealTargetWindow(Window currentWindow, Window targetWindow, LoadingOverlayWindow loadingOverlay)
{
if (!CanInteractWith(currentWindow) || !CanInteractWith(targetWindow))
{
CloseLoadingOverlay(loadingOverlay);
return;
}
currentWindow.Hide();
AnimateTargetWindow(targetWindow);
targetWindow.Activate();
targetWindow.Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(() => CloseLoadingOverlay(loadingOverlay)));
}
private static LoadingOverlayWindow ShowLoadingOverlay(Window currentWindow, string loadingTitle, string loadingSubtitle)
{
if (!CanInteractWith(currentWindow))
{
return null;
}
LoadingOverlayWindow loadingOverlay = new LoadingOverlayWindow();
loadingOverlay.AlignTo(currentWindow);
loadingOverlay.MatchTopmost(currentWindow);
loadingOverlay.SetMessage(
loadingTitle ?? "正在切换检测页面",
loadingSubtitle ?? "正在加载页面资源和设备状态,请稍候...");
loadingOverlay.Show();
return loadingOverlay;
}
private static void CloseLoadingOverlay(LoadingOverlayWindow loadingOverlay)
{
if (loadingOverlay == null)
{
return;
}
if (loadingOverlay.Dispatcher.HasShutdownStarted || loadingOverlay.Dispatcher.HasShutdownFinished)
{
return;
}
if (!loadingOverlay.IsLoaded)
{
return;
}
loadingOverlay.CloseWhenReady();
}
private static void AnimateTargetWindow(Window targetWindow)
{
if (targetWindow == null || targetWindow.Dispatcher.HasShutdownStarted || targetWindow.Dispatcher.HasShutdownFinished)
{
return;
}
DoubleAnimation fadeInAnimation = new DoubleAnimation
{
From = targetWindow.Opacity,
To = 1,
Duration = new Duration(TargetFadeDuration),
FillBehavior = FillBehavior.HoldEnd
};
targetWindow.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
FrameworkElement contentRoot = targetWindow.Content as FrameworkElement;
if (contentRoot == null)
{
return;
}
TranslateTransform slideTransform = GetOrCreateSlideTransform(contentRoot);
DoubleAnimation slideAnimation = new DoubleAnimation
{
From = slideTransform.X,
To = 0,
Duration = new Duration(ContentSlideDuration),
EasingFunction = SlideEase,
FillBehavior = FillBehavior.HoldEnd
};
slideTransform.BeginAnimation(TranslateTransform.XProperty, slideAnimation);
DoubleAnimation contentFadeAnimation = new DoubleAnimation
{
From = contentRoot.Opacity,
To = 1,
Duration = new Duration(ContentSlideDuration),
EasingFunction = SlideEase,
FillBehavior = FillBehavior.HoldEnd
};
contentRoot.BeginAnimation(UIElement.OpacityProperty, contentFadeAnimation);
}
private static TranslateTransform GetOrCreateSlideTransform(FrameworkElement contentRoot)
{
if (contentRoot.Resources[SlideTransformResourceKey] is TranslateTransform cachedTransform)
{
return cachedTransform;
}
TranslateTransform slideTransform = null;
Transform renderTransform = contentRoot.RenderTransform;
if (renderTransform is TranslateTransform existingTranslate)
{
slideTransform = existingTranslate;
}
else if (renderTransform is TransformGroup existingGroup)
{
foreach (Transform child in existingGroup.Children)
{
if (child is TranslateTransform childTranslate)
{
slideTransform = childTranslate;
break;
}
}
if (slideTransform == null)
{
slideTransform = new TranslateTransform();
existingGroup.Children.Add(slideTransform);
}
}
else if (renderTransform == null || renderTransform == Transform.Identity || renderTransform.Value.IsIdentity)
{
slideTransform = new TranslateTransform();
contentRoot.RenderTransform = slideTransform;
}
else
{
TransformGroup transformGroup = new TransformGroup();
transformGroup.Children.Add(renderTransform);
slideTransform = new TranslateTransform();
transformGroup.Children.Add(slideTransform);
contentRoot.RenderTransform = transformGroup;
}
contentRoot.RenderTransformOrigin = new Point(0.5, 0.5);
contentRoot.Resources[SlideTransformResourceKey] = slideTransform;
return slideTransform;
}
private static bool CanInteractWith(Window window)
{
return window != null
&& !AppShutdownCoordinator.IsShutdownInProgress
&& !window.Dispatcher.HasShutdownStarted
&& !window.Dispatcher.HasShutdownFinished;
}
}
}

View File

@@ -152,6 +152,9 @@
<Compile Include="DetectionMenuWindow.xaml.cs">
<DependentUpon>DetectionMenuWindow.xaml</DependentUpon>
</Compile>
<Compile Include="LoadingOverlayWindow.xaml.cs">
<DependentUpon>LoadingOverlayWindow.xaml</DependentUpon>
</Compile>
<Compile Include="ParameterSettingsWindow.xaml.cs">
<DependentUpon>ParameterSettingsWindow.xaml</DependentUpon>
</Compile>
@@ -179,7 +182,9 @@
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="AppShutdownCoordinator.cs" />
<Compile Include="MainViewModel.cs" />
<Compile Include="WindowNavigationHelper.cs" />
<Compile Include="MainWindow2.xaml.cs">
<DependentUpon>MainWindow2.xaml</DependentUpon>
</Compile>
@@ -205,6 +210,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="LoadingOverlayWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow2.xaml">
<SubType>Designer</SubType>
<Generator>XamlIntelliSenseFileGenerator</Generator>