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 { /// /// 加载遮罩:用于后台执行耗时加载任务时提供操作遮罩 /// 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(); /// /// 对目标元素提供加载遮罩 /// /// 要进行遮盖的视觉元素 private LoadingAdorner(UIElement adornedElement) : base(adornedElement) { PrepareLoading(); } private LoadingAdorner(UIElement adornedElement, string feedback, double progress = -1.1) : base(adornedElement) { PrepareLoading(feedback, progress); } /// /// 反馈信息 /// 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("请稍等...")); /// /// 进度信息 /// 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)); /// /// 准备启动:生成装饰器需要的内容 /// /// 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; } } /// /// 开始装载:可以保证每个控件上方只会出现一个加载遮罩层 /// /// 加载目标 /// 反馈信息 /// 进度信息 /// 加载遮罩装饰器 public static LoadingAdorner BeginLoading(UIElement adornedElement, string feedback = "", double progress = -1) { var adorners = AdornerLayer.GetAdornerLayer(adornedElement)?.GetAdorners(adornedElement)?.OfType(); if (adorners != null && adorners.Count() == 1) return adorners.ToArray()[0]; return new LoadingAdorner(adornedElement, feedback, progress); } /// /// 结束加载:清除一个控件上方所有的加载遮罩层 /// /// 用标元素 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(); } /// /// 进度反馈:用于向装饰器反馈当前进度 /// /// 信息:进度遮罩上显示的信息 /// 进度:0~1的小数,代表?%,其他数值无效 public void FeedbackProgress(string feedback = "", double progress = -1) { if (!string.IsNullOrEmpty(feedback)) Feedback = feedback; if (progress >= 0 && progress <= 1) Progress = progress; } private string _ProgressInfo = "请稍等..."; /// /// 用于更新“进度信息” /// private void Refresh() { string prog = "-*-"; if (0 <= Progress && Progress <= 1) prog = string.Format("{0:0%}", Progress).PadLeft(3); _ProgressInfo = $"[{prog}] : {Feedback.TrimEnd('.')} ... "; } /// /// 循环动画 /// 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 + "|"; } /// /// 结束装载 /// 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; } } }