1. 程式人生 > >New UWP Community Toolkit - RadialProgressBar

New UWP Community Toolkit - RadialProgressBar

pac ket stroke brush rep nbsp sys handle pri

概述

UWP Community Toolkit 中有一個圓形的進度條控件 - RadialProgressBar,本篇我們結合代碼詳細講解 RadialProgressBar 的實現。

RadialProgressBar 是一種圓形的進度條控件,進度值用圓形中的填充色的角度來表示,進度增長,填充色按照順時針方向增加,直到占滿整個圓形,則進度條達到最大值。我們來看一下官方的介紹和官網示例中的展示:

技術分享圖片

Source: https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/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!!!

New UWP Community Toolkit - RadialProgressBar