using BPASmartClient.CustomResource.UserControls.Enum; using BPASmartClient.CustomResource.UserControls.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace BPASmartClient.CustomResource.UserControls { /// /// Toast.xaml 的交互逻辑 /// public partial class Toast : UserControl { private Window owner = null; private Popup popup = null; private DispatcherTimer timer = null; private Toast() { InitializeComponent(); this.DataContext = this; } private Toast(Window owner, string message, ToastOptions options = null) { Message = message; InitializeComponent(); if (options != null) { if (options.ToastWidth != 0) ToastWidth = options.ToastWidth; if (options.ToastHeight != 0) ToastHeight = options.ToastHeight; if (options.TextWidth != 0) TextWidth = options.TextWidth; Icon = options.Icon; Location = options.Location; Time = options.Time; Closed += options.Closed; Click += options.Click; Background = options.Background; Foreground = options.Foreground; FontStyle = options.FontStyle; FontStretch = options.FontStretch; FontSize = options.FontSize; FontFamily = options.FontFamily; FontWeight = options.FontWeight; IconSize = options.IconSize; BorderBrush = options.BorderBrush; BorderThickness = options.BorderThickness; HorizontalContentAlignment = options.HorizontalContentAlignment; VerticalContentAlignment = options.VerticalContentAlignment; CornerRadius = options.CornerRadius; ToastMargin = options.ToastMargin; IconForeground = options.IconForeground; } this.DataContext = this; if (owner == null) { this.owner = Application.Current.MainWindow; } else { this.owner = owner; } this.owner.Closed += Owner_Closed; } private void Owner_Closed(object sender, EventArgs e) { this.Close(); } public static void Show(string msg, ToastOptions options = null) { var toast = new Toast(null, msg, options); int time = toast.Time; ShowToast(toast, time); } public static void Show(Window owner, string msg, ToastOptions options = null) { var toast = new Toast(owner, msg, options); int time = toast.Time; ShowToast(toast, time); } private static void ShowToast(Toast toast, int time) { toast.popup = null; Application.Current.Dispatcher.Invoke(new Action(() => { toast.popup = new Popup { PopupAnimation = PopupAnimation.Fade, AllowsTransparency = true, StaysOpen = true, Placement = PlacementMode.Left, IsOpen = false, Child = toast, MinWidth = toast.MinWidth, MaxWidth = toast.MaxWidth, MinHeight = toast.MinHeight, MaxHeight = toast.MaxHeight, }; if (toast.ToastWidth != 0) { toast.popup.Width = toast.ToastWidth; } if (toast.ToastHeight != 0) { toast.popup.Height = toast.ToastHeight; } toast.popup.PlacementTarget = GetPopupPlacementTarget(toast); //为 null 则 Popup 定位相对于屏幕的左上角; toast.owner.LocationChanged += toast.UpdatePosition; toast.owner.SizeChanged += toast.UpdatePosition; toast.popup.Closed += Popup_Closed; //SetPopupOffset(toast.popup, toast); //toast.UpdatePosition(toast, null); toast.popup.IsOpen = true; //先显示出来以确定宽高; SetPopupOffset(toast.popup, toast); //toast.UpdatePosition(toast, null); toast.popup.IsOpen = false; //先关闭再打开来刷新定位; toast.popup.IsOpen = true; })); toast.timer = new DispatcherTimer(); toast.timer.Tick += (sender, e) => { toast.popup.IsOpen = false; toast.owner.LocationChanged -= toast.UpdatePosition; toast.owner.SizeChanged -= toast.UpdatePosition; }; toast.timer.Interval = new TimeSpan(0, 0, 0, 0, time); toast.timer.Start(); } private void UpdatePosition(object sender, EventArgs e) { var up = typeof(Popup).GetMethod("UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (up == null || popup == null) { return; } SetPopupOffset(popup, this); up.Invoke(popup, null); } private static void Popup_Closed(object sender, EventArgs e) { Popup popup = sender as Popup; if (popup == null) { return; } Toast toast = popup.Child as Toast; if (toast == null) { return; } toast.RaiseClosed(e); } private void UserControl_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (e.ClickCount == 1) { RaiseClick(e); } } /// /// 获取定位目标 /// /// Toast 对象 /// 容器或null private static UIElement GetPopupPlacementTarget(Toast toast) { switch (toast.Location) { case ToastLocation.ScreenCenter: case ToastLocation.ScreenLeft: case ToastLocation.ScreenRight: case ToastLocation.ScreenTopLeft: case ToastLocation.ScreenTopCenter: case ToastLocation.ScreenTopRight: case ToastLocation.ScreenBottomLeft: case ToastLocation.ScreenBottomCenter: case ToastLocation.ScreenBottomRight: return null; } return toast.owner; } private static void SetPopupOffset(Popup popup, Toast toast) { double winTitleHeight = SystemParameters.CaptionHeight; //标题高度为22; double owner_width = toast.owner.ActualWidth; double owner_height = toast.owner.ActualHeight - winTitleHeight; if (popup.PlacementTarget == null) { owner_width = SystemParameters.WorkArea.Size.Width; owner_height = SystemParameters.WorkArea.Size.Height; } double popupWidth = (popup.Child as FrameworkElement)?.ActualWidth ?? 0; //Popup 宽高为其 Child 的宽高; double popupHeight = (popup.Child as FrameworkElement)?.ActualHeight ?? 0; double x = SystemParameters.WorkArea.X; double y = SystemParameters.WorkArea.Y; Thickness margin = toast.ToastMargin; /*[dlgcy] 38 和 16 两个数字的猜测: * PlacementTarget 为 Window 时,当 Placement 为 Bottom 时,Popup 上边缘与 Window 的下边缘的距离为 38; * 当 Placement 为 Right 时,Popup 左边缘与 Window 的右边缘的距离为 16。 */ double bottomDistance = 38; double rightDistance = 16; //上面创建时 Popup 的 Placement 为 PlacementMode.Left; switch (toast.Location) { case ToastLocation.OwnerLeft: //容器左中间 popup.HorizontalOffset = popupWidth + margin.Left; popup.VerticalOffset = (owner_height - popupHeight - winTitleHeight) / 2; break; case ToastLocation.ScreenLeft: //屏幕左中间 popup.HorizontalOffset = popupWidth + x + margin.Left; popup.VerticalOffset = (owner_height - popupHeight) / 2 + y; break; case ToastLocation.OwnerRight: //容器右中间 popup.HorizontalOffset = owner_width - rightDistance - margin.Right; popup.VerticalOffset = (owner_height - popupHeight - winTitleHeight) / 2; break; case ToastLocation.ScreenRight: //屏幕右中间 popup.HorizontalOffset = owner_width + x - margin.Right; popup.VerticalOffset = (owner_height - popupHeight) / 2 + y; break; case ToastLocation.OwnerTopLeft: //容器左上角 popup.HorizontalOffset = popupWidth + margin.Left; popup.VerticalOffset = margin.Top; break; case ToastLocation.ScreenTopLeft: //屏幕左上角 popup.HorizontalOffset = popupWidth + x + margin.Left; popup.VerticalOffset = margin.Top; break; case ToastLocation.OwnerTopCenter: //容器上中间 popup.HorizontalOffset = popupWidth + (owner_width - popupWidth - rightDistance) / 2; popup.VerticalOffset = margin.Top; break; case ToastLocation.ScreenTopCenter: //屏幕上中间 popup.HorizontalOffset = popupWidth + (owner_width - popupWidth) / 2 + x; popup.VerticalOffset = y + margin.Top; break; case ToastLocation.OwnerTopRight: //容器右上角 popup.HorizontalOffset = owner_width - rightDistance - margin.Right; popup.VerticalOffset = margin.Top; break; case ToastLocation.ScreenTopRight: //屏幕右上角 popup.HorizontalOffset = owner_width + x - margin.Right; popup.VerticalOffset = y + margin.Top; break; case ToastLocation.OwnerBottomLeft: //容器左下角 //popup.HorizontalOffset = popupWidth; //popup.VerticalOffset = owner_height - popupHeight - winTitleHeight; popup.Placement = PlacementMode.Bottom; popup.HorizontalOffset = margin.Left; popup.VerticalOffset = -(bottomDistance + popupHeight + margin.Bottom); break; case ToastLocation.ScreenBottomLeft: //屏幕左下角 popup.HorizontalOffset = popupWidth + x + margin.Left; popup.VerticalOffset = owner_height - popupHeight + y - margin.Bottom; break; case ToastLocation.OwnerBottomCenter: //容器下中间 //popup.HorizontalOffset = popupWidth + (owner_width - popupWidth - rightDistance) / 2; //popup.VerticalOffset = owner_height - popupHeight - winTitleHeight; popup.Placement = PlacementMode.Bottom; popup.HorizontalOffset = (owner_width - popupWidth - rightDistance) / 2; popup.VerticalOffset = -(bottomDistance + popupHeight + margin.Bottom); break; case ToastLocation.ScreenBottomCenter: //屏幕下中间 popup.HorizontalOffset = popupWidth + (owner_width - popupWidth) / 2 + x; popup.VerticalOffset = owner_height - popupHeight + y - margin.Bottom; break; case ToastLocation.OwnerBottomRight: //容器右下角 //popup.HorizontalOffset = popupWidth + (owner_width - popupWidth - rightDistance); //popup.VerticalOffset = owner_height - popupHeight - winTitleHeight; popup.Placement = PlacementMode.Bottom; popup.HorizontalOffset = owner_width - popupWidth - rightDistance - margin.Right; popup.VerticalOffset = -(bottomDistance + popupHeight + margin.Bottom); break; case ToastLocation.ScreenBottomRight: //屏幕右下角 popup.HorizontalOffset = owner_width + x - margin.Right; popup.VerticalOffset = owner_height - popupHeight + y - margin.Bottom; break; case ToastLocation.ScreenCenter: //屏幕正中间 popup.HorizontalOffset = popupWidth + (owner_width - popupWidth) / 2 + x; popup.VerticalOffset = (owner_height - popupHeight) / 2 + y; break; case ToastLocation.OwnerCenter: //容器正中间 case ToastLocation.Default: //popup.HorizontalOffset = popupWidth + (owner_width - popupWidth - rightDistance) / 2; //popup.VerticalOffset = (owner_height - popupHeight - winTitleHeight) / 2; popup.Placement = PlacementMode.Center; popup.HorizontalOffset = -rightDistance / 2; popup.VerticalOffset = -bottomDistance / 2; break; } } public void Close() { if (timer != null) { timer.Stop(); timer = null; } popup.IsOpen = false; owner.LocationChanged -= UpdatePosition; owner.SizeChanged -= UpdatePosition; } private event EventHandler Closed; private void RaiseClosed(EventArgs e) { Closed?.Invoke(this, e); } private event EventHandler Click; private void RaiseClick(EventArgs e) { Click?.Invoke(this, e); } #region 依赖属性 private string Message { get { return (string)GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } private static readonly DependencyProperty MessageProperty = DependencyProperty.Register("Message", typeof(string), typeof(Toast), new PropertyMetadata(string.Empty)); private CornerRadius CornerRadius { get { return (CornerRadius)GetValue(CornerRadiusProperty); } set { SetValue(CornerRadiusProperty, value); } } private static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(Toast), new PropertyMetadata(new CornerRadius(5))); private double IconSize { get { return (double)GetValue(IconSizeProperty); } set { SetValue(IconSizeProperty, value); } } private static readonly DependencyProperty IconSizeProperty = DependencyProperty.Register("IconSize", typeof(double), typeof(Toast), new PropertyMetadata(26.0)); private new Brush BorderBrush { get { return (Brush)GetValue(BorderBrushProperty); } set { SetValue(BorderBrushProperty, value); } } private static new readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(Toast), new PropertyMetadata((Brush)new BrushConverter().ConvertFromString("#FFFFFF"))); private new Thickness BorderThickness { get { return (Thickness)GetValue(BorderThicknessProperty); } set { SetValue(BorderThicknessProperty, value); } } private static new readonly DependencyProperty BorderThicknessProperty = DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(Toast), new PropertyMetadata(new Thickness(0))); private new Brush Background { get { return (Brush)GetValue(BackgroundProperty); } set { SetValue(BackgroundProperty, value); } } private static new readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(Toast), new PropertyMetadata((Brush)new BrushConverter().ConvertFromString("#2E2929"))); private new HorizontalAlignment HorizontalContentAlignment { get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); } set { SetValue(HorizontalContentAlignmentProperty, value); } } private static new readonly DependencyProperty HorizontalContentAlignmentProperty = DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(Toast), new PropertyMetadata(HorizontalAlignment.Left)); private new VerticalAlignment VerticalContentAlignment { get { return (VerticalAlignment)GetValue(VerticalContentAlignmentProperty); } set { SetValue(VerticalContentAlignmentProperty, value); } } private static new readonly DependencyProperty VerticalContentAlignmentProperty = DependencyProperty.Register("VerticalContentAlignment", typeof(VerticalAlignment), typeof(Toast), new PropertyMetadata(VerticalAlignment.Center)); private double ToastWidth { get { return (double)GetValue(ToastWidthProperty); } set { SetValue(ToastWidthProperty, value); } } private static readonly DependencyProperty ToastWidthProperty = DependencyProperty.Register("ToastWidth", typeof(double), typeof(Toast), new PropertyMetadata(0.0)); private double ToastHeight { get { return (double)GetValue(ToastHeightProperty); } set { SetValue(ToastHeightProperty, value); } } private static readonly DependencyProperty ToastHeightProperty = DependencyProperty.Register("ToastHeight", typeof(double), typeof(Toast), new PropertyMetadata(0.0)); private ToastIcons Icon { get { return (ToastIcons)GetValue(IconProperty); } set { SetValue(IconProperty, value); } } private static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ToastIcons), typeof(Toast), new PropertyMetadata(ToastIcons.None)); private int Time { get { return (int)GetValue(TimeProperty); } set { SetValue(TimeProperty, value); } } private static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof(int), typeof(Toast), new PropertyMetadata(2000)); private ToastLocation Location { get { return (ToastLocation)GetValue(LocationProperty); } set { SetValue(LocationProperty, value); } } private static readonly DependencyProperty LocationProperty = DependencyProperty.Register("Location", typeof(ToastLocation), typeof(Toast), new PropertyMetadata(ToastLocation.Default)); public double TextWidth { get { return (double)GetValue(TextWidthProperty); } set { SetValue(TextWidthProperty, value); } } public static readonly DependencyProperty TextWidthProperty = DependencyProperty.Register("TextWidth", typeof(double), typeof(Toast), new PropertyMetadata(double.NaN)); public Thickness ToastMargin { get { return (Thickness)GetValue(ToastMarginProperty); } set { SetValue(ToastMarginProperty, value); } } public static readonly DependencyProperty ToastMarginProperty = DependencyProperty.Register("ToastMargin", typeof(Thickness), typeof(Toast), new PropertyMetadata(new Thickness(0))); private Brush IconForeground { get { return (Brush)GetValue(IconForegroundProperty); } set { SetValue(IconForegroundProperty, value); } } private static readonly DependencyProperty IconForegroundProperty = DependencyProperty.Register("IconForeground", typeof(Brush), typeof(Toast), new PropertyMetadata((Brush)new BrushConverter().ConvertFromString("#00D91A"))); #endregion } }