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

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