Files
Future/Future.Contract/Adorners/LoadingAdorner.cs

216 lines
7.8 KiB
C#
Raw Normal View History

2025-08-30 17:19:57 +08:00
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;
}
}
}