From 26ddc4f9350ac32f89cfa8f3e3a4948caee034d9 Mon Sep 17 00:00:00 2001
From: NaBian <836904362@qq.com>
Date: Tue, 10 Jul 2018 16:47:06 +0800
Subject: [PATCH] add loading
---
HandyControl/Controls/Loading/LoadingBase.cs | 135 ++++++++++++
.../Controls/Loading/LoadingCircle.cs | 202 ++++++++++++++++++
HandyControl/Controls/Loading/LoadingLine.cs | 109 ++++++++++
HandyControl/HandyControl.csproj | 19 ++
.../Themes/Styles/Base/LoadingBaseStyle.xaml | 10 +
.../Styles/Base/LoadingCircleBaseStyle.xaml | 19 ++
.../Styles/Base/LoadingLineBaseStyle.xaml | 13 ++
HandyControl/Themes/Styles/Loading.xaml | 45 ++++
HandyControl/Themes/Styles/Style.xaml | 2 +-
HandyControlDemo/MainWindow.xaml | 14 +-
10 files changed, 559 insertions(+), 9 deletions(-)
create mode 100644 HandyControl/Controls/Loading/LoadingBase.cs
create mode 100644 HandyControl/Controls/Loading/LoadingCircle.cs
create mode 100644 HandyControl/Controls/Loading/LoadingLine.cs
create mode 100644 HandyControl/Themes/Styles/Base/LoadingBaseStyle.xaml
create mode 100644 HandyControl/Themes/Styles/Base/LoadingCircleBaseStyle.xaml
create mode 100644 HandyControl/Themes/Styles/Base/LoadingLineBaseStyle.xaml
create mode 100644 HandyControl/Themes/Styles/Loading.xaml
diff --git a/HandyControl/Controls/Loading/LoadingBase.cs b/HandyControl/Controls/Loading/LoadingBase.cs
new file mode 100644
index 00000000..12abb57c
--- /dev/null
+++ b/HandyControl/Controls/Loading/LoadingBase.cs
@@ -0,0 +1,135 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+// ReSharper disable once CheckNamespace
+namespace HandyControl.Controls
+{
+ public abstract class LoadingBase : ContentControl
+ {
+ protected Storyboard Storyboard;
+
+ public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register(
+ "IsRunning", typeof(bool), typeof(LoadingBase), new PropertyMetadata(true, (o, args) =>
+ {
+ var ctl = (LoadingBase) o;
+ var v = (bool) args.NewValue;
+ if (v)
+ {
+ ctl.Storyboard?.Resume();
+ }
+ else
+ {
+ ctl.Storyboard?.Pause();
+ }
+ }));
+
+ public bool IsRunning
+ {
+ get { return (bool) GetValue(IsRunningProperty); }
+ set { SetValue(IsRunningProperty, value); }
+ }
+
+ public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register(
+ "DotCount", typeof(int), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public static readonly DependencyProperty DotIntervalProperty = DependencyProperty.Register(
+ "DotInterval", typeof(double), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public static readonly DependencyProperty DotBorderBrushProperty = DependencyProperty.Register(
+ "DotBorderBrush", typeof(Brush), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public static readonly DependencyProperty DotBorderThicknessProperty = DependencyProperty.Register(
+ "DotBorderThickness", typeof(double), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public static readonly DependencyProperty DotDiameterProperty = DependencyProperty.Register(
+ "DotDiameter", typeof(double), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public static readonly DependencyProperty DotSpeedProperty = DependencyProperty.Register(
+ "DotSpeed", typeof(double), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(4.0, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public static readonly DependencyProperty DotDelayTimeProperty = DependencyProperty.Register(
+ "DotDelayTime", typeof(double), typeof(LoadingBase),
+ new FrameworkPropertyMetadata(80.0, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ protected readonly Canvas PrivateCanvas = new Canvas
+ {
+ ClipToBounds = true
+ };
+
+ protected LoadingBase()
+ {
+ Content = PrivateCanvas;
+ }
+
+ public int DotCount
+ {
+ get => (int) GetValue(DotCountProperty);
+ set => SetValue(DotCountProperty, value);
+ }
+
+ public double DotInterval
+ {
+ get => (double) GetValue(DotIntervalProperty);
+ set => SetValue(DotIntervalProperty, value);
+ }
+
+ public Brush DotBorderBrush
+ {
+ get => (Brush) GetValue(DotBorderBrushProperty);
+ set => SetValue(DotBorderBrushProperty, value);
+ }
+
+ public double DotBorderThickness
+ {
+ get => (double) GetValue(DotBorderThicknessProperty);
+ set => SetValue(DotBorderThicknessProperty, value);
+ }
+
+ public double DotDiameter
+ {
+ get => (double) GetValue(DotDiameterProperty);
+ set => SetValue(DotDiameterProperty, value);
+ }
+
+ public double DotSpeed
+ {
+ get => (double) GetValue(DotSpeedProperty);
+ set => SetValue(DotSpeedProperty, value);
+ }
+
+ public double DotDelayTime
+ {
+ get => (double) GetValue(DotDelayTimeProperty);
+ set => SetValue(DotDelayTimeProperty, value);
+ }
+
+ protected abstract void UpdateDots();
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ base.OnRender(drawingContext);
+ UpdateDots();
+ }
+
+ protected Ellipse CreateEllipse(int index)
+ {
+ var ellipse = new Ellipse();
+ ellipse.SetBinding(WidthProperty, new Binding("DotDiameter") {Source = this});
+ ellipse.SetBinding(HeightProperty, new Binding("DotDiameter") {Source = this});
+ ellipse.SetBinding(Shape.FillProperty, new Binding("Foreground") {Source = this});
+ ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding("DotBorderThickness") {Source = this});
+ ellipse.SetBinding(Shape.StrokeProperty, new Binding("DotBorderBrush") {Source = this});
+ return ellipse;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Controls/Loading/LoadingCircle.cs b/HandyControl/Controls/Loading/LoadingCircle.cs
new file mode 100644
index 00000000..1f373fe7
--- /dev/null
+++ b/HandyControl/Controls/Loading/LoadingCircle.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+
+// ReSharper disable once CheckNamespace
+namespace HandyControl.Controls
+{
+ public class LoadingCircle : LoadingBase
+ {
+ public static readonly DependencyProperty DotOffSetProperty = DependencyProperty.Register(
+ "DotOffSet", typeof(double), typeof(LoadingCircle),
+ new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public double DotOffSet
+ {
+ get => (double) GetValue(DotOffSetProperty);
+ set => SetValue(DotOffSetProperty, value);
+ }
+
+ public static readonly DependencyProperty NeedHiddenProperty = DependencyProperty.Register(
+ "NeedHidden", typeof(bool), typeof(LoadingCircle),
+ new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ public bool NeedHidden
+ {
+ get => (bool) GetValue(NeedHiddenProperty);
+ set => SetValue(NeedHiddenProperty, value);
+ }
+
+ static LoadingCircle()
+ {
+ DotSpeedProperty.OverrideMetadata(typeof(LoadingCircle),
+ new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
+ DotDelayTimeProperty.OverrideMetadata(typeof(LoadingCircle),
+ new FrameworkPropertyMetadata(220.0, FrameworkPropertyMetadataOptions.AffectsRender));
+ }
+
+ protected sealed override void UpdateDots()
+ {
+ if (DotCount < 1) return;
+ PrivateCanvas.Children.Clear();
+
+ //定义动画
+ Storyboard = new Storyboard
+ {
+ RepeatBehavior = RepeatBehavior.Forever
+ };
+
+ //创建圆点
+ for (var i = 0; i < DotCount; i++)
+ {
+ var border = CreateBorder(i);
+
+ var framesMove = new DoubleAnimationUsingKeyFrames
+ {
+ BeginTime = TimeSpan.FromMilliseconds(DotDelayTime * i)
+ };
+
+ var subAngle = -DotInterval * i;
+
+ //开始位置
+ var frame0 = new LinearDoubleKeyFrame
+ {
+ Value = 0 + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
+ };
+
+ //开始位置到第一次匀速开始
+ var frame1 = new EasingDoubleKeyFrame
+ {
+ EasingFunction = new PowerEase
+ {
+ EasingMode = EasingMode.EaseOut
+ },
+ Value = 180 + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (0.75 / 7)))
+ };
+
+ //第一次匀速开始到第一次匀速结束
+ var frame2 = new LinearDoubleKeyFrame
+ {
+ Value = 180 + DotOffSet + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (2.75 / 7)))
+ };
+
+ //第一次匀速结束到匀加速结束
+ var frame3 = new EasingDoubleKeyFrame
+ {
+ EasingFunction = new PowerEase
+ {
+ EasingMode = EasingMode.EaseIn
+ },
+ Value = 360 + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (3.5 / 7)))
+ };
+
+ //匀加速结束到匀减速结束
+ var frame4 = new EasingDoubleKeyFrame
+ {
+ EasingFunction = new PowerEase
+ {
+ EasingMode = EasingMode.EaseOut
+ },
+ Value = 540 + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (4.25 / 7)))
+ };
+
+ //匀减速结束到第二次匀速结束
+ var frame5 = new LinearDoubleKeyFrame
+ {
+ Value = 540 + DotOffSet + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (6.25 / 7)))
+ };
+
+ //第二次匀速结束到匀加速结束
+ var frame6 = new EasingDoubleKeyFrame
+ {
+ EasingFunction = new PowerEase
+ {
+ EasingMode = EasingMode.EaseIn
+ },
+ Value = 720 + subAngle,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed))
+ };
+
+ framesMove.KeyFrames.Add(frame0);
+ framesMove.KeyFrames.Add(frame1);
+ framesMove.KeyFrames.Add(frame2);
+ framesMove.KeyFrames.Add(frame3);
+ framesMove.KeyFrames.Add(frame4);
+ framesMove.KeyFrames.Add(frame5);
+ framesMove.KeyFrames.Add(frame6);
+
+ Storyboard.SetTarget(framesMove, border);
+ Storyboard.SetTargetProperty(framesMove, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
+ Storyboard.Children.Add(framesMove);
+
+ if (NeedHidden)
+ {
+ //隐藏一段时间
+ var frame7 = new DiscreteObjectKeyFrame
+ {
+ Value = Visibility.Collapsed,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed))
+ };
+
+ var frame8 = new DiscreteObjectKeyFrame
+ {
+ Value = Visibility.Collapsed,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed + 0.4))
+ };
+
+ var frame9 = new DiscreteObjectKeyFrame
+ {
+ Value = Visibility.Visible,
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
+ };
+ var framesVisibility = new ObjectAnimationUsingKeyFrames
+ {
+ BeginTime = TimeSpan.FromMilliseconds(DotDelayTime * i)
+ };
+ framesVisibility.KeyFrames.Add(frame9);
+ framesVisibility.KeyFrames.Add(frame7);
+ framesVisibility.KeyFrames.Add(frame8);
+ Storyboard.SetTarget(framesVisibility, border);
+ Storyboard.SetTargetProperty(framesVisibility, new PropertyPath("(UIElement.Visibility)"));
+ Storyboard.Children.Add(framesVisibility);
+ }
+ PrivateCanvas.Children.Add(border);
+ }
+
+ Storyboard.Begin();
+ }
+
+ private Border CreateBorder(int index)
+ {
+ var ellipse = CreateEllipse(index);
+ ellipse.HorizontalAlignment = HorizontalAlignment.Center;
+ ellipse.VerticalAlignment = VerticalAlignment.Bottom;
+ var rt = new RotateTransform
+ {
+ Angle = -DotInterval * index
+ };
+ var myTransGroup = new TransformGroup();
+ myTransGroup.Children.Add(rt);
+ var border = new Border
+ {
+ RenderTransformOrigin = new Point(0.5, 0.5),
+ RenderTransform = myTransGroup,
+ Child = ellipse,
+ Visibility = NeedHidden ? Visibility.Collapsed : Visibility.Visible
+ };
+ border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
+ border.SetBinding(HeightProperty, new Binding("Height") { Source = this });
+
+ return border;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/Controls/Loading/LoadingLine.cs b/HandyControl/Controls/Loading/LoadingLine.cs
new file mode 100644
index 00000000..9f98a7c6
--- /dev/null
+++ b/HandyControl/Controls/Loading/LoadingLine.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+// ReSharper disable once CheckNamespace
+namespace HandyControl.Controls
+{
+ ///
+ /// LoadingLine.xaml 的交互逻辑
+ ///
+ public class LoadingLine : LoadingBase
+ {
+ private const double MoveLength = 80;
+
+ private const double UniformScale = .6;
+
+ public LoadingLine()
+ {
+ SetBinding(HeightProperty, new Binding("DotDiameter") { Source = this });
+ }
+
+ protected sealed override void UpdateDots()
+ {
+ if (DotCount < 1) return;
+ PrivateCanvas.Children.Clear();
+
+ //计算相关尺寸
+ var centerWidth = DotDiameter * DotCount + DotInterval * (DotCount - 1) + MoveLength;
+ var speedDownLength = (ActualWidth - MoveLength) / 2;
+ var speedUniformLength = centerWidth / 2;
+
+ //定义动画
+ Storyboard = new Storyboard
+ {
+ RepeatBehavior = RepeatBehavior.Forever
+ };
+
+ //创建圆点
+ for (var i = 0; i < DotCount; i++)
+ {
+ var ellipse = CreateEllipse(i);
+
+ var frames = new ThicknessAnimationUsingKeyFrames
+ {
+ BeginTime = TimeSpan.FromMilliseconds(DotDelayTime * i)
+ };
+ //开始位置
+ var frame0 = new LinearThicknessKeyFrame
+ {
+ Value = new Thickness(ellipse.Margin.Left, 0, 0, 0),
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
+ };
+
+ //开始位置到匀速开始
+ var frame1 = new EasingThicknessKeyFrame
+ {
+ EasingFunction = new PowerEase
+ {
+ EasingMode = EasingMode.EaseOut
+ },
+ Value = new Thickness(speedDownLength + ellipse.Margin.Left, 0, 0, 0),
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (1 - UniformScale)/2))
+ };
+
+ //匀速开始到匀速结束
+ var frame2 = new LinearThicknessKeyFrame
+ {
+ Value = new Thickness(speedDownLength + speedUniformLength + ellipse.Margin.Left, 0, 0, 0),
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (1 + UniformScale) / 2))
+ };
+
+ //匀速结束到匀加速结束
+ var frame3 = new EasingThicknessKeyFrame
+ {
+ EasingFunction = new PowerEase
+ {
+ EasingMode = EasingMode.EaseIn
+ },
+ Value = new Thickness(ActualWidth + ellipse.Margin.Left + speedUniformLength, 0, 0, 0),
+ KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed))
+ };
+
+ frames.KeyFrames.Add(frame0);
+ frames.KeyFrames.Add(frame1);
+ frames.KeyFrames.Add(frame2);
+ frames.KeyFrames.Add(frame3);
+
+ Storyboard.SetTarget(frames, ellipse);
+ Storyboard.SetTargetProperty(frames, new PropertyPath(MarginProperty));
+ Storyboard.Children.Add(frames);
+
+ PrivateCanvas.Children.Add(ellipse);
+ }
+
+ Storyboard.Begin();
+ }
+
+ private new Ellipse CreateEllipse(int index)
+ {
+ var ellipse = base.CreateEllipse(index);
+ ellipse.HorizontalAlignment = HorizontalAlignment.Left;
+ ellipse.VerticalAlignment = VerticalAlignment.Top;
+ ellipse.Margin = new Thickness(-(DotInterval + DotDiameter) * index, 0, 0, 0);
+ return ellipse;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/HandyControl.csproj b/HandyControl/HandyControl.csproj
index 6d854d8e..d8a19cdc 100644
--- a/HandyControl/HandyControl.csproj
+++ b/HandyControl/HandyControl.csproj
@@ -42,6 +42,9 @@
InfoTextBox.xaml
+
+
+
@@ -159,6 +162,18 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
@@ -171,6 +186,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/HandyControl/Themes/Styles/Base/LoadingBaseStyle.xaml b/HandyControl/Themes/Styles/Base/LoadingBaseStyle.xaml
new file mode 100644
index 00000000..5aa742fd
--- /dev/null
+++ b/HandyControl/Themes/Styles/Base/LoadingBaseStyle.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Themes/Styles/Base/LoadingCircleBaseStyle.xaml b/HandyControl/Themes/Styles/Base/LoadingCircleBaseStyle.xaml
new file mode 100644
index 00000000..cca16378
--- /dev/null
+++ b/HandyControl/Themes/Styles/Base/LoadingCircleBaseStyle.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Themes/Styles/Base/LoadingLineBaseStyle.xaml b/HandyControl/Themes/Styles/Base/LoadingLineBaseStyle.xaml
new file mode 100644
index 00000000..4efa6878
--- /dev/null
+++ b/HandyControl/Themes/Styles/Base/LoadingLineBaseStyle.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Themes/Styles/Loading.xaml b/HandyControl/Themes/Styles/Loading.xaml
new file mode 100644
index 00000000..8e5d4010
--- /dev/null
+++ b/HandyControl/Themes/Styles/Loading.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Themes/Styles/Style.xaml b/HandyControl/Themes/Styles/Style.xaml
index 2973af17..42d4f76e 100644
--- a/HandyControl/Themes/Styles/Style.xaml
+++ b/HandyControl/Themes/Styles/Style.xaml
@@ -22,10 +22,10 @@
-
-->
+
\ No newline at end of file
diff --git a/HandyControlDemo/MainWindow.xaml b/HandyControlDemo/MainWindow.xaml
index 278c487e..8da76a79 100644
--- a/HandyControlDemo/MainWindow.xaml
+++ b/HandyControlDemo/MainWindow.xaml
@@ -3,9 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl"
- xmlns:model="clr-namespace:HandyControlDemo.Data.Model"
mc:Ignorable="d"
UseLayoutRounding="True"
Style="{StaticResource WindowDefaultStyle}"
@@ -282,7 +280,7 @@
-->
-
- -->
+