WPF 控件庫系列博文地址:html
WPF 控件庫——仿製Chrome的ColorPickergit
WPF 控件庫——仿製Windows10的進度條github
WPF 控件庫——可拖動選項卡的TabControlthis
1、其實有現成的編碼
先來看看Windows10進度條的兩種模式:spa
網上有很多介紹仿製Windows10進度條的文章,也都實現了不錯的效果。而我再開一文的緣由是以爲若是在這基礎上添加一些功能,好比圓點的數量,圓點的大小等等,效果可能會更好一些。接觸過UWP的朋友應該知道,其框架中自帶了進度條控件,以 ProgressRing 爲例,經過Blend,咱們能夠獲取到控件的XAML,如下是部分截圖:3d
粗略一看,只要稍做修改便能用到WPF中——咱們幾乎能夠什麼都不作!code
2、添加功能
若是要更改圓點的數量,圓點的大小或者圓點的移動速度,咱們該如何實現呢?繼承章節一中的XAML,並根據所需調整模板就顯得太麻煩了,這會讓咱們的樣式文件顯得臃腫不堪,因此採用純粹的C#代碼來實現它或許比較明智。不過以前的XAML也不是一無可取,至少它給出了環形進度條的關鍵幀動畫的構成,這些信息對咱們來講很重要,免去了咱們本身去分析的步驟。
如今咱們的主要工做就是讓寫死的關鍵幀可以經過屬性靈活配置,因此咱們可能須要先編碼一份進度條的基類( LoadingBase ),以提取兩種類型進度條的共性。基類中定義8個屬性,分別是 IsRunning 、 DotCount 、 DotInterval 、 DotBorderBrush 、 DotBorderThickness 、 DotDiameter 、 DotSpeed 、 DotDelayTime ,它們的含義已是自注釋的,沒必要贅述。而在環形進度條中,還有另外兩個屬性: DotOffSet 和 NeedHidden ,分別表示圓點總體的位置偏移和在運動中是否須要隱藏圓點。
3、關鍵幀動畫
最後一步就是用C#代碼實現關鍵幀動畫,不過得先有米才能作飯,故而須要先建立圓點:
1 protected Ellipse CreateEllipse(int index) 2 { 3 var ellipse = new Ellipse(); 4 ellipse.SetBinding(WidthProperty, new Binding("DotDiameter") {Source = this}); 5 ellipse.SetBinding(HeightProperty, new Binding("DotDiameter") {Source = this}); 6 ellipse.SetBinding(Shape.FillProperty, new Binding("Foreground") {Source = this}); 7 ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding("DotBorderThickness") {Source = this}); 8 ellipse.SetBinding(Shape.StrokeProperty, new Binding("DotBorderBrush") {Source = this}); 9 return ellipse; 10 }
上面的方法在進度條基類中實現,僅僅是用相關的屬性初始化了咱們的原材料:圓點。因爲環形進度條在X、Y軸方向都有移動,因此爲了方便,咱們能夠考慮在圓點外面再包一層 Border 做爲看不見的殼,咱們將圓點與殼底部對齊,如今只要讓殼繞中心旋轉就基本實現了目標,下面是環形進度條1個點到5個點帶殼的示意圖:
想想,若是沒有這層殼,咱們又有什麼替代方法,這些方法是否都是極爲方便的?可能沒有這層殼,就須要去琢磨怎麼改變圓點的 RenderTransformOrigin ,好讓它們看起來都是圍繞一個點旋轉的,即便改變了進度條總體的尺寸。套殼的代碼以下:
1 private Border CreateBorder(int index) 2 { 3 var ellipse = CreateEllipse(index); 4 ellipse.HorizontalAlignment = HorizontalAlignment.Center; 5 ellipse.VerticalAlignment = VerticalAlignment.Bottom; 6 var rt = new RotateTransform 7 { 8 Angle = -DotInterval * index 9 }; 10 var myTransGroup = new TransformGroup(); 11 myTransGroup.Children.Add(rt); 12 var border = new Border 13 { 14 RenderTransformOrigin = new Point(0.5, 0.5), 15 RenderTransform = myTransGroup, 16 Child = ellipse, 17 Visibility = NeedHidden ? Visibility.Collapsed : Visibility.Visible 18 }; 19 border.SetBinding(WidthProperty, new Binding("Width") { Source = this }); 20 border.SetBinding(HeightProperty, new Binding("Height") { Source = this }); 21 22 return border; 23 }
套殼代碼除了套殼和相關的初始化,最重要的是19和20行的寬高綁定,這是讓圓點旋轉中心始終惟一的關鍵。有了以上的準備,咱們終於能夠開始for循環了:
1 //定義動畫 2 Storyboard = new Storyboard 3 { 4 RepeatBehavior = RepeatBehavior.Forever 5 }; 6 7 for (var i = 0; i < DotCount; i++) 8 { 9 //在這裏建立圓點 10 }
下面就是最核心的關鍵幀動畫,經過以前用Blend提取出來的XAML,咱們能夠看到它使用了 SplineDoubleKeyFrame ,這會涉及三次貝塞爾曲線的控制點,考慮到易用性,咱們會用 LinearDoubleKeyFrame 和 EasingDoubleKeyFrame 代替。在XAML中咱們最關心的關鍵字應該是角度,在時間片的哪部分,圓點應該在哪兒,而又在何時,圓點應該會消失,咱們只要隨意截取兩個點的關鍵幀就能得到以上全部信息:
上面兩張分別是圓點1和2透明度和位置的關鍵幀截圖,經過兩個點咱們徹底能夠推斷全部點。出於我的喜愛,我將透明度替換成了 Visibility 的切換,因此還會引入 DiscreteObjectKeyFrame 。篇幅緣由,咱們直接總結分析結果:
從上面7張圖中能夠看出,在一次循環中點1是這樣運動的:減速、勻速、加速、減速、勻速、加速,並且與之對應的角度位置也給出了,最後水到渠成,環形進度條就完成了。
4、截圖
經過設置不一樣的屬性,能夠實現不一樣的效果:
5、源碼
本文所討論的進度條源碼已經在github開源:https://github.com/NaBian/HandyControl