Files
ShrlAlgoToolkit/Melskin/Controls/Notification/NotificationView.xaml.cs
2026-02-17 22:17:13 +08:00

120 lines
4.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace Melskin.Controls;
/// <summary>
/// 代表一个通知视图用于在应用程序中显示系统通知。此类继承自Window并根据提供的Notification模型和位置信息来初始化自身。支持抽屉式滑入和滑出动画效果。
/// </summary>
public partial class NotificationView
{
private readonly DispatcherTimer? closeTimer;
private readonly NotificationPlacement placement; // 新增一个字段来存储位置
/// <summary>
/// 当通知视图加载完毕并准备好显示时触发的事件。此事件允许外部代码在通知视图完全初始化后执行特定操作,例如调整其位置或开始动画。
/// </summary>
public event Action<NotificationView>? Ready;
/// <summary>
/// 获取与此视图关联的通知模型。该模型包含了通知的标题、消息内容、类型以及显示时长等信息。
/// </summary>
public NotificationModel Model { get; }
/// <summary>
/// 代表一个通知视图用于在应用程序中显示系统通知。该类继承自Window并根据提供的Notification模型和位置信息来初始化自身。
/// </summary>
public NotificationView(NotificationModel model, NotificationPlacement placement) // 构造函数接收placement
{
InitializeComponent();
this.Model = model;
this.DataContext = this.Model;
this.placement = placement; // 保存placement
this.Opacity = 0; // 初始时依然保持透明,防止在动画开始前闪烁
this.Loaded += (_, _) => Ready?.Invoke(this);
if (model.Duration <= TimeSpan.Zero) return;
closeTimer = new DispatcherTimer { Interval = model.Duration };
closeTimer.Tick += (_, _) => AnimateOut();
}
/// <summary>
/// 命令视图执行抽屉式滑入动画
/// </summary>
public void AnimateIn(double finalLeft, double finalTop)
{
// Manager已经计算好了最终位置我们只需执行动画
this.Top = finalTop; // Y坐标直接设定
// 根据位置判断是从左侧滑入还是右侧滑入
var isRightSide = placement == NotificationPlacement.TopRight ||
placement == NotificationPlacement.BottomRight;
var startLeft = isRightSide
? finalLeft + this.ActualWidth + 10 // 从右侧屏幕外开始
: finalLeft - this.ActualWidth - 10; // 从左侧屏幕外开始
// 在动画开始前,立即将窗口移动到屏幕外的起始位置
this.Left = startLeft;
// 创建滑入动画
var slideAnim = new DoubleAnimation(startLeft, finalLeft, TimeSpan.FromMilliseconds(400))
{
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
// 同时,让窗口从透明变为不透明,这是一个很好的辅助效果
var fadeAnim = new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(200));
// 应用动画
this.BeginAnimation(Window.LeftProperty, slideAnim);
this.BeginAnimation(Window.OpacityProperty, fadeAnim);
closeTimer?.Start();
}
/// <summary>
/// 命令视图执行抽屉式滑出动画
/// </summary>
public void AnimateOut()
{
closeTimer?.Stop();
// 计算滑出到屏幕外的目标位置
var isRightSide = placement is NotificationPlacement.TopRight or NotificationPlacement.BottomRight;
var endLeft = isRightSide ? this.Left + this.ActualWidth + 10 : this.Left - this.ActualWidth - 10;
// 创建滑出动画
var slideAnim = new DoubleAnimation(this.Left, endLeft, TimeSpan.FromMilliseconds(300))
{
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn }
};
// 同时淡出
var fadeAnim = new DoubleAnimation(this.Opacity, 0, TimeSpan.FromMilliseconds(250));
// 当动画完成时,关闭窗口
slideAnim.Completed += (_, _) => this.Close();
this.BeginAnimation(Window.LeftProperty, slideAnim);
this.BeginAnimation(Window.OpacityProperty, fadeAnim);
}
/// <summary>
/// 当其他通知关闭时,平滑地移动到新位置 (这个方法保持不变)
/// </summary>
public void AnimateMove(double newTop)
{
var moveAnim = new DoubleAnimation(this.Top, newTop, TimeSpan.FromMilliseconds(350))
{
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
this.BeginAnimation(Window.TopProperty, moveAnim);
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
AnimateOut();
}
}