using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Animation; namespace Ink_Canvas.Helpers { internal class AnimationsHelper { #region Win32 API - 用于提升 Popup 层级和刷新位置 [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 bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } private const uint GW_HWNDPREV = 3; private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); private static readonly IntPtr HWND_TOP = new IntPtr(0); private const uint SWP_NOMOVE = 0x0002; private const uint SWP_NOSIZE = 0x0001; private const uint SWP_NOACTIVATE = 0x0010; private const uint SWP_SHOWWINDOW = 0x0040; /// /// 强制刷新 Popup 的实际窗口位置(终极方案) /// 通过 Win32 API 直接操作窗口句柄 /// public static void ForceRefreshPopupPosition(Popup popup) { if (popup?.Child == null || !popup.IsOpen) return; try { Application.Current.Dispatcher.BeginInvoke(new Action(() => { try { var source = PresentationSource.FromVisual(popup.Child) as HwndSource; if (source?.Handle == null) return; var hwnd = source.Handle; // 获取当前窗口位置 if (GetWindowRect(hwnd, out RECT rect)) { // 使用相同的参数调用 SetWindowPos,但加上 SWP_SHOWWINDOW // 这会强制窗口管理器重新评估并更新窗口位置 SetWindowPos( hwnd, HWND_TOP, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, SWP_NOACTIVATE | SWP_SHOWWINDOW); System.Diagnostics.Debug.WriteLine($"[PopupZOrder] Force refreshed position: ({rect.Left}, {rect.Top})"); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[PopupZOrder] ForceRefreshPopupPosition failed: {ex.Message}"); } }), System.Windows.Threading.DispatcherPriority.Render); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[PopupZOrder] ForceRefreshPopupPosition error: {ex.Message}"); } } /// /// 将 Popup 窗口提升到最顶层,确保不被其他控件遮挡 /// 采用多重策略确保置顶生效 /// private static void BringPopupToFront(Popup popup) { try { if (popup?.Child == null) return; Action bringToTopAction = () => { try { var source = PresentationSource.FromVisual(popup.Child) as HwndSource; if (source?.Handle == null) return; var hwnd = source.Handle; // 策略1:直接设置为 TOPMOST(最高优先级) SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW); System.Diagnostics.Debug.WriteLine($"[PopupZOrder] Set TOPMOST for popup"); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[PopupZOrder] BringPopupToFront failed: {ex.Message}"); } }; // 立即执行第一次 Application.Current.Dispatcher.BeginInvoke(bringToTopAction, System.Windows.Threading.DispatcherPriority.Render); // 延迟 50ms 后再次执行(确保在其他窗口操作之后) Application.Current.Dispatcher.BeginInvoke(bringToTopAction, System.Windows.Threading.DispatcherPriority.Normal); // 延迟 100ms 后第三次执行(最终确认) Application.Current.Dispatcher.BeginInvoke(bringToTopAction, System.Windows.Threading.DispatcherPriority.Background); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[PopupZOrder] BringPopupToFront error: {ex.Message}"); } } #endregion private static UIElement ResolveAnimationTarget(UIElement element) { return element; } public static void ShowWithFadeIn(UIElement element, double duration = 0.15) { if (element.Visibility == Visibility.Visible) return; if (element == null) throw new ArgumentNullException(nameof(element)); var sb = new Storyboard(); // 渐变动画 var fadeInAnimation = new DoubleAnimation { From = 0.5, To = 1, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty)); sb.Children.Add(fadeInAnimation); element.Visibility = Visibility.Visible; sb.Begin((FrameworkElement)element); } public static void ShowWithSlideFromBottomAndFade(UIElement element, double duration = 0.15) { try { if (element == null) throw new ArgumentNullException(nameof(element)); if (element.Visibility == Visibility.Visible) return; element.Visibility = Visibility.Visible; var target = ResolveAnimationTarget(element); var sb = new Storyboard(); var fadeInAnimation = new DoubleAnimation { From = 0.5, To = 1, Duration = TimeSpan.FromSeconds(duration) }; fadeInAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty)); var slideAnimation = new DoubleAnimation { From = 10, To = 0, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); slideAnimation.EasingFunction = new CubicEase(); sb.Children.Add(fadeInAnimation); sb.Children.Add(slideAnimation); target.RenderTransform = new TranslateTransform(); sb.Begin((FrameworkElement)target); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } public static void ShowWithSlideFromLeftAndFade(UIElement element, double duration = 0.25) { try { if (element.Visibility == Visibility.Visible) return; if (element == null) throw new ArgumentNullException(nameof(element)); var sb = new Storyboard(); // 渐变动画 var fadeInAnimation = new DoubleAnimation { From = 0.5, To = 1, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty)); // 滑动动画 var slideAnimation = new DoubleAnimation { From = element.RenderTransform.Value.OffsetX - 20, // 滑动距离 To = 0, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)")); sb.Children.Add(fadeInAnimation); sb.Children.Add(slideAnimation); element.Visibility = Visibility.Visible; element.RenderTransform = new TranslateTransform(); sb.Begin((FrameworkElement)element); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } public static void ShowWithScaleFromLeft(UIElement element, double duration = 0.2) { try { if (element.Visibility == Visibility.Visible) return; if (element == null) throw new ArgumentNullException(nameof(element)); var sb = new Storyboard(); // 水平方向的缩放动画 var scaleXAnimation = new DoubleAnimation { From = 0, To = 1, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)")); // 垂直方向的缩放动画 var scaleYAnimation = new DoubleAnimation { From = 0, To = 1, Duration = TimeSpan.FromSeconds(duration) }; scaleYAnimation.EasingFunction = new CubicEase(); scaleXAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); sb.Children.Add(scaleXAnimation); sb.Children.Add(scaleYAnimation); element.Visibility = Visibility.Visible; element.RenderTransformOrigin = new Point(0, 0.5); // 左侧中心点为基准 element.RenderTransform = new ScaleTransform(0, 0); sb.Begin((FrameworkElement)element); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } public static void ShowWithScaleFromRight(UIElement element, double duration = 0.2) { try { if (element.Visibility == Visibility.Visible) return; if (element == null) throw new ArgumentNullException(nameof(element)); var sb = new Storyboard(); // 水平方向的缩放动画 var scaleXAnimation = new DoubleAnimation { From = 0, To = 1, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(scaleXAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)")); // 垂直方向的缩放动画 var scaleYAnimation = new DoubleAnimation { From = 0, To = 1, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(scaleYAnimation, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); scaleYAnimation.EasingFunction = new CubicEase(); scaleXAnimation.EasingFunction = new CubicEase(); sb.Children.Add(scaleXAnimation); sb.Children.Add(scaleYAnimation); element.Visibility = Visibility.Visible; element.RenderTransformOrigin = new Point(1, 0.5); // 右侧中心点为基准 element.RenderTransform = new ScaleTransform(0, 0); sb.Begin((FrameworkElement)element); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } public static void HideWithSlideAndFade(UIElement element, double duration = 0.15) { try { if (element == null) throw new ArgumentNullException(nameof(element)); if (element.Visibility == Visibility.Collapsed) return; var target = ResolveAnimationTarget(element); var sb = new Storyboard(); var fadeOutAnimation = new DoubleAnimation { From = 1, To = 0, Duration = TimeSpan.FromSeconds(duration) }; fadeOutAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(UIElement.OpacityProperty)); var slideAnimation = new DoubleAnimation { From = 0, To = 10, Duration = TimeSpan.FromSeconds(duration) }; slideAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); sb.Children.Add(fadeOutAnimation); sb.Children.Add(slideAnimation); sb.Completed += (s, e) => { element.Visibility = Visibility.Collapsed; }; target.RenderTransform = new TranslateTransform(); sb.Begin((FrameworkElement)target); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } public static void HideWithFadeOut(UIElement element, double duration = 0.15) { if (element.Visibility == Visibility.Collapsed) return; if (element == null) throw new ArgumentNullException(nameof(element)); var sb = new Storyboard(); var fadeOutAnimation = new DoubleAnimation { From = 1, To = 0, Duration = TimeSpan.FromSeconds(duration) }; Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(UIElement.OpacityProperty)); sb.Children.Add(fadeOutAnimation); sb.Completed += (s, e) => { element.Visibility = Visibility.Collapsed; }; sb.Begin((FrameworkElement)element); } public static void ShowPopupWithSlideAndFade(Popup popup, double duration = 0.15) { try { if (popup == null) throw new ArgumentNullException(nameof(popup)); if (popup.IsOpen) return; var child = popup.Child as FrameworkElement; if (child == null) { popup.IsOpen = true; BringPopupToFront(popup); return; } child.Opacity = 0.5; child.RenderTransform = new TranslateTransform(0, 10); popup.IsOpen = true; // 提升 Popup 到最顶层 BringPopupToFront(popup); var sb = new Storyboard(); var fadeInAnimation = new DoubleAnimation { From = 0.5, To = 1, Duration = TimeSpan.FromSeconds(duration) }; fadeInAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(UIElement.OpacityProperty)); var slideAnimation = new DoubleAnimation { From = 10, To = 0, Duration = TimeSpan.FromSeconds(duration) }; slideAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); sb.Children.Add(fadeInAnimation); sb.Children.Add(slideAnimation); sb.Begin(child); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } public static void HidePopupWithSlideAndFade(Popup popup, double duration = 0.15) { try { if (popup == null) throw new ArgumentNullException(nameof(popup)); if (!popup.IsOpen) return; var child = popup.Child as FrameworkElement; if (child == null) { popup.IsOpen = false; return; } var sb = new Storyboard(); var fadeOutAnimation = new DoubleAnimation { From = 1, To = 0, Duration = TimeSpan.FromSeconds(duration) }; fadeOutAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(UIElement.OpacityProperty)); var slideAnimation = new DoubleAnimation { From = 0, To = 10, Duration = TimeSpan.FromSeconds(duration) }; slideAnimation.EasingFunction = new CubicEase(); Storyboard.SetTargetProperty(slideAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)")); sb.Children.Add(fadeOutAnimation); sb.Children.Add(slideAnimation); sb.Completed += (s, e) => { popup.IsOpen = false; child.Opacity = 1; child.RenderTransform = new TranslateTransform(); }; child.RenderTransform = new TranslateTransform(); sb.Begin(child); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } } }