diff --git a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json
index 85486a72..91de8154 100644
--- a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json
+++ b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json
@@ -44,6 +44,7 @@
[ "ConfettiCannon", "ConfettiCannonDemo", "Brush.Favorite", "", "Interacting" ],
[ "Gravatar", "GravatarDemo", "Brush.User", "", "DataDisplay" ],
[ "ButtonGroup", "ButtonGroupDemo", "Brush.ButtonGroup", "", "Button" ],
+ [ "ProgressButton", "ProgressButtonDemo", "Brush.ProgressBarStatusBar8", "", "Button" ],
[ "Card", "CardDemo", "Brush.ContactCard", "", "DataDisplay" ],
[ "ElementGroup", "ElementGroupDemo", "Brush.ButtonGroup", "", "Layout" ],
[ "CirclePanel", "CirclePanelDemo", "Brush.ColorWheel", "", "Layout" ],
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressButtonDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressButtonDemo.axaml
new file mode 100644
index 00000000..97515047
--- /dev/null
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressButtonDemo.axaml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressButtonDemo.axaml.cs b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressButtonDemo.axaml.cs
new file mode 100644
index 00000000..d48770e4
--- /dev/null
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressButtonDemo.axaml.cs
@@ -0,0 +1,114 @@
+using System;
+using Avalonia;
+using Avalonia.Threading;
+using HandyControlDemo.Properties.Langs;
+
+namespace HandyControlDemo.UserControl;
+
+public partial class ProgressButtonDemo : Avalonia.Controls.UserControl
+{
+ private readonly DispatcherTimer _timer;
+
+ private int _progress;
+ private bool _isUploading;
+
+ public static readonly DirectProperty ProgressProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Progress), o => o.Progress, (o, v) => o.Progress = v);
+
+ public static readonly DirectProperty IsUploadingProperty =
+ AvaloniaProperty.RegisterDirect(nameof(IsUploading), o => o.IsUploading, (o, v) => o.IsUploading = v);
+
+ public static readonly DirectProperty UploadContentProperty =
+ AvaloniaProperty.RegisterDirect(nameof(UploadContent), o => o.UploadContent);
+
+ public ProgressButtonDemo()
+ {
+ InitializeComponent();
+
+ DataContext = this;
+
+ _timer = new DispatcherTimer
+ {
+ Interval = TimeSpan.FromMilliseconds(200)
+ };
+ _timer.Tick += Timer_Tick;
+ }
+
+ public int Progress
+ {
+ get => _progress;
+ set
+ {
+ if (_progress == value)
+ {
+ return;
+ }
+
+ var oldValue = _progress;
+ _progress = value;
+ RaisePropertyChanged(ProgressProperty, oldValue, value);
+ }
+ }
+
+ public bool IsUploading
+ {
+ get => _isUploading;
+ set
+ {
+ if (_isUploading == value)
+ {
+ return;
+ }
+
+ var oldValue = _isUploading;
+ var oldUploadContent = UploadContent;
+ _isUploading = value;
+ RaisePropertyChanged(IsUploadingProperty, oldValue, value);
+ RaisePropertyChanged(UploadContentProperty, oldUploadContent, UploadContent);
+
+ if (_isUploading)
+ {
+ if (!_timer.IsEnabled)
+ {
+ _timer.Start();
+ }
+ }
+ else
+ {
+ if (_timer.IsEnabled)
+ {
+ _timer.Stop();
+ }
+
+ if (Progress != 0)
+ {
+ Progress = 0;
+ }
+ }
+ }
+ }
+
+ public string UploadContent
+ {
+ get
+ {
+ var values = Lang.UploadStr.Split(';');
+ if (values.Length < 2)
+ {
+ return Lang.UploadStr;
+ }
+
+ return (IsUploading ? values[1] : values[0]).Trim();
+ }
+ }
+
+ private void Timer_Tick(object? sender, EventArgs e)
+ {
+ Progress++;
+ if (Progress >= 100)
+ {
+ Progress = 0;
+ IsUploading = false;
+ }
+ }
+}
diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/Button/ProgressButton.cs b/src/Avalonia/HandyControl_Avalonia/Controls/Button/ProgressButton.cs
new file mode 100644
index 00000000..072b00f8
--- /dev/null
+++ b/src/Avalonia/HandyControl_Avalonia/Controls/Button/ProgressButton.cs
@@ -0,0 +1,26 @@
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using Avalonia.Styling;
+
+namespace HandyControl.Controls;
+
+public class ProgressButton : ToggleButton
+{
+ public static readonly StyledProperty ProgressThemeProperty =
+ AvaloniaProperty.Register(nameof(ProgressTheme));
+
+ public static readonly StyledProperty ProgressProperty =
+ AvaloniaProperty.Register(nameof(Progress));
+
+ public ControlTheme? ProgressTheme
+ {
+ get => GetValue(ProgressThemeProperty);
+ set => SetValue(ProgressThemeProperty, value);
+ }
+
+ public double Progress
+ {
+ get => GetValue(ProgressProperty);
+ set => SetValue(ProgressProperty, value);
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/Loading/LoadingCircle.cs b/src/Avalonia/HandyControl_Avalonia/Controls/Loading/LoadingCircle.cs
index 42570e16..2a3e4a9b 100644
--- a/src/Avalonia/HandyControl_Avalonia/Controls/Loading/LoadingCircle.cs
+++ b/src/Avalonia/HandyControl_Avalonia/Controls/Loading/LoadingCircle.cs
@@ -58,6 +58,8 @@ public class LoadingCircle : LoadingBase
var dotDelayTime = DotDelayTime;
var needHidden = NeedHidden;
var dotOffset = DotOffSet;
+ var totalSeconds = dotSpeed + 0.4d;
+ var visibleCue = dotSpeed / totalSeconds;
if (dotCount < 1) return;
PrivateCanvas.Children.Clear();
@@ -73,20 +75,21 @@ public class LoadingCircle : LoadingBase
var rotateAnimation = new Animation
{
- Duration = TimeSpan.FromSeconds(dotSpeed),
+ Duration = TimeSpan.FromSeconds(totalSeconds),
Delay = TimeSpan.FromMilliseconds(dotDelayTime * i),
IterationCount = IterationCount.Infinite,
FillMode = FillMode.Forward,
Easing = new LinearEasing(),
Children =
{
- BuildAngleFrame(0d / 7d, 0 + subAngle, null),
- BuildAngleFrame(0.75d / 7d, 180 + subAngle, EaseOutSpline),
- BuildAngleFrame(2.75d / 7d, 180 + dotOffset + subAngle, null),
- BuildAngleFrame(3.5d / 7d, 360 + subAngle, EaseInSpline),
- BuildAngleFrame(4.25d / 7d, 540 + subAngle, EaseOutSpline),
- BuildAngleFrame(6.25d / 7d, 540 + dotOffset + subAngle, null),
- BuildAngleFrame(1d, 720 + subAngle, EaseInSpline),
+ BuildAngleFrame(0d, 0 + subAngle, null),
+ BuildAngleFrame((dotSpeed * (0.75d / 7d)) / totalSeconds, 180 + subAngle, EaseOutSpline),
+ BuildAngleFrame((dotSpeed * (2.75d / 7d)) / totalSeconds, 180 + dotOffset + subAngle, null),
+ BuildAngleFrame((dotSpeed * (3.5d / 7d)) / totalSeconds, 360 + subAngle, EaseInSpline),
+ BuildAngleFrame((dotSpeed * (4.25d / 7d)) / totalSeconds, 540 + subAngle, EaseOutSpline),
+ BuildAngleFrame((dotSpeed * (6.25d / 7d)) / totalSeconds, 540 + dotOffset + subAngle, null),
+ BuildAngleFrame(visibleCue, 720 + subAngle, EaseInSpline),
+ BuildAngleFrame(1d, 720 + subAngle, null),
}
};
@@ -96,10 +99,7 @@ public class LoadingCircle : LoadingBase
if (needHidden)
{
- var totalSeconds = dotSpeed + 0.4d;
- var visibleCue = dotSpeed / totalSeconds;
-
- var opacityAnimation = new Animation
+ var visibleAnimation = new Animation
{
Duration = TimeSpan.FromSeconds(totalSeconds),
Delay = TimeSpan.FromMilliseconds(dotDelayTime * i),
@@ -111,29 +111,29 @@ public class LoadingCircle : LoadingBase
new KeyFrame
{
Cue = new Cue(0d),
- Setters = { new Setter(Visual.OpacityProperty, 1d) }
+ Setters = { new Setter(Visual.IsVisibleProperty, true) }
},
new KeyFrame
{
Cue = new Cue(Math.Max(0d, visibleCue - 0.0001d)),
- Setters = { new Setter(Visual.OpacityProperty, 1d) }
+ Setters = { new Setter(Visual.IsVisibleProperty, true) }
},
new KeyFrame
{
Cue = new Cue(visibleCue),
- Setters = { new Setter(Visual.OpacityProperty, 0d) }
+ Setters = { new Setter(Visual.IsVisibleProperty, false) }
},
new KeyFrame
{
Cue = new Cue(1d),
- Setters = { new Setter(Visual.OpacityProperty, 0d) }
+ Setters = { new Setter(Visual.IsVisibleProperty, false) }
}
}
};
- var opacityCts = new CancellationTokenSource();
- AnimationTokens.Add(opacityCts);
- _ = opacityAnimation.RunAsync(border, opacityCts.Token);
+ var visibleCts = new CancellationTokenSource();
+ AnimationTokens.Add(visibleCts);
+ _ = visibleAnimation.RunAsync(border, visibleCts.Token);
}
}
}
@@ -166,7 +166,7 @@ public class LoadingCircle : LoadingBase
Child = ellipse,
RenderTransformOrigin = RelativePoint.Center,
RenderTransform = rotate,
- Opacity = needHidden ? 0d : 1d
+ IsVisible = !needHidden
};
border.Bind(WidthProperty, new Binding(nameof(Width)) { Source = this });
border.Bind(HeightProperty, new Binding(nameof(Height)) { Source = this });
diff --git a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/MergedStyles.axaml b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/MergedStyles.axaml
index dab03312..16d30524 100644
--- a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/MergedStyles.axaml
+++ b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/MergedStyles.axaml
@@ -34,6 +34,7 @@
+
diff --git a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressButton.axaml b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressButton.axaml
new file mode 100644
index 00000000..c2871968
--- /dev/null
+++ b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressButton.axaml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ToggleButton.axaml b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ToggleButton.axaml
index 8bf5021b..d6c8d5b0 100644
--- a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ToggleButton.axaml
+++ b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ToggleButton.axaml
@@ -895,4 +895,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+