将以往的代码复制到代码库

This commit is contained in:
于智纯
2025-08-30 17:19:57 +08:00
parent da46e0242d
commit 20ea70bf64
198 changed files with 10075 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace Future.Contract
{
/// <summary>
/// 拖动辅助装饰器
/// </summary>
public class DragDropAdorner : Adorner
{
#region
private static DragDropAdorner sAdorner; //装饰器:拖放过程中所有效果绘制使用的装饰器对象,结束后会释放
private static AdornerLayer sLayer; //装饰层:用于放置拖放效果装饰器,拖放过程结束后会清除对象引用
private static UIElement sOrigin; //拖起目标:被拖动的源对象
private static UIElement sTarget; //放置目标:在拖动过程中用于放置拖动源的目标,随拖动过程而变化
private static Canvas sCanvas; //拖放画布:整个拖放过程的完整区域,是拖起目标与放置目标的祖辈
private static Border sShadow; //鼠标拖起阴影:用于将托起目标尺寸按照相同缩放比缩放后变成阴影
private static Canvas sEffect; //鼠标拖动效果:用于提示放置的参考点位置,左、上、右、下,中。
private static Dock sPosition; //参考放置位置:为放置目标的位置提供参考,左、上、右、下,中。
/// <summary>
/// 放置的参考点
/// </summary>
public static Dock DropPosition { get => sPosition; }
#endregion
#region
/// <summary>
/// 设定完整拖放效果区域以及被拖动的对象,或是等效对象来起动拖放事件
/// </summary>
/// <param name="area">完整的拖放区域,必须是拖起目标与放置目标的共同祖先节点</param>
/// <param name="origin">被拖动的可视化目标,或是其等效目标</param>
/// <returns></returns>
public static DragDropEffects DoDragDrop(UIElement area, UIElement dragSource, object data, DragDropEffects allowedEffects)
{
DragDropEffects dragDropEffects = allowedEffects;
//TODO准备拖放生成阴影监听拖动执行区域中的拖动事件
dragDropEffects = DragDrop.DoDragDrop(dragSource, data, dragDropEffects);
//TODO拖拽操作完成释放装饰器以及过程中使用的所有资源
sLayer.Remove(sAdorner);
return dragDropEffects;
}
private static void OnDragEnter(UIElement target, DragEventArgs args)
{
sTarget= target;
target.MouseMove += Target_MouseMove;
//TODO更换目标对象监听目标对象的鼠标事件规制“阴影”与“效果”
}
private static void Target_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
//TODO计算鼠标位置移动效果与阴影
}
//public static void OnDragOver(UIElement target, DragEventArgs args)
//{
// //TODO计算鼠标位置移动效果与阴影
//}
public static void OnDragLeave(UIElement target, DragEventArgs args)
{
//TODO清除目标对象清除对鼠标事件的监听清除“阴影”与“效果”
target.MouseMove -= Target_MouseMove;
}
public static void DropCompleted(UIElement target, DragEventArgs args)
{
//TODO清除静态存储区中所使用过的数据清除装饰器
}
#endregion
private readonly VisualCollection mVisuals;
private DragDropAdorner(UIElement adornedElement) : base(adornedElement)
{
mVisuals = new VisualCollection(this);
sCanvas = new Canvas
{
Background=Brushes.Transparent
};
mVisuals.Add(sCanvas);
sLayer = AdornerLayer.GetAdornerLayer(adornedElement);
sLayer?.Add(this);
}
protected override int VisualChildrenCount => mVisuals.Count;
protected override Visual GetVisualChild(int index) => mVisuals[index];
protected override Size ArrangeOverride(Size finalSize)
{
//var bubbleSize = _bubble.DesiredSize;
//var location = new Point(finalSize.Width + 10, (finalSize.Height - bubbleSize.Height) / 2);
//_bubble.Arrange(new Rect(location, DesiredSize));
sCanvas.Arrange(new Rect(new Point(), finalSize));
return finalSize;
}
}
}

View File

@@ -0,0 +1,290 @@
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
}
}

View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
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;
namespace Future.Contract
{
[TemplatePart(Name = "CloseHandle", Type = typeof(Button))] //关闭按钮
//[TemplatePart(Name = "OpacityArea", Type = typeof(UIElement))] //透视区域
public class DrawerTemplate : ContentControl
{
private Button? _CloseHandle;
//private UIElement? _OpacityArea;
static DrawerTemplate()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DrawerTemplate), new FrameworkPropertyMetadata(typeof(DrawerTemplate)));
}
/// <summary>
/// 请求关闭事件
/// </summary>
public event Action? ApplyCloseDrawer;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this._CloseHandle = this.Template.FindName("CloseHandle", this) as Button;
//this._OpacityArea = this.Template.FindName("OpacityArea", this) as UIElement;
if (this._CloseHandle != null) this._CloseHandle.Click += (s, e) => this.ApplyCloseDrawer?.Invoke();
}
public DrawerTemplate Clone()
{
return new DrawerTemplate()
{
Background = this.Background,
Foreground = this.Foreground,
BorderBrush = this.BorderBrush,
BorderThickness = this.BorderThickness,
HorizontalAlignment = this.HorizontalAlignment,
VerticalAlignment = this.VerticalAlignment,
HorizontalContentAlignment = this.HorizontalContentAlignment,
VerticalContentAlignment = this.VerticalContentAlignment,
Dock = this.Dock,
FontFamily = this.FontFamily,
FontSize = this.FontSize,
FontStretch=this.FontStretch,
FontStyle = this.FontStyle,
FontWeight = this.FontWeight,
//Template = this.Template,
Style = this.Style
};
}
#region
/// <summary>
/// 抽屉标题
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(DrawerTemplate), new PropertyMetadata(""));
/// <summary>
/// 停靠方向
/// </summary>
public Dock Dock
{
get { return (Dock)GetValue(DockProperty); }
set { SetValue(DockProperty, value); }
}
// Using a DependencyProperty as the backing store for Dock. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DockProperty =
DependencyProperty.Register("Dock", typeof(Dock), typeof(DrawerTemplate), new PropertyMetadata(Dock.Right));
public Visibility HandleVisibility
{
get { return (Visibility)GetValue(HandleVisibilityProperty); }
set { SetValue(HandleVisibilityProperty, value); }
}
// Using a DependencyProperty as the backing store for HandleVisibility. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HandleVisibilityProperty =
DependencyProperty.Register("HandleVisibility", typeof(Visibility), typeof(DrawerTemplate), new PropertyMetadata(Visibility.Visible));
public double TitleHeight
{
get { return (double)GetValue(TitleHeightProperty); }
set { SetValue(TitleHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for TitleHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleHeightProperty =
DependencyProperty.Register("TitleHeight", typeof(double), typeof(DrawerTemplate), new PropertyMetadata(60.0));
#endregion
}
}

View File

@@ -0,0 +1,366 @@
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.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Future.Contract
{
/// <summary>
/// [Content]属性就是抽屉中要呈现的视图
/// <MyNamespace:FutureDrawer/>
/// </summary>
[TemplatePart(Name = "TitleBorder", Type = typeof(Border))]//执行透明度动画
[TemplatePart(Name = "ContentView", Type = typeof(Border))]//执行Margin动画
[TemplatePart(Name = "CloseHandle", Type = typeof(Button))]//执行透明度动化
[TemplatePart(Name = "DrawerRoot", Type = typeof(DockPanel))]//处理窗口最大化时的边距
public class FutureDrawer : ContentControl
{
static FutureDrawer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FutureDrawer), new FrameworkPropertyMetadata(typeof(FutureDrawer)));
}
#region
private const string TitleBorder = "TitleBorder";
private const string ContentView = "ContentView";
private const string CloseButton = "CloseHandle";
private const string DrawerRoot = "DrawerRoot";
private Border mContentView;
private Button mCloseButton;
private DockPanel mDrawerRoot;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
mContentView = GetTemplateChild(ContentView) as Border;
mCloseButton = GetTemplateChild(CloseButton) as Button;
mDrawerRoot = GetTemplateChild(DrawerRoot) as DockPanel;
if (mCloseButton != null) mCloseButton.Click += (s, e) =>
{
if(this.ApplyCloseDrawer != null && !this.ApplyCloseDrawer(this.Sign)) return;
else IsOpen = false;
};
this.Loaded += (s, e) => IsOpen = true;
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(FutureDrawer), new PropertyMetadata(false, (s, e) =>
{
var drawer = s as FutureDrawer;
if (e.NewValue is bool b && b)
{
drawer?.StartAnimationIn();
}
else
{
drawer?.StartAnimationOut();
}
}));
private async void StartAnimationIn(float seconds = 0.3f)
{
var sb = new Storyboard();
var offset = mDrawerRoot?.ActualWidth ?? 0;
var animation = new ThicknessAnimation
{
Duration = new Duration(TimeSpan.FromSeconds(seconds)),
From = new Thickness(-offset, 0, offset, 0),
To = new Thickness(0)
};
Storyboard.SetTargetProperty(animation, new PropertyPath("Margin"));
sb.Children.Add(animation);
sb.Begin(mDrawerRoot);
await Task.Delay((int)(seconds * 1000));
}
private async void StartAnimationOut(float seconds = 0.3f)
{
var sb = new Storyboard();
var offset = mDrawerRoot?.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(mDrawerRoot);
await Task.Delay((int)(seconds * 1000));
this.DrawerClosed?.Invoke();
}
#endregion
public Dock Dock
{
get { return (Dock)GetValue(DockProperty); }
set { SetValue(DockProperty, value); }
}
// Using a DependencyProperty as the backing store for Dock. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DockProperty =
DependencyProperty.Register("Dock", typeof(Dock), typeof(FutureDrawer), new PropertyMetadata(Dock.Right));
public double TitleHeight
{
get { return (double)GetValue(TitleHeightProperty); }
set { SetValue(TitleHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for TitleHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleHeightProperty =
DependencyProperty.Register("TitleHeight", typeof(double), typeof(FutureDrawer), new PropertyMetadata(40.0));
public double TitleIndent
{
get { return (double)GetValue(TitleIndentProperty); }
set { SetValue(TitleIndentProperty, value); }
}
// Using a DependencyProperty as the backing store for TitleIndent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleIndentProperty =
DependencyProperty.Register("TitleIndent", typeof(double), typeof(FutureDrawer), new PropertyMetadata(0.0));
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(FutureDrawer), new PropertyMetadata(""));
public int? Sign
{
get { return (int?)GetValue(SignProperty); }
set { SetValue(SignProperty, value); }
}
// Using a DependencyProperty as the backing store for Sign. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SignProperty =
DependencyProperty.Register("Sign", typeof(int?), typeof(FutureDrawer), new PropertyMetadata(0));
/// <summary>
/// 当抽屉准备关闭时发出的申请
/// </summary>
public event Func<int?, bool> ApplyCloseDrawer;
/// <summary>
/// 抽屉关闭后发出通知的委托
/// </summary>
public Action DrawerClosed;
/// <summary>
/// 默认构造:用来使用设计器构造抽屉模板
/// </summary>
public FutureDrawer() { }
public void Close()
{
this.StartAnimationOut();
}
/// <summary>
/// 内部构造:用来复刻当前抽屉模板创建新的抽屉来显示内容
/// </summary>
/// <param name="view">要呈现的视图</param>
/// <param name="title">抽屉的标题</param>
/// <param name="callBackSign">用于CallBack的识别码</param>
/// <param name="applyCloseCallBack">抽屉要关闭时的回调通知</param>
private FutureDrawer(UIElement view, string title, int? callBackSign = null, Func<int?, bool> applyCloseCallBack = null)
{
this.Content = view;
this.Title = title;
this.Sign = Sign;
this.ApplyCloseDrawer = applyCloseCallBack;
}
/// <summary>
/// 克隆当前抽屉的所有属性
/// </summary>
/// <returns></returns>
private FutureDrawer Clone(UIElement view, string title, int? callBackSign = null, Func<int?, bool> applyCloseCallBack = null)
{
FutureDrawer result = new FutureDrawer(view, title, callBackSign, applyCloseCallBack);
result.Dock = this.Dock;
result.TitleHeight = this.TitleHeight;
result.TitleIndent = this.TitleIndent;
result.Background = this.Background;
result.Foreground = this.Foreground;
result.Margin = this.Margin;
result.HorizontalAlignment = this.HorizontalAlignment;
result.VerticalAlignment = this.VerticalAlignment;
result.HorizontalContentAlignment = this.HorizontalContentAlignment;
result.VerticalContentAlignment = this.VerticalContentAlignment;
result.FontSize = this.FontSize;
return result;
}
private Window GetWindow(FrameworkElement origin)
{
if (origin == null) return null;
while (origin != null && origin is not Window) origin = origin.Parent as FrameworkElement;
return origin as Window;
}
/// <summary>
/// 抽屉使用的装饰器,如果存在则证明抽屉曾经打开过,不需要重复创建
/// </summary>
private DrawerAdorner _adorner;
/// <summary>
/// 将当前抽屉显示在目标UIElement【target】中
/// </summary>
/// <param name="target">要显示当前抽屉的UIElement元素</param>
public void ShowInTarget(FrameworkElement target, object content = null)
{
this.Content = content ?? this.Content;
Window window = GetWindow(target);
if (window != null)
{
if (window.WindowState == WindowState.Maximized) this.Padding = new Thickness(8);
window.StateChanged += (s, e) =>
{
Window w = s as Window;
if (w != null)
{
if (w.WindowState == WindowState.Maximized)
this.Padding = new Thickness(8);
else this.Padding = new Thickness(0);
}
};
}
//TODO:这里需要处理多个抽屉叠加的效果
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(target);
_adorner = _adorner ?? new DrawerAdorner(adornerLayer) { Child = this };
this.DrawerClosed = () => adornerLayer.Remove(_adorner);
adornerLayer.Add(_adorner);
}
/// <summary>
/// 在参照点【origin】所在的视觉树向上查找Window对象如果找到则在其可视范围内显示抽屉
/// </summary>
/// <param name="origin">参照点</param>
public void ShowInWindow(FrameworkElement origin, object content = null)
{
FrameworkElement target = origin;
while (target != null && !(target is Window)) target = target.Parent as FrameworkElement;
if (target is Window) this.ShowInTarget(target, content);
}
/// <summary>
/// 在参照点【origin】所在的视觉树内名称为【targetName】名称的UIElement可视范围内显示抽屉
/// </summary>
/// <param name="origin">参照点</param>
/// <param name="targetName">目标UIElement的名称</param>
public void ShowInNamedView(FrameworkElement origin, string targetName, object content = null)
{
FrameworkElement target = origin;
while (target != null && target.Parent != null && target.Name != targetName) target = target.Parent as FrameworkElement;
if (target != null && target.Name != targetName) target = target.FindName(targetName) as FrameworkElement;
if (target is UIElement && target.Name == targetName) this.ShowInTarget(target, content);
}
/// <summary>
/// 以当前抽屉为模板复制一个新的抽屉在目标UIElement的可视范围内显示抽屉
/// </summary>
/// <param name="view">抽屉中承载的内容</param>
/// <param name="target">承载抽屉的UI元素</param>
/// <param name="title">抽屉的标题</param>
/// <param name="callBackSign">请求关闭回调时传入的识别码</param>
/// <param name="applyCloseCallBack">抽屉请求关闭时的回调</param>
public FutureDrawer ShowNewInTarget(UIElement view, FrameworkElement target, string title, int? callBackSign = null, Func<int?, bool> applyCloseCallBack = null)
{
FutureDrawer drawer = this.Clone(view, title, callBackSign, applyCloseCallBack);
drawer.ShowInTarget(target);
return drawer;
}
/// <summary>
/// 以当前抽屉为模板复制一个新的抽屉以参照原点【origin】为起点沿着视觉树向上查找到的第一个Window对象在其可视范围内显示抽屉
/// </summary>
/// <param name="view">抽屉中承载的内容</param>
/// <param name="origin">查找的参照点</param>
/// <param name="title">抽屉的标题</param>
/// <param name="callBackSign">请求关闭回调时传入的识别码</param>
/// <param name="applyCloseCallBack">抽屉请求关闭时的回调</param>
public void ShowNewInWindow(UIElement view, FrameworkElement origin, string title, int? callBackSign = null, Func<int?, bool> applyCloseCallBack = null)
{
FutureDrawer drawer = this.Clone(view, title, callBackSign, applyCloseCallBack);
drawer?.ShowInWindow(origin);
}
/// <summary>
/// 以当前抽屉为模板复制一个新的抽屉在参照原点【origin】的视觉树内查找【target】名称的UIElement元素在其可视范围内显示抽屉
/// </summary>
/// <param name="view">抽屉中承载的内容</param>
/// <param name="origin">查找的参照点</param>
/// <param name="targetName">要查找的目标UIElement的名称</param>
/// <param name="title">抽屉的标题</param>
/// <param name="callBackSign">请求关闭回调时传入的识别码</param>
/// <param name="applyCloseCallBack">抽屉请求关闭时的回调</param>
public void ShowNewInNamedView(UIElement view, FrameworkElement origin, string targetName, string title, int? callBackSign = null, Func<int?, bool> applyCloseCallBack = null)
{
FutureDrawer drawer = this.Clone(view, title, callBackSign, applyCloseCallBack);
drawer?.ShowInNamedView(origin, targetName);
}
#region
/// <summary>
/// 专为抽屉使用的装饰器类
/// </summary>
private class DrawerAdorner : Adorner
{
/// <summary>
/// Adorner本身没有Child属性所以这里需要自己加一个
/// </summary>
private UIElement mChild;
public DrawerAdorner(UIElement adornedElement) : base(adornedElement) { }
public UIElement Child
{
get => mChild;
set
{
if (value == null) RemoveVisualChild(mChild);
else AddVisualChild(value);
mChild = value;
}
}
//重写VisualChildrenCount 表示此控件只有一个子控件
protected override int VisualChildrenCount => 1;
//控件计算大小的时候,我们在装饰层上添加的控件也计算一下大小
protected override Size ArrangeOverride(Size finalSize)
{
mChild?.Arrange(new Rect(finalSize));
return finalSize;
}
//重写GetVisualChild,返回我们添加的控件
protected override Visual GetVisualChild(int index)
{
if (index == 0 && mChild != null) return mChild;
return base.GetVisualChild(index);
}
}
#endregion
}
}

View File

@@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Future.Contract
{
/// <summary>
/// 加载遮罩:用于后台执行耗时加载任务时提供操作遮罩
/// </summary>
public class LoadingAdorner : Adorner
{
[AllowNull]
private Grid _Layout;
[AllowNull]
private TextBlock _Feedback;
[AllowNull]
private AdornerLayer _AdornerLayer;
[AllowNull]
private VisualCollection _Visuals;
private Cursor _Cursor;
private System.Windows.Threading.DispatcherTimer _UiTimer = new System.Windows.Threading.DispatcherTimer();
/// <summary>
/// 对目标元素提供加载遮罩
/// </summary>
/// <param name="adornedElement">要进行遮盖的视觉元素</param>
private LoadingAdorner(UIElement adornedElement) : base(adornedElement)
{
PrepareLoading();
}
private LoadingAdorner(UIElement adornedElement, string feedback, double progress = -1.1) : base(adornedElement)
{
PrepareLoading(feedback, progress);
}
/// <summary>
/// 反馈信息
/// </summary>
public string Feedback
{
get { return (string)GetValue(FeedbackProperty); }
set { SetValue(FeedbackProperty, value); Refresh(); }
}
// Using a DependencyProperty as the backing store for Feedback. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FeedbackProperty =
DependencyProperty.Register("Feedback", typeof(string), typeof(LoadingAdorner), new PropertyMetadata("请稍等..."));
/// <summary>
/// 进度信息
/// </summary>
public double Progress
{
get { return (double)GetValue(ProgressProperty); }
set { SetValue(ProgressProperty, value); Refresh(); }
}
// Using a DependencyProperty as the backing store for Progress. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(double), typeof(LoadingAdorner), new PropertyMetadata(-1.1));
/// <summary>
/// 准备启动:生成装饰器需要的内容
/// </summary>
/// <param name="feedback"></param>
private void PrepareLoading(string feedback = "", double progress = -1.1)
{
_Visuals = new VisualCollection(this);
_Layout = new Grid
{
Cursor = Cursors.Wait,
ForceCursor = true
};
_Layout.Children.Add(new Border()
{
Background = Brushes.Gray,
Opacity = 0.85,
});
_Feedback = new TextBlock()
{
Text = feedback,
//FontSize=14,
Foreground = Brushes.White,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(10)
};
_Layout.Children.Add(_Feedback);
_Visuals.Add(_Layout);
FeedbackProgress(feedback, progress);
_UiTimer.Interval = TimeSpan.FromMilliseconds(200);
_UiTimer.Tick += (s, e) => Animate();
_UiTimer.Start();
_AdornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement);
_AdornerLayer?.Add(this);
if (AdornedElement is FrameworkElement)
{
FrameworkElement target = (FrameworkElement)AdornedElement;
_Cursor = target.Cursor;
target.Cursor = Cursors.Wait;
}
}
/// <summary>
/// 开始装载:可以保证每个控件上方只会出现一个加载遮罩层
/// </summary>
/// <param name="adornedElement">加载目标</param>
/// <param name="feedback">反馈信息</param>
/// <param name="progress">进度信息</param>
/// <returns>加载遮罩装饰器</returns>
public static LoadingAdorner BeginLoading(UIElement adornedElement, string feedback = "", double progress = -1)
{
var adorners = AdornerLayer.GetAdornerLayer(adornedElement)?.GetAdorners(adornedElement)?.OfType<LoadingAdorner>();
if (adorners != null && adorners.Count() == 1) return adorners.ToArray()[0];
return new LoadingAdorner(adornedElement, feedback, progress);
}
/// <summary>
/// 结束加载:清除一个控件上方所有的加载遮罩层
/// </summary>
/// <param name="adornedElement">用标元素</param>
public static void FinishLoading(UIElement adornedElement)
{
var adornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
var adorners = adornerLayer?.GetAdorners(adornedElement);
if (adorners == null) return;
for (int i = adorners.Length - 1; i >= 0; i--)
if (adorners[i] is LoadingAdorner)
((LoadingAdorner)adorners[i]).Finish();
}
/// <summary>
/// 进度反馈:用于向装饰器反馈当前进度
/// </summary>
/// <param name="feedback">信息:进度遮罩上显示的信息</param>
/// <param name="progress">进度0~1的小数代表?%,其他数值无效</param>
public void FeedbackProgress(string feedback = "", double progress = -1)
{
if (!string.IsNullOrEmpty(feedback)) Feedback = feedback;
if (progress >= 0 && progress <= 1) Progress = progress;
}
private string _ProgressInfo = "请稍等...";
/// <summary>
/// 用于更新“进度信息”
/// </summary>
private void Refresh()
{
string prog = "-*-";
if (0 <= Progress && Progress <= 1)
prog = string.Format("{0:0%}", Progress).PadLeft(3);
_ProgressInfo = $"[{prog}] : {Feedback.TrimEnd('.')} ... ";
}
/// <summary>
/// 循环动画
/// </summary>
private void Animate()
{
string info = _Feedback.Text;
if (!string.IsNullOrEmpty(info) && info.Length > 0)
{
char end = info[info.Length - 1];
info = _ProgressInfo;
switch (end)
{
case '|': { info = info + '/'; } break;
case '/': { info = info + '-'; } break;
case '-': { info = info + '\\'; } break;
case '\\': { info = info + '|'; } break;
default: { info = info + '|'; } break;
}
_Feedback.Text = info;
}
else _Feedback.Text = _ProgressInfo + "|";
}
/// <summary>
/// 结束装载
/// </summary>
public void Finish()
{
_UiTimer.Stop();
if (AdornedElement is FrameworkElement)
{
((FrameworkElement)AdornedElement).Cursor = _Cursor;
}
_AdornerLayer?.Remove(this);
}
protected override int VisualChildrenCount => _Visuals.Count;
protected override Visual GetVisualChild(int index) => _Visuals[index];
protected override Size ArrangeOverride(Size finalSize)
{
_Layout.Arrange(new Rect(new Point(0, 0), finalSize));
return finalSize;
}
}
}