Last active
October 12, 2023 17:03
-
-
Save JanDeDobbeleer/d9d78f748047c157188f to your computer and use it in GitHub Desktop.
Circular progress bar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<UserControl | |
x:Class="Foo.Bar.CircularProgressBar" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
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" | |
mc:Ignorable="d" | |
d:DesignHeight="480" d:DesignWidth="480"> | |
<Grid> | |
<Path x:Name="PathRoot" Stroke="{Binding SegmentColor}" | |
StrokeThickness="{Binding StrokeThickness}" HorizontalAlignment="Center" | |
VerticalAlignment="Center"> | |
<Path.Data> | |
<PathGeometry> | |
<PathGeometry.Figures> | |
<PathFigureCollection> | |
<PathFigure x:Name="PathFigure"> | |
<PathFigure.Segments> | |
<PathSegmentCollection> | |
<ArcSegment x:Name="ArcSegment" SweepDirection="Clockwise" /> | |
</PathSegmentCollection> | |
</PathFigure.Segments> | |
</PathFigure> | |
</PathFigureCollection> | |
</PathGeometry.Figures> | |
</PathGeometry> | |
</Path.Data> | |
</Path> | |
</Grid> | |
</UserControl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236 | |
using System; | |
using Windows.Foundation; | |
using Windows.UI.Xaml; | |
using Windows.UI.Xaml.Controls; | |
using Windows.UI.Xaml.Media; | |
namespace Foo.Bar | |
{ | |
public partial class CircularProgressBar : UserControl | |
{ | |
public static readonly DependencyProperty PercentageProperty = | |
DependencyProperty.Register("Percentage", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(0d, OnPercentageChanged)); | |
// Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty StrokeThicknessProperty = | |
DependencyProperty.Register("StrokeThickness", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(5, OnPropertyChanged)); | |
// Using a DependencyProperty as the backing store for SegmentColor. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty SegmentColorProperty = | |
DependencyProperty.Register("SegmentColor", typeof(Brush), typeof(CircularProgressBar), new PropertyMetadata(new SolidColorBrush(Colors.Aquamarine))); | |
// Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty RadiusProperty = | |
DependencyProperty.Register("Radius", typeof(int), typeof(CircularProgressBar), new PropertyMetadata(25, OnPropertyChanged)); | |
// Using a DependencyProperty as the backing store for Angle. This enables animation, styling, binding, etc... | |
public static readonly DependencyProperty AngleProperty = | |
DependencyProperty.Register("Angle", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(120d, OnPropertyChanged)); | |
public CircularProgressBar() | |
{ | |
InitializeComponent(); | |
var frameworkElement = Content as FrameworkElement; | |
if (frameworkElement != null) | |
frameworkElement.DataContext = this; | |
Angle = (Percentage * 360) / 100; | |
RenderArc(); | |
} | |
public int Radius | |
{ | |
get { return (int)GetValue(RadiusProperty); } | |
set { SetValue(RadiusProperty, value); } | |
} | |
public Brush SegmentColor | |
{ | |
get { return (Brush)GetValue(SegmentColorProperty); } | |
set { SetValue(SegmentColorProperty, value); } | |
} | |
public int StrokeThickness | |
{ | |
get { return (int)GetValue(StrokeThicknessProperty); } | |
set { SetValue(StrokeThicknessProperty, value); } | |
} | |
public double Percentage | |
{ | |
get { return (double)GetValue(PercentageProperty); } | |
set { SetValue(PercentageProperty, value); } | |
} | |
public double Angle | |
{ | |
get { return (double)GetValue(AngleProperty); } | |
set { SetValue(AngleProperty, value); } | |
} | |
private static void OnPercentageChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) | |
{ | |
var circle = sender as CircularProgressBar; | |
circle.Angle = (circle.Percentage * 360) / 100; | |
} | |
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) | |
{ | |
var circle = sender as CircularProgressBar; | |
circle.RenderArc(); | |
} | |
public void RenderArc() | |
{ | |
var startPoint = new Point(Radius, 0); | |
var endPoint = ComputeCartesianCoordinate(Angle, Radius); | |
endPoint.X += Radius; | |
endPoint.Y += Radius; | |
PathRoot.Width = Radius * 2 + StrokeThickness; | |
PathRoot.Height = Radius * 2 + StrokeThickness; | |
PathRoot.Margin = new Thickness(StrokeThickness, StrokeThickness, 0, 0); | |
var largeArc = Angle > 180.0; | |
var outerArcSize = new Size(Radius, Radius); | |
PathFigure.StartPoint = startPoint; | |
if (startPoint.X == Math.Round(endPoint.X) && startPoint.Y == Math.Round(endPoint.Y)) | |
endPoint.X -= 0.01; | |
ArcSegment.Point = endPoint; | |
ArcSegment.Size = outerArcSize; | |
ArcSegment.IsLargeArc = largeArc; | |
} | |
private Point ComputeCartesianCoordinate(double angle, double radius) | |
{ | |
// convert to radians | |
var angleRad = (Math.PI / 180.0) * (angle - 90); | |
var x = radius * Math.Cos(angleRad); | |
var y = radius * Math.Sin(angleRad); | |
return new Point(x, y); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment