[WPF] 使用三種方式實現弧形進度條


1. 需求

前天看到有人問弧形進度條怎么做,我模仿了一下,成果如下圖所示:

當時我第一反應是可以用 Microsoft.Toolkit.Uwp.UI.Controls 里的 RadialGauge 實現,雖然這是個 UWP 的控件,不過代碼沒有很復雜,應該很輕松就能移植到 WPF:

但仔細想想,我實現過很多次圓形的進度條,這種弧形的進度條則沒碰過。原型進度條基本只需要用 Ellipse 就能實現,而且只需要 Progress 一個參數,而弧形進度條則還需要 StartAngle 和 EndAngle 兩個屬性,而且計算復雜許多。於是興致來了試試用不同的方式實現弧形進度條。

這篇文章只介紹了怎么顯示弧形及怎么顯示進度,只有原理,沒有具體實現一個弧形進度條控件。

2. 使用 Path 及 ArcSegment

Path 用於繪制曲線和復雜形狀,而且 ArcSegment 用於描述 Path 中兩點之間的一條橢圓弧。通常使用以下幾個屬性控制 ArcSegment:

屬性 描述
Point 終點(起始點在 Path 或前一個 Segment 中描述)。
Size X 軸和 Y 軸的半徑。
IsLargeArc 圓弧是整個圓形中大的那部分,還是小的那部分。
SweepDirection 弧線繪制的方向。

具體說明可以看 這個文檔

用 Path 和 ArcSegment 可以很好地實現弧形的進度條,它的 XAML 如下:

<Path Stroke="SlateBlue"
      StrokeThickness="4">
    <Path.Data>
        <PathGeometry>
            <PathFigure IsClosed="False" StartPoint="30,170">
                <ArcSegment IsLargeArc="True"
                            Point="170,170"
                            Size="96,96"
                            SweepDirection="Clockwise" />
            </PathFigure>
        </PathGeometry>
    </Path.Data>
</Path>

疊加兩個不同顏色的 Path,就可以實現這種效果:

Path 和 ArcSegment 是一個很正統的方案,前面提到的 RadialGauge 就用了這個方案。不過它的計算很麻煩,三角函數我已經忘光了。

另外,請注意弧線兩端都是平平的直角,這和需求不符,所以需要設置 StrokeStartLineCapStrokeEndLineCap 這兩個屬性的值為 Round

StrokeStartLineCap="Round" StrokeEndLineCap="Round"

它們控制線條兩端邊緣的輪廓,Round 表示一個直徑等於線條粗細的半圓形。這樣才能實現需求中的圓角:

順便一提,這兩個屬性的類型是 PenLineCap 枚舉,這個枚舉的四個值分別代表以下幾種形狀:

3. 使用 Arc

第二個方案是使用 Microsoft.Expression.Drawing 中的 Arc 形狀直接畫出一個弧形。如果安裝了舊版的 Blend(好像 2017 或以前的都可以),可以在 資產->形狀 里找到這個形狀(我裝的是英文版所以沒有中文截圖):

或者在 Nuget 上搜索 Microsoft.Expression.Drawing 找到一個符合自己項目的版本。

Arc 的用法很簡單,只需要執行 StartAngleEndAngle 即可輸出一個弧形:

<ed:Arc ArcThickness="12"
        ArcThicknessUnit="Pixel"
        EndAngle="150"
        Fill="#101a26"
        StartAngle="-150"
        Stretch="None"
        StrokeEndLineCap="Round"
        StrokeStartLineCap="Round" />

疊加兩個不同顏色的 Arc,可以實現這種效果:

可是仔細看,就算用了 StrokeStartLineCapStrokeEndLineCap 兩個屬性,Arc 的兩端任然是直角,這不符合需求,所以這個方案簡單但不完美,我還要嘗試下一個方案。

4. 使用 Ellipse

這個方案還算有趣,Ellipse 明明是圓形,卻能用來畫弧形。為了用 Ellipse 顯示進度,我們會用 StrokeDashArray 控制它的邊框長度。StrokeDashArray 用於將邊框變成虛線,它的值是一個 double 類型的有序集合,集合中的值指虛線中每一段的長度,長度單位是邊框值的寬度。例如以下圓形:

<Ellipse StrokeDashArray="1,2,3"
         Stroke="#FFFF0EC4"
         StrokeThickness="10"
         Height="200"
         Width="200" />

邊框寬度為 10,虛線的第一段是長度為 10 的實線,第二段為長度為 20 的空白,第三段為長度為 30 的實線,然后如此循環直到結束。

用 StrokeDashArray 做進度提示的基本做法就是將進度(Progress)通過 Converter 轉換為分成兩段的 StrokeDashArray,第一段為實線,表示當前進度,第二段為空白。假設一個 Shape 的邊長是 100,當前進度為 50,則將 StrokeDashArray 設置成 {50,double.MaxValue} 兩段。

為了實現弧形進度條,我們還需要控制 Ellipse 旋轉的角度。具體來說我實現了一個 EllipseProgressBehavior,里面有 Progress、StartAngle 和 EndAngle 三個屬性,具體代碼在 這里。用這個 Behavior 控制 Ellipse 的邊框長度和旋轉角度,使用方式如下:

<Ellipse Margin="4"
         Stroke="#7bcdd9"
         StrokeThickness="4">
    <interactivity:Interaction.Behaviors>
        <local1:EllipseProgressBehavior EndAngle="150"
                                        Progress="50"
                                        StartAngle="-150" />
    </interactivity:Interaction.Behaviors>
</Ellipse>

疊加兩個 Ellipse,即可實現需求中的弧形進度條。可是這時候弧形的兩端都是直角,即使設置了 StrokeStartLineCapStrokeEndLineCap 兩個屬性都不起作用。對於用 StrokeDashArray 顯示的邊框,不能使用 StrokeStartLineCapStrokeEndLineCap 去控制它的兩端的輪廓,而應該使用 StrokeDashCap:

StrokeDashCap="Round"

最終通過疊加兩個 Ellipse 實現了戶型進度條的需求:

5. 最后

童話和寓言都喜歡把相似的內容說上三次,例如三只小豬,三顧茅廬,弗利薩的三段變身。所以不是我在研究回字有多少種寫法,我只是遵循古法想把一種技術講透而已。

6. 參考

ArcSegment 類

幾何圖形

WPF 中的形狀和基本圖形概述

實用的Shape指南

7. 源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM