Files
Shrlalgo.RvKits/NeoUI/Melskin/Controls/Notification/Notification.cs

112 lines
4.2 KiB
C#
Raw Normal View History

2026-01-02 17:30:30 +08:00
namespace VariaStudio.Controls;
2025-08-20 12:10:13 +08:00
/// <summary>
/// 通知管理器类,提供显示系统通知的方法。
/// 该类主要用于在应用程序中方便地创建和显示不同类型的通知。
/// </summary>
2025-12-23 21:35:54 +08:00
public static class Notification
2025-08-20 12:10:13 +08:00
{
// 数据结构现在存储 Notification 模型,而不是 Window
2025-12-23 21:35:54 +08:00
private static readonly Dictionary<NotificationPlacement, List<Tuple<NotificationModel, NotificationView>>> ActiveNotifications =
2025-08-20 12:10:13 +08:00
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(() =>
{
2025-12-23 21:35:54 +08:00
var model = new NotificationModel(title, message, type, TimeSpan.FromSeconds(durationSeconds));
2025-08-20 12:10:13 +08:00
// 修改这里:在创建 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)
{
2025-12-23 21:35:54 +08:00
ActiveNotifications[placement].Add(new Tuple<NotificationModel, NotificationView>(view.Model, view));
2025-08-20 12:10:13 +08:00
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坐标
}
}
}