Files
zijiuqizonghejianyanyi/WindowNavigationHelper.cs

258 lines
9.3 KiB
C#
Raw Permalink Normal View History

2026-03-16 19:10:33 +08:00
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;
}
}
}