博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF 控件库——仿制Windows10的进度条
阅读量:5788 次
发布时间:2019-06-18

本文共 3535 字,大约阅读时间需要 11 分钟。

原文:

一、其实有现成的

  先来看看Windows10进度条的两种模式:

 

 

  网上有不少介绍仿制Windows10进度条的文章,也都实现了不错的效果。而我再开一文的原因是觉得如果在这基础上添加一些功能,比如圆点的数量,圆点的大小等等,效果可能会更好一些。接触过UWP的朋友应该知道,其框架中自带了进度条控件,以 ProgressRing 为例,通过Blend,我们可以获取到控件的XAML,以下是部分截图:

 

  粗略一看,只要稍作修改便能用到WPF中——我们几乎可以什么都不做!

 

二、添加功能

  如果要更改圆点的数量,圆点的大小或者圆点的移动速度,我们该如何实现呢?继承章节一中的XAML,并根据所需调整模板就显得太麻烦了,这会让我们的样式文件显得臃肿不堪,所以采用纯粹的C#代码来实现它或许比较明智。不过之前的XAML也不是一无是处,至少它给出了环形进度条的关键帧动画的构成,这些信息对我们来说很重要,免去了我们自己去分析的步骤。

  现在我们的主要工作就是让写死的关键帧能够通过属性灵活配置,所以我们可能需要先编码一份进度条的基类( LoadingBase ),以提取两种类型进度条的共性。基类中定义8个属性,分别是 IsRunning 、 DotCount 、 DotInterval 、 DotBorderBrush 、 DotBorderThickness 、 DotDiameter 、 DotSpeed 、 DotDelayTime ,它们的含义已经是自注释的,不必赘述。而在环形进度条中,还有另外两个属性: DotOffSet 和 NeedHidden ,分别表示圆点整体的位置偏移和在运动中是否需要隐藏圆点。

 

三、关键帧动画

  最后一步就是用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 Border13             {14                 RenderTransformOrigin = new Point(0.5, 0.5),15                 RenderTransform = myTransGroup,16                 Child = ellipse,17                 Visibility = NeedHidden ? Visibility.Collapsed : Visibility.Visible18             };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 。篇幅原因,我们直接总结分析结果:

  • 一开始所有点都是显示的,但是位置不同,从点1的-110度开始,角度逐个减6;
  • 点1开始运动后,0.167秒(1/6秒)后点2开始运动,所以各点动画延迟时间为1/6秒(这里不太能确定是否和圆点数量有关);
  • 以点1为例,旋转角度随时间变化图如下:

  从上面7张图中可以看出,在一次循环中点1是这样运动的:减速、匀速、加速、减速、匀速、加速,而且与之对应的角度位置也给出了,最后水到渠成,环形进度条就完成了。

 

四、截图

  通过设置不同的属性,可以实现不同的效果:

  

 

五、源码

  本文所讨论的进度条源码已经在github开源:

转载地址:http://jfqyx.baihongyu.com/

你可能感兴趣的文章
MVC模式利用xib文件定制collectionCell
查看>>
(六)Oracle学习笔记—— 约束
查看>>
【SQL】查询数据库中某个字段有重复值出现的信息
查看>>
mysql 行转列 列转行
查看>>
[Oracle]如何在Oracle中设置Event
查看>>
top.location.href和localtion.href有什么不同
查看>>
02-创建hibernate工程
查看>>
Open Graph Protocol(开放内容协议)
查看>>
模块化(1):基本思路
查看>>
Ubuntu18.04中配置QT5.11开发环境
查看>>
Exception的妙用
查看>>
基于浏览器的开源“管理+开发”工具,Pivotal MySQL*Web正式上线!
查看>>
JavaScript(五):变量的作用域
查看>>
知识图谱在互联网金融中的应用
查看>>
MySQL 到底能不能放到 Docker 里跑?
查看>>
wpf 自定义窗口,最大化时覆盖任务栏解决方案
查看>>
【docker】关于docker 中 镜像、容器的关系理解
查看>>
information_schema系列五(表,触发器,视图,存储过程和函数)
查看>>
瓜子二手车的谎言!
查看>>
[转]使用Git Submodule管理子模块
查看>>