Files
Shrlalgo.RvKits/Melskin/Controls/Notification/Notification.cs
2026-02-17 22:17:13 +08:00

112 lines
4.2 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.
namespace Melskin.Controls;
/// <summary>
/// 通知管理器类,提供显示系统通知的方法。
/// 该类主要用于在应用程序中方便地创建和显示不同类型的通知。
/// </summary>
public static class Notification
{
// 数据结构现在存储 Notification 模型,而不是 Window
private static readonly Dictionary<NotificationPlacement, List<Tuple<NotificationModel, NotificationView>>> ActiveNotifications =
new()
{
{ NotificationPlacement.TopRight, [] },
{ NotificationPlacement.TopLeft, [] },
{ NotificationPlacement.BottomRight, [] },
{ NotificationPlacement.BottomLeft, [] }
};
private static readonly object Lock = new();
/// <summary>
/// 显示系统通知。
/// </summary>
/// <param name="title">通知的标题。</param>
/// <param name="message">通知的内容消息。</param>
/// <param name="type">通知的类型,默认为信息类型。</param>
/// <param name="placement">通知在屏幕上的位置,默认为右上角。</param>
/// <param name="durationSeconds">通知显示的持续时间默认为5秒。</param>
public static void Show(string title, string message, NotificationType type = NotificationType.Info,
NotificationPlacement placement = NotificationPlacement.TopRight, int durationSeconds = 5)
{
Application.Current.Dispatcher.Invoke(() =>
{
var model = new NotificationModel(title, message, type, TimeSpan.FromSeconds(durationSeconds));
// 修改这里:在创建 view 时传入 placement
var view = new NotificationView(model, placement);
// 后续代码完全不变
view.Ready += (v) => OnNotificationReady(v, placement);
view.Closed += (_, _) => OnNotificationClosed(view, placement);
view.Show();
});
}
private static void OnNotificationReady(NotificationView view, NotificationPlacement placement)
{
lock (Lock)
{
ActiveNotifications[placement].Add(new Tuple<NotificationModel, NotificationView>(view.Model, view));
Reposition(placement);
}
}
private static void OnNotificationClosed(NotificationView view, NotificationPlacement placement)
{
lock (Lock)
{
var itemToRemove = ActiveNotifications[placement].FirstOrDefault(t => t.Item2 == view);
if (itemToRemove != null)
{
ActiveNotifications[placement].Remove(itemToRemove);
Reposition(placement);
}
}
}
private static void Reposition(NotificationPlacement placement)
{
var notifications = ActiveNotifications[placement];
if (!notifications.Any()) return;
var workArea = SystemParameters.WorkArea;
double currentTop;
var isTop = placement == NotificationPlacement.TopLeft || placement == NotificationPlacement.TopRight;
if (isTop)
{
currentTop = workArea.Top + 10;
}
else // Bottom
{
// 对于底部,我们需要先计算总高度
var totalHeight = notifications.Sum(t => t.Item2.ActualHeight + 10);
currentTop = workArea.Bottom - totalHeight;
}
foreach (var tuple in notifications)
{
var view = tuple.Item2;
// 在 `Ready` 事件之后ActualWidth/Height 已经是准确的了
var finalLeft = (placement == NotificationPlacement.TopRight || placement == NotificationPlacement.BottomRight)
? workArea.Right - view.ActualWidth - 10
: workArea.Left + 10;
// 检查这个窗口是否是新来的(通过透明度判断)
if (view.Opacity == 0)
{
// 如果是新的,命令它播放入场动画
view.AnimateIn(finalLeft, currentTop);
}
else
{
// 如果是已经存在的,命令它平滑移动到新位置
view.AnimateMove(currentTop);
}
currentTop += view.ActualHeight + 10; // 为下一个窗口计算Y坐标
}
}
}