291 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
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
 | 
						||
    }
 | 
						||
}
 |