Skip to content

Commit fb2b942

Browse files
authored
Merge pull request #828 from Aftnet/dev
Added radial progress bar control
2 parents 7d8bc3b + 94af716 commit fb2b942

File tree

12 files changed

+426
-0
lines changed

12 files changed

+426
-0
lines changed

Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@
271271
<Content Include="SamplePages\ParallaxService\Parallax.png" />
272272
<Content Include="SamplePages\PrintHelper\PrintHelper.png" />
273273
<Content Include="SamplePages\PullToRefreshListView\PullToRefreshListView.png" />
274+
<Content Include="SamplePages\RadialProgressBar\RadialProgressBar.png" />
274275
<Content Include="SamplePages\ScrollHeader\ScrollHeader.png" />
275276
<Content Include="SamplePages\RadialGauge\RadialGauge.png" />
276277
<Content Include="SamplePages\RangeSelector\RangeSelector.png" />
@@ -371,6 +372,7 @@
371372
<Content Include="SamplePages\MarkdownTextBlock\InitialContent.md" />
372373
<Content Include="SamplePages\AdvancedCollectionView\AdvancedCollectionView.bind" />
373374
<Content Include="SamplePages\TextBoxRegex\TextBoxRegex.bind" />
375+
<Content Include="SamplePages\RadialProgressBar\RadialProgressBarCode.bind" />
374376
<Content Include="SamplePages\MarkdownTextBlock\MarkdownTextBlockCode.bind" />
375377
<Content Include="SamplePages\OneDrive Service\OneDriveCode.bind" />
376378
<Content Include="SamplePages\Analytics\AnalyticsCode.bind" />
@@ -474,6 +476,9 @@
474476
<Compile Include="SamplePages\TileControl\TileControlPage.xaml.cs">
475477
<DependentUpon>TileControlPage.xaml</DependentUpon>
476478
</Compile>
479+
<Compile Include="SamplePages\RadialProgressBar\RadialProgressBarPage.xaml.cs">
480+
<DependentUpon>RadialProgressBarPage.xaml</DependentUpon>
481+
</Compile>
477482
<Compile Include="SamplePages\ScrollHeader\ScrollHeaderPage.xaml.cs">
478483
<DependentUpon>ScrollHeaderPage.xaml</DependentUpon>
479484
</Compile>
@@ -706,6 +711,10 @@
706711
<Generator>MSBuild:Compile</Generator>
707712
<SubType>Designer</SubType>
708713
</Page>
714+
<Page Include="SamplePages\RadialProgressBar\RadialProgressBarPage.xaml">
715+
<Generator>MSBuild:Compile</Generator>
716+
<SubType>Designer</SubType>
717+
</Page>
709718
<Page Include="SamplePages\ScrollHeader\ScrollHeaderPage.xaml">
710719
<Generator>MSBuild:Compile</Generator>
711720
<SubType>Designer</SubType>
5.94 KB
Loading
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.RadialProgressBarPage"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
7+
mc:Ignorable="d">
8+
9+
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
10+
<Grid VerticalAlignment="Center"
11+
HorizontalAlignment="Center">
12+
<Grid.ColumnDefinitions>
13+
<ColumnDefinition Width="50"></ColumnDefinition>
14+
<ColumnDefinition Width="250"></ColumnDefinition>
15+
<ColumnDefinition Width="50"></ColumnDefinition>
16+
</Grid.ColumnDefinitions>
17+
<controls:RadialProgressBar
18+
x:Name="RadialProgressBar"
19+
Grid.Column="1"
20+
Value="@[Value:Slider:0:0-100]"
21+
Thickness="@[Thickness:Slider:4:4-20]"
22+
Minimum="0"
23+
Maximum="100"
24+
Width="@[Width:Slider:100:100-200]"
25+
Height="@[Height:Slider:100:100-200]"
26+
Outline="@[Outline:Brush:LightGray]"/>
27+
</Grid>
28+
</Grid>
29+
</Page>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.RadialProgressBarPage"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
7+
mc:Ignorable="d">
8+
9+
<Grid Background="{StaticResource Brush-Grey-05}">
10+
<Grid HorizontalAlignment="Center"
11+
VerticalAlignment="Center">
12+
<Grid.ColumnDefinitions>
13+
<ColumnDefinition Width="50" />
14+
<ColumnDefinition Width="250" />
15+
<ColumnDefinition Width="50" />
16+
</Grid.ColumnDefinitions>
17+
<controls:RadialProgressBar x:Name="RadialProgressBarControl"
18+
Grid.Column="1"
19+
Maximum="100"
20+
Minimum="0"
21+
Thickness="{Binding Thickness.Value, Mode=OneWay}"
22+
Value="{Binding Value.Value, Mode=OneWay}"
23+
Width="{Binding Width.Value, Mode=OneWay}"
24+
Height="{Binding Height.Value, Mode=OneWay}"
25+
Outline="{Binding Outline.Value, Mode=OneWay}"/>
26+
</Grid>
27+
</Grid>
28+
</Page>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// ******************************************************************
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// This code is licensed under the MIT License (MIT).
4+
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
5+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
7+
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
8+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
9+
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
10+
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
11+
// ******************************************************************
12+
13+
using Microsoft.Toolkit.Uwp.SampleApp.Models;
14+
15+
using Windows.UI.Xaml.Controls;
16+
using Windows.UI.Xaml.Navigation;
17+
18+
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
19+
{
20+
public sealed partial class RadialProgressBarPage : Page
21+
{
22+
public RadialProgressBarPage()
23+
{
24+
InitializeComponent();
25+
}
26+
27+
protected override void OnNavigatedTo(NavigationEventArgs e)
28+
{
29+
base.OnNavigatedTo(e);
30+
31+
var propertyDesc = e.Parameter as PropertyDescriptor;
32+
33+
if (propertyDesc != null)
34+
{
35+
DataContext = propertyDesc.Expando;
36+
}
37+
}
38+
}
39+
}

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@
8585
"Icon": "/SamplePages/RadialGauge/RadialGauge.png",
8686
"DocumentationUrl": "https://raw.githubusercontent.com/Microsoft/UWPCommunityToolkit/dev/docs/controls/RadialGauge.md"
8787
},
88+
{
89+
"Name": "RadialProgressBar",
90+
"Type": "RadialProgressBarPage",
91+
"About": "The radial progress bar displays progress as a circle getting filled.",
92+
"CodeUrl": "https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/RadialProgressBar",
93+
"XamlCodeFile": "RadialProgressBarCode.bind",
94+
"Icon": "/SamplePages/RadialProgressBar/RadialProgressBar.png"
95+
},
8896
{
8997
"Name": "SlidableListItem",
9098
"Type": "SlidableListItemPage",

Microsoft.Toolkit.Uwp.UI.Controls/Microsoft.Toolkit.Uwp.UI.Controls.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
<Compile Include="BladeView\BladeItem.Events.cs" />
113113
<Compile Include="MasterDetailsView\MasterDetailsViewState.cs" />
114114
<Compile Include="ScrollHeader\ScrollHeaderMode.cs" />
115+
<Compile Include="RadialProgressBar\RadialProgressBar.cs" />
115116
<Compile Include="SlidableListItem\SwipeStatus.cs" />
116117
<Compile Include="SlidableListItem\SwipeStatusChangedEventArgs.cs" />
117118
<Compile Include="SurfaceDialTextboxHelper\SurfaceDialTextboxHelper.cs" />
@@ -202,6 +203,10 @@
202203
<Generator>MSBuild:Compile</Generator>
203204
<SubType>Designer</SubType>
204205
</Page>
206+
<Page Include="RadialProgressBar\RadialProgressBar.xaml">
207+
<Generator>MSBuild:Compile</Generator>
208+
<SubType>Designer</SubType>
209+
</Page>
205210
<Page Include="ScrollHeader\ScrollHeader.xaml">
206211
<SubType>Designer</SubType>
207212
<Generator>MSBuild:Compile</Generator>
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// ******************************************************************
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// This code is licensed under the MIT License (MIT).
4+
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
5+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
7+
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
8+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
9+
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
10+
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
11+
// ******************************************************************
12+
13+
using System;
14+
using Windows.Foundation;
15+
using Windows.UI.Xaml;
16+
using Windows.UI.Xaml.Controls;
17+
using Windows.UI.Xaml.Media;
18+
19+
namespace Microsoft.Toolkit.Uwp.UI.Controls
20+
{
21+
/// <summary>
22+
/// An alternative impementation of a progress bar.
23+
/// Progression is represented by a loop filling up in a clockwise fashion.
24+
/// Like the traditional progress bar, it inherits from RangeBase, so Minimum, Maximum and Value properties work the same way.
25+
/// </summary>
26+
[TemplatePart(Name = OutlineFigurePartName, Type = typeof(PathFigure))]
27+
[TemplatePart(Name = OutlineArcPartName, Type = typeof(ArcSegment))]
28+
[TemplatePart(Name = BarFigurePartName, Type = typeof(PathFigure))]
29+
[TemplatePart(Name = BarArcPartName, Type = typeof(ArcSegment))]
30+
public sealed class RadialProgressBar : ProgressBar
31+
{
32+
private const string OutlineFigurePartName = "OutlineFigurePart";
33+
private const string OutlineArcPartName = "OutlineArcPart";
34+
private const string BarFigurePartName = "BarFigurePart";
35+
private const string BarArcPartName = "BarArcPart";
36+
37+
private PathFigure outlineFigure;
38+
private PathFigure barFigure;
39+
private ArcSegment outlineArc;
40+
private ArcSegment barArc;
41+
42+
private bool allTemplatePartsDefined = false;
43+
44+
/// <summary>
45+
/// Called when the Minimum property changes.
46+
/// </summary>
47+
/// <param name="oldMinimum">Old value of the Minimum property.</param>
48+
/// <param name="newMinimum">New value of the Minimum property.</param>
49+
protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
50+
{
51+
base.OnMinimumChanged(oldMinimum, newMinimum);
52+
RenderSegment();
53+
}
54+
55+
/// <summary>
56+
/// Called when the Maximum property changes.
57+
/// </summary>
58+
/// <param name="oldMaximum">Old value of the Maximum property.</param>
59+
/// <param name="newMaximum">New value of the Maximum property.</param>
60+
protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
61+
{
62+
base.OnMaximumChanged(oldMaximum, newMaximum);
63+
RenderSegment();
64+
}
65+
66+
/// <summary>
67+
/// Called when the Value property changes.
68+
/// </summary>
69+
/// <param name="oldValue">Old value of the Value property.</param>
70+
/// <param name="newValue">New value of the Value property.</param>
71+
protected override void OnValueChanged(double oldValue, double newValue)
72+
{
73+
base.OnValueChanged(oldValue, newValue);
74+
RenderSegment();
75+
}
76+
77+
/// <summary>
78+
/// Update the visual state of the control when its template is changed.
79+
/// </summary>
80+
protected override void OnApplyTemplate()
81+
{
82+
base.OnApplyTemplate();
83+
84+
outlineFigure = GetTemplateChild(OutlineFigurePartName) as PathFigure;
85+
outlineArc = GetTemplateChild(OutlineArcPartName) as ArcSegment;
86+
barFigure = GetTemplateChild(BarFigurePartName) as PathFigure;
87+
barArc = GetTemplateChild(BarArcPartName) as ArcSegment;
88+
89+
allTemplatePartsDefined = outlineFigure != null && outlineArc != null && barFigure != null && barArc != null;
90+
91+
RenderAll();
92+
}
93+
94+
/// <summary>
95+
/// Gets or sets the thickness of the circular ouline and segment
96+
/// </summary>
97+
public double Thickness
98+
{
99+
get { return (double)GetValue(ThicknessProperty); }
100+
set { SetValue(ThicknessProperty, value); }
101+
}
102+
103+
/// <summary>
104+
/// Identifies the Thickness dependency property
105+
/// </summary>
106+
public static readonly DependencyProperty ThicknessProperty = DependencyProperty.Register(nameof(Thickness), typeof(double), typeof(RadialProgressBar), new PropertyMetadata(0.0, ThicknessChangedHandler));
107+
108+
/// <summary>
109+
/// Gets or sets the color of the circular ouline on which the segment is drawn
110+
/// </summary>
111+
public Brush Outline
112+
{
113+
get { return (Brush)GetValue(OutlineProperty); }
114+
set { SetValue(OutlineProperty, value); }
115+
}
116+
117+
/// <summary>
118+
/// Identifies the Outline dependency property
119+
/// </summary>
120+
public static readonly DependencyProperty OutlineProperty = DependencyProperty.Register(nameof(Outline), typeof(Brush), typeof(RadialProgressBar), new PropertyMetadata(new SolidColorBrush(Windows.UI.Colors.Transparent)));
121+
122+
/// <summary>
123+
/// Initializes a new instance of the <see cref="RadialProgressBar"/> class.
124+
/// Create a default circular progress bar
125+
/// </summary>
126+
public RadialProgressBar()
127+
{
128+
DefaultStyleKey = typeof(RadialProgressBar);
129+
SizeChanged += SizeChangedHandler;
130+
}
131+
132+
// Render outline and progress segment when thickness is changed
133+
private static void ThicknessChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
134+
{
135+
var sender = d as RadialProgressBar;
136+
sender.RenderAll();
137+
}
138+
139+
// Render outline and progress segment when control is resized.
140+
private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
141+
{
142+
var self = sender as RadialProgressBar;
143+
self.RenderAll();
144+
}
145+
146+
private double ComputeNormalizedRange()
147+
{
148+
var range = Maximum - Minimum;
149+
var delta = Value - Minimum;
150+
var output = range == 0.0 ? 0.0 : delta / range;
151+
output = Math.Min(Math.Max(0.0, output), 0.9999);
152+
return output;
153+
}
154+
155+
// Compute size of ellipse so that the outer edge touches the bounding rectangle
156+
private Size ComputeEllipseSize()
157+
{
158+
var safeThickness = Math.Max(Thickness, 0.0);
159+
var width = Math.Max((ActualWidth - safeThickness) / 2.0, 0.0);
160+
var height = Math.Max((ActualHeight - safeThickness) / 2.0, 0.0);
161+
return new Size(width, height);
162+
}
163+
164+
// Render the segment representing progress ratio.
165+
private void RenderSegment()
166+
{
167+
if (!allTemplatePartsDefined)
168+
{
169+
return;
170+
}
171+
172+
var normalizedRange = ComputeNormalizedRange();
173+
174+
var angle = 2 * Math.PI * normalizedRange;
175+
var size = ComputeEllipseSize();
176+
var translationFactor = Math.Max(Thickness / 2.0, 0.0);
177+
178+
double x = (Math.Sin(angle) * size.Width) + size.Width + translationFactor;
179+
double y = (((Math.Cos(angle) * size.Height) - size.Height) * -1) + translationFactor;
180+
181+
barArc.IsLargeArc = angle >= Math.PI;
182+
barArc.Point = new Point(x, y);
183+
}
184+
185+
// Render the progress segment and the loop outline. Needs to run when control is resized or retemplated
186+
private void RenderAll()
187+
{
188+
if (!allTemplatePartsDefined)
189+
{
190+
return;
191+
}
192+
193+
var size = ComputeEllipseSize();
194+
var segmentWidth = size.Width;
195+
var translationFactor = Math.Max(Thickness / 2.0, 0.0);
196+
197+
outlineFigure.StartPoint = barFigure.StartPoint = new Point(segmentWidth + translationFactor, translationFactor);
198+
outlineArc.Size = barArc.Size = new Size(segmentWidth, size.Height);
199+
outlineArc.Point = new Point(segmentWidth + translationFactor - 0.05, translationFactor);
200+
201+
RenderSegment();
202+
}
203+
}
204+
}

0 commit comments

Comments
 (0)