Files
Future/Future.Contract/Adorners/DrawerAdorner.cs
2025-08-30 17:19:57 +08:00

291 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace Future.Contract
{
public sealed class DrawerAdorner : Adorner
{
private VisualCollection mVisuals;
private Border mRoot;
private AdornerLayer? mLayer;
private Size mSize;
private bool mIsShowed = false;
private bool mIsClosed = false;
private Func<bool> mApplyCloseCallback;
public DrawerAdorner(UIElement adornedElement) : base(adornedElement)
{
mVisuals = new VisualCollection(this);
mRoot = new Border();
mVisuals.Add(mRoot);
this.mLayer = AdornerLayer.GetAdornerLayer(adornedElement);
//注意:开场动画必须在此处执行
this.Loaded += (ss, ee) => this.StartAnimationIn();
//this.Unloaded += (ss, ee) => this.StartAnimationOut();
}
protected override int VisualChildrenCount => mVisuals.Count;
protected override Visual GetVisualChild(int index) => mVisuals[index];
protected override Size ArrangeOverride(Size finalSize)
{
mSize = finalSize;
mRoot.Arrange(new Rect(new Point(), finalSize));
return finalSize;
}
#pragma warning disable CS0628 // 在密封类型中声明了新的保护成员
protected void SetHandleVisibility(Visibility visibility)
#pragma warning restore CS0628 // 在密封类型中声明了新的保护成员
{
if (this.mRoot.Child != null && this.mRoot.Child is DrawerTemplate)
{
DrawerTemplate template = this.mRoot.Child as DrawerTemplate;
template.HandleVisibility = visibility;
}
}
public void ShowWithTemplate(DrawerTemplate? template, FrameworkElement view, string title, Func<bool>? applyCloseCallback = null)
{
if (template == null && view == null) return;
view?.ClearValue(FrameworkElement.WidthProperty);
view?.ClearValue(FrameworkElement.HeightProperty);
this.mApplyCloseCallback = applyCloseCallback;
if (template != null)
{
this.mRoot.Child = template;
template.Title = title;
template.Content = view;
template.ApplyCloseDrawer += () => this.Close();
}
else this.mRoot.Child = view;
//隐藏同一个目标上最上层的相同装饰器的半透明区域(注意需要防止这个逻辑执行2次)
var adorners = this.mLayer?.GetAdorners(this.AdornedElement);
if (adorners != null && !this.mIsShowed)//保证即使更换显示内容也不会重复处理关闭按钮的隐藏
{
for (int i = adorners.Length - 1; i >= 0; i--)
if (adorners[i] is DrawerAdorner)
{
((DrawerAdorner)adorners[i]).SetHandleVisibility(Visibility.Collapsed);
break;
}
}
if (!this.mIsShowed) this.mLayer?.Add(this);//完成显示工作
//注意入场动画在OnLoad事件的响应时调用
this.mIsShowed = true;
this.mIsClosed = false;
}
public static DrawerAdorner ShowInVisual(UIElement adornedElement, DrawerTemplate? template, FrameworkElement view, string title, Func<bool>? applyCloseCallback = null)
{
DrawerAdorner drawer = new DrawerAdorner(adornedElement);
drawer.ShowWithTemplate(template?.Clone(), view, title, applyCloseCallback);
//drawer.ShowWithTemplate(template, view, title, applyCloseCallback);
return drawer;
}
public static DrawerAdorner ShowInTarget(string targetName, DrawerTemplate? template, FrameworkElement view, string title, Func<bool>? applyCloseCallback = null)
{
if (LayerOwners.ContainsKey(targetName))
return ShowInVisual(LayerOwners[targetName], template, view, title, applyCloseCallback);
else return null;
}
public void ShowOnlyView(FrameworkElement view) => this.ShowWithTemplate(null, view, null, null);
public static DrawerAdorner ShowView(UIElement adornedElement, FrameworkElement view)
{
DrawerAdorner drawer = new DrawerAdorner(adornedElement);
drawer.ShowOnlyView(view);
return drawer;
}
public void CloseDrawer()
{
//强制清空请求关闭委托
this.mApplyCloseCallback = null;
this.Close();
}
private async void Close()
{
if (this.mIsClosed) return;//如果已经关闭了就不要重复执行了,会导致层叠问题
var sign = this.mApplyCloseCallback?.Invoke();
if (sign == false) return;
#region 使
float seconds = 0.3f;
var sb = new Storyboard();
//var offset = mRoot?.ActualWidth ?? 0;
//var animation = new ThicknessAnimation
//{
// Duration = new Duration(TimeSpan.FromSeconds(seconds)),
// From = new Thickness(0),
// To = new Thickness(-offset, 0, offset, 0),
//};
var animation = BuildAnimation(seconds, true);
Storyboard.SetTargetProperty(animation, new PropertyPath("Margin"));
sb.Children.Add(animation);
sb.Begin(mRoot);
await Task.Delay((int)(seconds * 1000));
#endregion
//开始卸载装饰器
this.SetHandleVisibility(Visibility.Collapsed);
this.mRoot.Child = null; //清除模板视图的引用
this.mLayer?.Remove(this); //完成关闭装饰器工作
//显示同一个目标上最上层的相同装饰器的半透明区域
var adorners = this.mLayer?.GetAdorners(this.AdornedElement);
if (adorners != null)
{
for (int i = adorners.Length - 1; i >= 0; i--)
if (adorners[i] is DrawerAdorner)
{
((DrawerAdorner)adorners[i]).SetHandleVisibility(Visibility.Visible);
break;
}
}
this.mIsClosed = true;
this.mIsShowed = false;
}
private ThicknessAnimation BuildAnimation(float seconds = 0.3f, bool inOrOut = false)
{
var wOffset = mRoot?.ActualWidth ?? 0;
var hOffset = mRoot?.ActualHeight ?? 0;
Thickness from = new Thickness(-wOffset, 0, wOffset, 0);
Thickness to = new Thickness(0);
DrawerTemplate template = this.mRoot?.Child as DrawerTemplate;
if (template != null)
{
switch (template.Dock)
{
case Dock.Top:
from = new Thickness(0, -hOffset, 0, hOffset);
break;
case Dock.Bottom:
from = new Thickness(0, hOffset, 0, -hOffset);
break;
case Dock.Right:
from = new Thickness(wOffset, 0, -wOffset, 0);
break;
default: break;
}
}
//如果是退出动画则需要对调From与To两个节点
if (inOrOut)
{
Thickness thickness = from;
from = to;
to = thickness;
}
return new ThicknessAnimation()
{
Duration = new Duration(TimeSpan.FromSeconds(seconds)),
From = from,
To = to
};
}
private async void StartAnimationIn(float seconds = 0.3f)
{
var sb = new Storyboard();
//var offset = mRoot?.ActualWidth ?? 0;
//var animation = new ThicknessAnimation
//{
// Duration = new Duration(TimeSpan.FromSeconds(seconds)),
// From = new Thickness(-offset, 0, offset, 0),
// To = new Thickness(0)
//};
var animation = BuildAnimation(seconds);
Storyboard.SetTargetProperty(animation, new PropertyPath("Margin"));
sb.Children.Add(animation);
sb.Begin(mRoot);
await Task.Delay((int)(seconds * 1000));
}
private async void StartAnimationOut(float seconds = 0.3f)
{
var sb = new Storyboard();
var offset = mRoot?.ActualWidth ?? 0;
var animation = new ThicknessAnimation
{
Duration = new Duration(TimeSpan.FromSeconds(seconds)),
From = new Thickness(0),
To = new Thickness(-offset, 0, offset, 0),
};
Storyboard.SetTargetProperty(animation, new PropertyPath("Margin"));
sb.Children.Add(animation);
sb.Begin(mRoot);
await Task.Delay((int)(seconds * 1000));
this.mRoot.Child = null;
this.mLayer?.Remove(this);//完成关闭工作
//显示同一个目标上最上层的相同装饰器的半透明区域
var adorners = this.mLayer?.GetAdorners(this.AdornedElement);
if (adorners != null)
{
for (int i = adorners.Length - 1; i >= 0; i--)
if (adorners[i] is DrawerAdorner)
{
((DrawerAdorner)adorners[i]).SetHandleVisibility(Visibility.Visible);
break;
}
}
}
#region
private static Dictionary<string, UIElement> LayerOwners = new Dictionary<string, UIElement>();
public static string GetLayerName(DependencyObject obj)
{
return (string)obj.GetValue(LayerNameProperty);
}
public static void SetLayerName(DependencyObject obj, string value)
{
obj.SetValue(LayerNameProperty, value);
}
// Using a DependencyProperty as the backing store for LayerName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LayerNameProperty =
DependencyProperty.RegisterAttached("LayerName", typeof(string), typeof(DrawerAdorner), new PropertyMetadata(null, (s, e) =>
{
//将抽屉拥有者注册到静态区
var layerOwner = s as UIElement;
if (s == null) return;
if (e.OldValue != null && LayerOwners.ContainsKey((string)e.OldValue))
LayerOwners.Remove((string)e.OldValue);
if (e.NewValue != null && layerOwner != null)
{
if (LayerOwners.ContainsKey((string)e.NewValue))
LayerOwners.Remove((string)e.NewValue);
LayerOwners.Add((string)e.NewValue, layerOwner);
DependencyObject d = s;
while (d != null)
{
if (d is Window) break;
var f = d as FrameworkElement;
if (f != null && f is not Window) d = f.Parent;
}
if (d is Window)
{
Window w = (Window)d;
w.Unloaded += (ss, ee) =>
LayerOwners.Remove((string)e.NewValue);
}
}
}));
#endregion
}
}