namespace VariaStudio.Controls; /// /// 通知管理器类,提供显示系统通知的方法。 /// 该类主要用于在应用程序中方便地创建和显示不同类型的通知。 /// public static class Notification { // 数据结构现在存储 Notification 模型,而不是 Window private static readonly Dictionary>> ActiveNotifications = new() { { NotificationPlacement.TopRight, [] }, { NotificationPlacement.TopLeft, [] }, { NotificationPlacement.BottomRight, [] }, { NotificationPlacement.BottomLeft, [] } }; private static readonly object Lock = new(); /// /// 显示系统通知。 /// /// 通知的标题。 /// 通知的内容消息。 /// 通知的类型,默认为信息类型。 /// 通知在屏幕上的位置,默认为右上角。 /// 通知显示的持续时间(秒),默认为5秒。 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(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坐标 } } }