using System.Windows.Documents; namespace Melskin.Controls; #region Internal Implementation internal class WindowHost : IToastHost { private readonly StackPanel toastContainer; public WindowHost(Window window) { toastContainer = new StackPanel(); // Use a style to apply margin between toasts, which is a robust approach. var childStyle = new Style(typeof(ToastControl)); childStyle.Setters.Add(new Setter(FrameworkElement.MarginProperty, new Thickness(0, 8, 0, 8))); toastContainer.Resources.Add(typeof(ToastControl), childStyle); var adornerLayer = AdornerLayer.GetAdornerLayer(window.Content as Visual); if (adornerLayer == null) throw new InvalidOperationException("在指定的窗口中找不到 AdornerLayer。"); adornerLayer.Add(new ToastAdorner(window.Content as UIElement, toastContainer)); } public void AddToast(ToastControl toast) => toastContainer.Children.Add(toast); public void RemoveToast(ToastControl toast) => toastContainer.Children.Remove(toast); private class ToastAdorner : Adorner { private readonly UIElement child; // This is our StackPanel public ToastAdorner(UIElement? adornedElement, UIElement child) : base(adornedElement) { this.child = child; // Add the child to the visual tree. This is essential. AddVisualChild(this.child); } protected override int VisualChildrenCount => 1; protected override Visual GetVisualChild(int index) => child; // We override Measure to tell our child (StackPanel) how much space it has. protected override Size MeasureOverride(Size constraint) { // Give the child all the space it could possibly want. // It will then measure itself and its children, establishing its DesiredSize. child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); // The Adorner itself doesn't need to return a specific size, as it's a transparent overlay. return base.MeasureOverride(constraint); } // ArrangeOverride is where we do the actual positioning. protected override Size ArrangeOverride(Size finalSize) { // 'finalSize' is the full size of the element we are adorning (e.g., the window's content area). // '_child.DesiredSize' is the size our StackPanel reported it needs after being measured. // Calculate the X coordinate to center the child (StackPanel). var x = (finalSize.Width - child.DesiredSize.Width) / 2; // The Y coordinate is 0, placing it at the very top. double y = 0; // Create the final rectangle for our child, positioned correctly. var finalRect = new Rect(new Point(x, y), child.DesiredSize); // Arrange the child (our StackPanel) in the calculated centered rectangle. child.Arrange(finalRect); // The Adorner itself arranges to fill the entire space. return finalSize; } } } #endregion