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 就用了这个方案。不过它的计算很麻烦,三角函数我已经忘光了。

另外,请注意弧线两端都是平平的直角,这和需求不符,而且微软的 文档 说过:

在 Windows 11 中,我们对窗口边框进行了圆角处理。 我们的用户研究团队发现,圆润的几何图形在心理上提供一种安全感,并且使应用的 UI 更易于扫描。 这使用户更少感觉威慑,也使应用更具吸引力。 圆角处理的量也是精心选择的。 我们公司对此进行了研究,努力在专业性、柔和感和吸引度之间取得平衡。

微软说得没错,这条弧线两端的直角确实让我感觉很没有有安全感。所以需要设置 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 的两端任然是直角,这不仅不符合需求,而且微软的 文档 说过:

在 Windows 11 中,我们对窗口边框进行了圆角处理。 我们的用户研究团队发现,圆润的几何图形在心理上提供一种安全感,并且使应用的 UI 更易于扫描。 这使用户更少感觉威慑,也使应用更具吸引力。 圆角处理的量也是精心选择的。 我们公司对此进行了研究,努力在专业性、柔和感和吸引度之间取得平衡。

微软又说对了,这个方案弧线两端的直角确实让我感觉到威胁。所以这个方案简单但不完美,我还要尝试下一个方案。

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 两个属性都不起作用。微软的 文档 说过:

在 Windows 11 中,我们对窗口边框进行了圆角处理。 我们的用户研究团队发现,圆润的几何图形在心理上提供一种安全感,并且使应用的 UI 更易于扫描。 这使用户更少感觉威慑,也使应用更具吸引力。 圆角处理的量也是精心选择的。 我们公司对此进行了研究,努力在专业性、柔和感和吸引度之间取得平衡。

微软永远都是对的,这个方案弧线两端的直角确实让我感觉没有吸引力。

对于用 StrokeDashArray 显示的边框,不能使用 StrokeStartLineCapStrokeEndLineCap 去控制它的两端的轮廓,而应该使用 StrokeDashCap:

StrokeDashCap="Round"

最终通过叠加两个 Ellipse 实现了户型进度条的需求:

5. 最后

童话和寓言都喜欢把相似的内容说上三次,例如三只小猪,三顾茅庐,弗利萨的三段变身。所以不是我在研究回字有多少种写法,我只是遵循古法想把一种技术讲透而已。

6. 参考

ArcSegment 类

几何图形

WPF 中的形状和基本图形概述

实用的Shape指南

7. 源码

[WPF] 使用三种方式实现弧形进度条的更多相关文章

  1. WPFの三种方式实现快捷键

    最近,对wpf添加快捷键的方式进行了整理.主要用到的三种方式如下: 一.wpf命令: 资源中添加命令 <Window.Resources> <RoutedUICommand x:Ke ...

  2. WPF中实现PropertyGrid(用于展示对象的详细信息)的三种方式

    原文:WPF中实现PropertyGrid(用于展示对象的详细信息)的三种方式 由于WPF中没有提供PropertyGrid控件,有些业务需要此类的控件.这篇文章介绍在WPF中实现PropertyGr ...

  3. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

  4. github下载源码的三种方式

      从github上下载源码的三种方式 CreationTime--2018年6月7日15点21分 Author:Marydon 1.情景展示 2.实现方式 方式一:直接点击"Downloa ...

  5. Linux 软件安装的三种方式

    Linux 软件安装的三种方式 1.yum ​ 语法格式: ​ yum -y install package.name ​ -y yes # 遇到提示自动输入yes ​ 案例: 安装ifconfig命 ...

  6. 监视EntityFramework中的sql流转你需要知道的三种方式Log,SqlServerProfile, EFProfile

    大家在学习entityframework的时候,都知道那linq写的叫一个爽,再也不用区分不同RDMS的sql版本差异了,但是呢,高效率带来了差灵活性,我们 无法控制sql的生成策略,所以必须不要让自 ...

  7. iOS字体加载三种方式

    静态加载 动态加载 动态下载苹果提供的多种字体 其他 打印出当前所有可用的字体 检查某字体是否已经下载 这是一篇很简短的文章,介绍了 iOS 自定义字体加载的三种方式. 静态加载 这个可以说是最简单最 ...

  8. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  9. 【整理】Linux下中文检索引擎coreseek4安装,以及PHP使用sphinx的三种方式(sphinxapi,sphinx的php扩展,SphinxSe作为mysql存储引擎)

          一,软件准备 coreseek4.1 (包含coreseek测试版和mmseg最新版本,以及测试数据包[内置中文分词与搜索.单字切分.mysql数据源.python数据源.RT实时索引等测 ...

随机推荐

  1. 变着花样来接参,PHP中接收外部参数的方式

    对于PHP这样一个web语言来说,接参是非常重要的一个能力.毕竟从前端表单或异步请求传递上来的数据都要获取到才能进行正常的交互展示.当然,这也是所有能够进行web开发的语言的必备能力.今天我们就来看看 ...

  2. C博客作业00--顺序分支结构

    这个作业属于哪个班级 C语言--网络2011/2012 这个作业的地址 C博客作业00--顺序分支结构 这个作业的目标 初步认识C语法,掌握数据表达.printf.scanf语法及分支结构内容 0.展 ...

  3. 如何在word中美观地插入编程代码

    零.缘起 在整理Java笔记时,想把代码直接贴到word文档中,原来一直截图很麻烦,所以找到以下方法. 思想:问题比答案更重要!你能想到问题,才知道去百度搜索. 一.打开网站 http://www.p ...

  4. python学习笔记(十四)python实现发邮件

    import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart u ...

  5. 鸿蒙内核源码分析(汇编汇总篇) | 所有的汇编代码都在这里 | 百篇博客分析OpenHarmony源码 | v40.03

    百篇博客系列篇.本篇为: v40.xx 鸿蒙内核源码分析(汇编汇总篇) | 汇编可爱如邻家女孩 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...

  6. 如何基于Security实现OIDC单点登录?

    一.说明 本文主要是给大家介绍 OIDC 的核心概念以及如何通过对 Spring Security 的授权码模式进行扩展来实现 OIDC 的单点登录. OIDC 是 OpenID Connect 的简 ...

  7. PHP审计之POP链挖掘

    PHP审计之POP链挖掘 前言 续上文中的php反序列化,继续来看,这个POP的挖掘思路.在其中一直构思基于AST去自动化挖掘POP链,迫于开发能力有限.没有进展,随后找到了一个别的师傅已经实现好的项 ...

  8. 虚拟机Parallels Desktop 17 (PD17)支持M1 自己动手制作启动器解锁

    个人博客:xzajyjs.cn 如果自己有能力的话,直接查看这个视频即可.点此 前段时间刚出pd17,作为mac上最最强(没有之一)的虚拟机,版本17更是更进一步,性能提升极大,更是支持了Monter ...

  9. 分布式全局ID与分布式事务

    1. 概述 老话说的好:人不可貌相,海水不可斗量.以貌取人是非常不好的,我们要平等的对待每一个人. 言归正传,今天我们来聊一下分布式全局 ID 与分布式事务. 2. 分布式全局ID 2.1 分布式数据 ...

  10. 【c++ Prime 学习笔记】第4章 表达式

    表达式由一个或多个运算对象组成,对表达式求值返回结果. 字面值和变量是最简单的表达式 把运算符和运算对象组合可得到复杂表达式. 4.1 基础 4.1.1 基本概念 一元运算符作用于一个对象,如取地址符 ...