You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 06:05:03