216 lines
7.8 KiB
C#
216 lines
7.8 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|