概述 UWP Community Toolkit 中有一個圓形的進度條控制項 - RadialProgressBar,本篇我們結合代碼詳細講解 RadialProgressBar 的實現。 RadialProgressBar 是一種圓形的進度條控制項,進度值用圓形中的填充色的角度來表示,進度增長,填充色 ...
概述
UWP Community Toolkit 中有一個圓形的進度條控制項 - RadialProgressBar,本篇我們結合代碼詳細講解 RadialProgressBar 的實現。
RadialProgressBar 是一種圓形的進度條控制項,進度值用圓形中的填充色的角度來表示,進度增長,填充色按照順時針方向增加,直到占滿整個圓形,則進度條達到最大值。我們來看一下官方的介紹和官網示例中的展示:
Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/radialprogressbar
Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;
開發過程
代碼分析
我們來看一下 RadialProgressBar 控制項的結構:
- RadialProgressBar.cs - RadialProgressBar 控制項定義類
- RadialProgressBar.xaml - RadialProgressBar 控制項樣式
1. RadialProgressBar.xaml
這是 RadialProgressBar 控制項的樣式,我們可以看到 Template 部分由 OutlineFigurePart 和 BarFigurePart 組成,分別代表了進度條的灰色底和實際的進度條,因為兩個部分的樣式基本一致,所以我們省略了一部分。
可以看到,兩個部分的樣式組成,都是一個 Path 的幾何圖形,裡面包含了 ParhFigure,它的 segment 屬性包含了 ArcSegment:一個弧度區段;這就是樣式的基本組成了。
<Style TargetType="local:RadialProgressBar" > <Setter Property="Foreground" Value="{ThemeResource SystemControlHighlightAccentBrush}" /> <Setter Property="Outline" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" /> <Setter Property="Background" Value="Transparent" /> <Setter Property="Thickness" Value="4"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:RadialProgressBar"> <Grid Background="{TemplateBinding Background}"> <!-- OutlineFigurePart of progress bar --> <Path Fill="Transparent" Stroke="{TemplateBinding Outline}" StrokeThickness="{TemplateBinding Thickness}" StrokeDashCap="Flat"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigureCollection> <PathFigure x:Name="OutlineFigurePart"> <PathFigure.Segments> <PathSegmentCollection> <ArcSegment x:Name="OutlineArcPart" IsLargeArc="True" SweepDirection="Clockwise"/> </PathSegmentCollection> </PathFigure.Segments> </PathFigure> </PathFigureCollection> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> <!-- BarFigurePart of Progress Bar --> ... </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
2. RadialProgressBar.cs
看一下這個類的構成:
RadialProgressBar 類繼承自 ProgressBar 類,表現形式為圓形的進度條,分為 outline 和 bar 兩個部分,所以可以看到類中定義了 outlineFigure、barFigure、outlineArc 和 barArc 屬性;而依賴屬性有:
- Thickness - 表示圓形進度條的圓環大小,預設為 0,xaml 中定義為 4
- Outline - 表示圓形底的畫刷,預設為 transparent,xaml 中定義為 gray
而繼承自 ProgressBar 的 Background 和 Foreground,則分別表示進度條中間空白部分的顏色,和進度條的進度顏色。因為繼承自 ProgressBar 類,所以重載了 Progress 類的幾個方法:
- OnMinimumChanged(old, new) - 進度條最小值變化的處理方法,會觸發 RenderSegment() 方法;
- OnMaximumChanged(old, new) - 進度條最大值變化的處理方法,會觸發 RenderSegment() 方法;
- OnValueChanged(old, new) - 進度條進度值變化的處理方法,會觸發 RenderSegment() 方法;
- OnApplyTemplate() - 應用模板或哦模板改變時,更新控制項的視覺顯示 ,會觸發 RenderAll() 方法;
還有兩個 Changed 處理方法:ThicknessChangedHandler(d, e) 和 SizeChangedHandler(s, e),分別處理進度條寬度變化和進度條尺寸變化,也會觸發 RenderAll() 方法;
下麵來看看幾個主要的方法:
① ComputeNormalizedRange()
根據進度條的最大值和最小值計算出的區間,以及當前值,計算出當前值在區間中占的百分比,如果當前值 > 0.999, 則取值 0.999
private double ComputeNormalizedRange() { var range = Maximum - Minimum; var delta = Value - Minimum; var output = range == 0.0 ? 0.0 : delta / range; output = Math.Min(Math.Max(0.0, output), 0.9999); return output; }
② ComputeEllipseSize()
計算圓形的尺寸,根據進度條的實際寬度和高度,去掉安全寬度,計算後值的 1/2 就是 Ellipse 的長短半徑;
private Size ComputeEllipseSize() { var safeThickness = Math.Max(Thickness, 0.0); var width = Math.Max((ActualWidth - safeThickness) / 2.0, 0.0); var height = Math.Max((ActualHeight - safeThickness) / 2.0, 0.0); return new Size(width, height); }
③ RenderSegment()
弧形區段的實際渲染,根據當前角度,尺寸和圓環寬度,計算出當前弧形的終點坐標;同時輸出一個值:IsLargeArc,角度是否 >= 180 度。
private void RenderSegment() { if (!allTemplatePartsDefined) { return; } var normalizedRange = ComputeNormalizedRange(); var angle = 2 * Math.PI * normalizedRange; var size = ComputeEllipseSize(); var translationFactor = Math.Max(Thickness / 2.0, 0.0); double x = (Math.Sin(angle) * size.Width) + size.Width + translationFactor; double y = (((Math.Cos(angle) * size.Height) - size.Height) * -1) + translationFactor; barArc.IsLargeArc = angle >= Math.PI; barArc.Point = new Point(x, y); }
④ RenderAll()
渲染進度條的全部控制項部分,計算 outlineFigure 和 barFigure 的起始點,new Point(segmentWidth + translationFactor, translationFactor) 也就是圓形最上方的橫向中心點;然後計算 outlineArc 和 barArc 的尺寸,也就是圓形半徑;outlineArc 的角度固定,所以只需要給一個初始值,最後是調用 RenderSegment() 方法計算 Bar 的實際渲染部分。
private void RenderAll() { if (!allTemplatePartsDefined) { return; } var size = ComputeEllipseSize(); var segmentWidth = size.Width; var translationFactor = Math.Max(Thickness / 2.0, 0.0); outlineFigure.StartPoint = barFigure.StartPoint = new Point(segmentWidth + translationFactor, translationFactor); outlineArc.Size = barArc.Size = new Size(segmentWidth, size.Height); outlineArc.Point = new Point(segmentWidth + translationFactor - 0.05, translationFactor); RenderSegment(); }
調用示例
我們定義了一個 RadialProgressBar 控制項,底色是淺灰色,進度顏色是綠色,區間是 0~100,當前值是 29,進度條寬度是 20;從示例的運行圖中可以印證這些數據。
<controls:RadialProgressBar x:Name="RadialProgressBarControl" Grid.Column="1" Value="29" Foreground="Green" Thickness="20" Minimum="0" Maximum="100" Width="200" Height="200" Outline="LightGray"/>
總結
到這裡我們就把 UWP Community Toolkit 中的 RadialProgressBar 控制項的源代碼實現過程和簡單的調用示例講解完成了,希望能對大家更好的理解和使用這個控制項有所幫助;大家也可以基於簡單的圓形進度條,擴展出更多中不同形狀的進度條,例如矩形,實心圓形等等,歡迎大家多多交流,謝謝!
最後,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通過微博關註最新動態。
衷心感謝 UWPCommunityToolkit 的作者們傑出的工作,Thank you so much, UWPCommunityToolkit authors!!!