PowerShell/WPF中如何通过任意点击触发DoubleAnimation关闭动画
实现PowerShell/WPF动画按钮的全局触发(含窗口外点击)
核心解决方案思路
- 给主窗口添加
PreviewMouseDown事件,捕获所有鼠标点击,判断点击目标是否为需要保留的按钮,否则触发返回动画 - 绑定动画的
Completed事件,在动画结束后再执行按钮隐藏/窗口关闭逻辑,确保动画完整执行 - 窗口外点击处理:通过Windows API获取鼠标位置,判断是否落在窗口区域外,触发关闭动画
PowerShell原生实现代码
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase # 构建WPF窗口和控件 $window = New-Object System.Windows.Window $window.Title = "动画按钮示例" $window.Width = 300 $window.Height = 200 $window.WindowStyle = "ToolWindow" $window.AllowsTransparency = $true $window.Background = [System.Windows.Media.Brushes]::Transparent # 容器布局 $grid = New-Object System.Windows.Controls.Grid $window.Content = $grid # 初始按钮(弹出动画) $btnPopup = New-Object System.Windows.Controls.Button $btnPopup.Content = "点击弹出" $btnPopup.Width = 100 $btnPopup.Height = 40 $btnPopup.HorizontalAlignment = "Left" $btnPopup.VerticalAlignment = "Top" $grid.Children.Add($btnPopup) # 返回按钮(隐藏,待弹出后显示) $btnBack = New-Object System.Windows.Controls.Button $btnBack.Content = "返回" $btnBack.Width = 100 $btnBack.Height = 40 $btnBack.HorizontalAlignment = "Right" $btnBack.VerticalAlignment = "Top" $btnBack.Visibility = "Hidden" $grid.Children.Add($btnBack) # 弹出动画定义 $popupAnimation = New-Object System.Windows.Media.Animation.DoubleAnimation $popupAnimation.From = 0 $popupAnimation.To = 200 $popupAnimation.Duration = [System.Windows.Duration]::FromSeconds(0.3) # 返回动画定义 $backAnimation = New-Object System.Windows.Media.Animation.DoubleAnimation $backAnimation.From = 200 $backAnimation.To = 0 $backAnimation.Duration = [System.Windows.Duration]::FromSeconds(0.3) # 弹出按钮点击事件 $btnPopup.Add_Click({ $btnPopup.BeginAnimation([System.Windows.Controls.Button]::MarginProperty, $popupAnimation) $popupAnimation.Completed.Add({ $btnPopup.Visibility = "Hidden" $btnBack.Visibility = "Visible" }) }) # 返回按钮点击事件 $btnBack.Add_Click({ $btnBack.BeginAnimation([System.Windows.Controls.Button]::MarginProperty, $backAnimation) $backAnimation.Completed.Add({ $btnBack.Visibility = "Hidden" $btnPopup.Visibility = "Visible" }) }) # 全局窗口点击事件(触发返回动画) $window.Add_PreviewMouseDown({ param($sender, $e) # 如果当前显示的是返回按钮,且点击的不是返回按钮 if ($btnBack.Visibility -eq "Visible" -and $e.Source -ne $btnBack) { # 触发返回动画 $btnBack.BeginAnimation([System.Windows.Controls.Button]::MarginProperty, $backAnimation) $backAnimation.Completed.Add({ $btnBack.Visibility = "Hidden" $btnPopup.Visibility = "Visible" # 检查是否为窗口外点击,是则关闭窗口 $cursorPos = New-Object System.Drawing.Point [NativeMethods]::GetCursorPos([ref]$cursorPos) $hwnd = [NativeMethods]::WindowFromPoint($cursorPos) if ($hwnd -ne $window.Dispatcher.Invoke({$window.Handle})) { $window.Close() } }) $e.Handled = $true } }) # 引入Windows API用于判断窗口外点击 Add-Type @" using System; using System.Runtime.InteropServices; public class NativeMethods { [DllImport("user32.dll")] public static extern IntPtr WindowFromPoint(System.Drawing.Point point); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetCursorPos(out System.Drawing.Point lpPoint); } "@ # 监听窗口失活事件(处理窗口外点击) $window.Add_Deactivated({ if ($btnBack.Visibility -eq "Visible") { $cursorPos = New-Object System.Drawing.Point [NativeMethods]::GetCursorPos([ref]$cursorPos) $hwnd = [NativeMethods]::WindowFromPoint($cursorPos) if ($hwnd -ne $window.Dispatcher.Invoke({$window.Handle})) { # 触发返回动画后关闭窗口 $btnBack.BeginAnimation([System.Windows.Controls.Button]::MarginProperty, $backAnimation) $backAnimation.Completed.Add({ $window.Close() }) } } }) # 显示窗口 $window.ShowDialog()
C#实现示例(方便转换为PowerShell)
using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Animation; namespace WpfAnimatedButton { public partial class MainWindow : Window { private Button btnPopup; private Button btnBack; private DoubleAnimation popupAnimation; private DoubleAnimation backAnimation; public MainWindow() { InitializeComponent(); SetupWindow(); SetupControls(); SetupAnimations(); SetupEvents(); } private void SetupWindow() { Title = "动画按钮示例"; Width = 300; Height = 200; WindowStyle = WindowStyle.ToolWindow; AllowsTransparency = true; Background = System.Windows.Media.Brushes.Transparent; } private void SetupControls() { var grid = new Grid(); Content = grid; btnPopup = new Button { Content = "点击弹出", Width = 100, Height = 40, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top }; grid.Children.Add(btnPopup); btnBack = new Button { Content = "返回", Width = 100, Height = 40, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Top, Visibility = Visibility.Hidden }; grid.Children.Add(btnBack); } private void SetupAnimations() { popupAnimation = new DoubleAnimation { From = 0, To = 200, Duration = TimeSpan.FromSeconds(0.3) }; backAnimation = new DoubleAnimation { From = 200, To = 0, Duration = TimeSpan.FromSeconds(0.3) }; } private void SetupEvents() { btnPopup.Click += (s, e) => { btnPopup.BeginAnimation(Button.MarginProperty, popupAnimation); popupAnimation.Completed += (_, __) => { btnPopup.Visibility = Visibility.Hidden; btnBack.Visibility = Visibility.Visible; }; }; btnBack.Click += (s, e) => { btnBack.BeginAnimation(Button.MarginProperty, backAnimation); backAnimation.Completed += (_, __) => { btnBack.Visibility = Visibility.Hidden; btnPopup.Visibility = Visibility.Visible; }; }; PreviewMouseDown += (s, e) => { if (btnBack.Visibility == Visibility.Visible && e.Source != btnBack) { TriggerBackAnimationAndCloseIfNeeded(); e.Handled = true; } }; Deactivated += (s, e) => { if (btnBack.Visibility == Visibility.Visible) { Point cursorPos; NativeMethods.GetCursorPos(out cursorPos); var hwnd = NativeMethods.WindowFromPoint(cursorPos); if (hwnd != new WindowInteropHelper(this).Handle) { TriggerBackAnimationAndCloseIfNeeded(); } } }; } private void TriggerBackAnimationAndCloseIfNeeded() { btnBack.BeginAnimation(Button.MarginProperty, backAnimation); backAnimation.Completed += (_, __) => { btnBack.Visibility = Visibility.Hidden; btnPopup.Visibility = Visibility.Visible; // 判断是否需要关闭窗口 Point cursorPos; NativeMethods.GetCursorPos(out cursorPos); var hwnd = NativeMethods.WindowFromPoint(cursorPos); if (hwnd != new WindowInteropHelper(this).Handle) { Close(); } }; } private static class NativeMethods { [DllImport("user32.dll")] public static extern IntPtr WindowFromPoint(Point point); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetCursorPos(out Point lpPoint); } } }
内容的提问来源于stack exchange,提问作者PlayLORD-SysOp
相关产品推荐
相关产品推荐

