diff --git a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json index 87e98710..b7a4576b 100644 --- a/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json +++ b/src/Avalonia/HandyControlDemo_Avalonia/Data/DemoInfo.json @@ -47,7 +47,8 @@ [ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "", "Layout" ], [ "WaterfallPanel", "WaterfallPanelDemo", "Brush.Panel", "", "Layout" ], [ "HoneycombPanel", "HoneycombPanelDemo", "Brush.PolygonHexagon", "", "Layout" ], - [ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "", "Layout" ] + [ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "", "Layout" ], + [ "ProgressBar", "ProgressBarDemo", "Brush.ProgressBar", "", "Feedback" ] ] } ] diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressBarDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressBarDemo.axaml new file mode 100644 index 00000000..708b0b3f --- /dev/null +++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressBarDemo.axaml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressBarDemo.axaml.cs b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressBarDemo.axaml.cs new file mode 100644 index 00000000..4e60d7c3 --- /dev/null +++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Controls/ProgressBarDemo.axaml.cs @@ -0,0 +1,9 @@ +namespace HandyControlDemo.UserControl; + +public partial class ProgressBarDemo : Avalonia.Controls.UserControl +{ + public ProgressBarDemo() + { + InitializeComponent(); + } +} diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/ProgressBar/CircleProgressBar.cs b/src/Avalonia/HandyControl_Avalonia/Controls/ProgressBar/CircleProgressBar.cs new file mode 100644 index 00000000..4ba2b9c6 --- /dev/null +++ b/src/Avalonia/HandyControl_Avalonia/Controls/ProgressBar/CircleProgressBar.cs @@ -0,0 +1,87 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Controls.Shapes; + +namespace HandyControl.Controls; + +[TemplatePart(IndicatorTemplateName, typeof(Arc))] +public class CircleProgressBar : RangeBase +{ + private const string IndicatorTemplateName = "PART_Indicator"; + + public static readonly StyledProperty ArcThicknessProperty = + AvaloniaProperty.Register(nameof(ArcThickness)); + + public static readonly StyledProperty ShowTextProperty = + AvaloniaProperty.Register(nameof(ShowText), true); + + public static readonly StyledProperty TextProperty = + AvaloniaProperty.Register(nameof(Text)); + + public static readonly StyledProperty IsIndeterminateProperty = + AvaloniaProperty.Register(nameof(IsIndeterminate)); + + private Arc? _indicator; + + static CircleProgressBar() + { + FocusableProperty.OverrideDefaultValue(false); + MaximumProperty.OverrideDefaultValue(100d); + } + + public string? Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public bool ShowText + { + get => GetValue(ShowTextProperty); + set => SetValue(ShowTextProperty, value); + } + + public double ArcThickness + { + get => GetValue(ArcThicknessProperty); + set => SetValue(ArcThicknessProperty, value); + } + + public bool IsIndeterminate + { + get => GetValue(IsIndeterminateProperty); + set => SetValue(IsIndeterminateProperty, value); + } + + private void SetProgressBarIndicatorAngle() + { + if (_indicator == null) return; + var minimum = Minimum; + var maximum = Maximum; + var num = Value; + _indicator.StartAngle = 0; + _indicator.SweepAngle = (maximum <= minimum ? 0 : (num - minimum) / (maximum - minimum)) * 360; + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + _indicator = e.NameScope.Find(IndicatorTemplateName); + SetProgressBarIndicatorAngle(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == MinimumProperty + || change.Property == MaximumProperty + || change.Property == ValueProperty) + { + SetProgressBarIndicatorAngle(); + } + } +} diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/ProgressBar/WaveProgressBar.cs b/src/Avalonia/HandyControl_Avalonia/Controls/ProgressBar/WaveProgressBar.cs new file mode 100644 index 00000000..7ede4115 --- /dev/null +++ b/src/Avalonia/HandyControl_Avalonia/Controls/ProgressBar/WaveProgressBar.cs @@ -0,0 +1,120 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; +using Avalonia.Media; +using System; + +namespace HandyControl.Controls; + +/// +/// 波浪进度条 +/// +[TemplatePart(ElementWave, typeof(Control))] +[TemplatePart(ElementClip, typeof(Control))] +public class WaveProgressBar : RangeBase +{ + private const string ElementWave = "PART_Wave"; + + private const string ElementClip = "PART_Clip"; + + private const double TranslateTransformMinY = -20; + + private Control? _waveElement; + + private double _translateTransformYRange; + + private TranslateTransform? _yTransform; + + static WaveProgressBar() + { + FocusableProperty.OverrideDefaultValue(false); + MaximumProperty.OverrideDefaultValue(100d); + } + + public static readonly StyledProperty TextProperty = + AvaloniaProperty.Register(nameof(Text)); + + public string? Text + { + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public static readonly StyledProperty ShowTextProperty = + AvaloniaProperty.Register(nameof(ShowText), true); + + public bool ShowText + { + get => GetValue(ShowTextProperty); + set => SetValue(ShowTextProperty, value); + } + + public static readonly StyledProperty WaveFillProperty = + AvaloniaProperty.Register(nameof(WaveFill)); + + public IBrush? WaveFill + { + get => GetValue(WaveFillProperty); + set => SetValue(WaveFillProperty, value); + } + + public static readonly StyledProperty WaveThicknessProperty = + AvaloniaProperty.Register(nameof(WaveThickness)); + + public double WaveThickness + { + get => GetValue(WaveThicknessProperty); + set => SetValue(WaveThicknessProperty, value); + } + + public static readonly StyledProperty WaveStrokeProperty = + AvaloniaProperty.Register(nameof(WaveStroke)); + + public IBrush? WaveStroke + { + get => GetValue(WaveStrokeProperty); + set => SetValue(WaveStrokeProperty, value); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + _waveElement = e.NameScope.Find(ElementWave); + var clipElement = e.NameScope.Find(ElementClip); + + if (_waveElement != null && clipElement != null) + { + var clipElementHeight = clipElement.Height; + + _yTransform = new TranslateTransform { Y = clipElementHeight }; + _translateTransformYRange = clipElementHeight - TranslateTransformMinY; + _waveElement.RenderTransform = _yTransform; + + UpdateWave(Value); + } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ValueProperty + || change.Property == MinimumProperty + || change.Property == MaximumProperty) + { + UpdateWave(Value); + } + } + + private void UpdateWave(double value) + { + if (_yTransform == null) return; + var maximum = Maximum; + if (Math.Abs(maximum) < 1e-6) return; + var scale = 1 - value / maximum; + var y = _translateTransformYRange * scale + TranslateTransformMinY; + _yTransform.Y = y; + } +} diff --git a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressBar.axaml b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressBar.axaml index 80e4ca1b..b913a842 100644 --- a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressBar.axaml +++ b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/ProgressBar.axaml @@ -1,7 +1,8 @@ + xmlns:hc="clr-namespace:HandyControl.Controls" + xmlns:shapes="clr-namespace:Avalonia.Controls.Shapes;assembly=Avalonia.Controls"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +