using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Effects; namespace _29WxOrderWpf.ToolCtrls; public enum ToastType { Info, Success, Warning, Error } /// /// 在指定 Window 内显示自动消失的提示框(Toast) /// 用法:ToastHelper.Show(window, "保存成功", ToastType.Success); /// public static class ToastHelper { // 每个 Window 对应一个提示容器,用于多个提示堆叠 private static readonly ConditionalWeakTable Hosts = new(); private static SolidColorBrush msgBrush; static ToastHelper() { msgBrush = Application.Current.Resources["pageColorBrush"] as SolidColorBrush; msgBrush = Application.Current.Resources["pageColorBrush"] as SolidColorBrush; } /// 要展示提示的窗口 /// 提示内容 /// 类型(决定背景色) /// 停留时长(毫秒,不含淡入淡出) public static void Show(Window owner, string message, ToastType type = ToastType.Info, int durationMs = 2000) { if (owner == null || string.IsNullOrEmpty(message)) return; // 允许在非 UI 线程调用 if (!owner.Dispatcher.CheckAccess()) { owner.Dispatcher.BeginInvoke(new Action(() => Show(owner, message, type, durationMs))); return; } // 窗口还没加载完,等加载后再显示 if (!owner.IsLoaded) { RoutedEventHandler handler = null; handler = (s, e) => { owner.Loaded -= handler; Show(owner, message, type, durationMs); }; owner.Loaded += handler; return; } if (owner.Content is not UIElement content) return; var layer = AdornerLayer.GetAdornerLayer(content); if (layer == null) return; if (!Hosts.TryGetValue(owner, out var host)) { host = new ToastAdorner(content); layer.Add(host); Hosts.Add(owner, host); } var toast = CreateToast(message, type); host.Panel.Children.Add(toast); // 淡入 -> 停留 -> 淡出 var fadeIn = TimeSpan.FromMilliseconds(250); var fadeOut = TimeSpan.FromMilliseconds(400); var hold = TimeSpan.FromMilliseconds(Math.Max(durationMs, 0)); var anim = new DoubleAnimationUsingKeyFrames(); anim.KeyFrames.Add(new LinearDoubleKeyFrame(0, TimeSpan.Zero)); anim.KeyFrames.Add(new LinearDoubleKeyFrame(1, fadeIn)); anim.KeyFrames.Add(new LinearDoubleKeyFrame(1, fadeIn + hold)); anim.KeyFrames.Add(new LinearDoubleKeyFrame(0, fadeIn + hold + fadeOut)); anim.Completed += (s, e) => { host.Panel.Children.Remove(toast); if (host.Panel.Children.Count == 0) { layer.Remove(host); Hosts.Remove(owner); } }; toast.BeginAnimation(UIElement.OpacityProperty, anim); } public static void Show(string message, ToastType type = ToastType.Info, int durationMs = 2000) { Window window = Application.Current.Windows .OfType() .FirstOrDefault(w => w.IsActive) ?? Application.Current.MainWindow; Show(window, message, type, durationMs); } private static Border CreateToast(string message, ToastType type) { var color = type switch { ToastType.Success => Color.FromRgb(0x2E, 0x9E, 0x5B), ToastType.Warning => Color.FromRgb(0xE6, 0x9A, 0x1A), ToastType.Error => Color.FromRgb(0xD9, 0x4A, 0x4A), _ => msgBrush.Color,//Color.FromRgb(0x40, 0x40, 0x40), }; return new Border { Background = new SolidColorBrush(color), CornerRadius = new CornerRadius(6), Padding = new Thickness(18, 10, 18, 10), Margin = new Thickness(0, 0, 0, 8), Opacity = 0, MaxWidth = 480, Effect = new DropShadowEffect { BlurRadius = 12, ShadowDepth = 2, Opacity = 0.3, Color = Colors.Black }, Child = new TextBlock { Text = message, Foreground = Brushes.White, FontSize = 14, TextWrapping = TextWrapping.Wrap } }; } /// 覆盖在窗口内容上方的提示容器,顶部居中 private sealed class ToastAdorner : Adorner { public StackPanel Panel { get; } public ToastAdorner(UIElement adornedElement) : base(adornedElement) { IsHitTestVisible = false; // 不拦截鼠标事件 Panel = new StackPanel { Orientation = Orientation.Vertical }; AddVisualChild(Panel); } protected override int VisualChildrenCount => 1; protected override Visual GetVisualChild(int index) => Panel; protected override Size MeasureOverride(Size constraint) { Panel.Measure(constraint); return AdornedElement.RenderSize; } protected override Size ArrangeOverride(Size finalSize) { var size = Panel.DesiredSize; double x = (finalSize.Width - size.Width) / 2; Panel.Arrange(new Rect(new Point(Math.Max(x, 0), 30), size)); // 距顶部 30 return finalSize; } } }