功能更新
This commit is contained in:
77
Melskin/Controls/Toast/WindowHost.cs
Normal file
77
Melskin/Controls/Toast/WindowHost.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
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
|
||||
Reference in New Issue
Block a user