【WPF】环形进度条

【【WPF】环形进度条】WPF中自带有长条形的进度条,大部分场景都算适用,但是仍然有一部分空间小的场景不太合适,此时我们想到安卓上常用的环形进度条,美观,又不占空间。
那么WPF中的环形进度条控件在哪呢?
很遗憾,自带组件中没有,这需要我们自己绘制。
环形进度条的核心在于根据百分比绘制弧形长度,在WPF中,PathGeometry可以绘制复杂形状,其中ArcSegment专门用于画弧形。
示例工程直接下载:https://download.csdn.net/download/u010875635/10791096
示例动画:
【WPF】环形进度条
文章图片


ArcSegment的弧形有几个重要参数,起点(一般在PathFigure中设置,ArcSegment属于PathFigure一部分)、两个半径、是否优势弧(大于180度为优势弧)、终点、旋转角度等。
我们根据角度百分比计算弧长终点,由于弧长虽然是线性增长,但对应的终点X、Y坐标并非是线性变化,我们需要区分4个象限来计算终点。
我们将弧形放置在一个方形的Grid中,半边长为R,设置弧形进度条的粗细为x,设置弧形半径为r(r要小于R-x),起始点坐标为 (leftStart, topStart)
一、第一象限终点计算
【WPF】环形进度条
文章图片


第一象限终点坐标为:X = leftStart + r*cos(α); Y = topStart + r*sin(α);

二、第二象限终点计算 【WPF】环形进度条
文章图片

第二象限终点坐标为:X = leftStart + r*cos(α); Y = topStart + r + r*sin(α);

三、第三象限终点计算 【WPF】环形进度条
文章图片

第三象限终点坐标为:X = leftStart- r*sin(α); Y = topStart + r + r*cos(α);

四、第四象限终点计算 【WPF】环形进度条
文章图片

第四象限终点坐标为:X = leftStart- r*cos(α); Y = topStart + r - r*sin(α);
注意100%时,终点与起点不要重合,最好偏移极小数值(太大弧形不好看),然后闭合图形,否则无法组合成圆。

详细代码如下: 一、自定义控件 前台:


后台:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace BeatfanControls.ProcessBars { /// /// CycleProcessBar1.xaml 的交互逻辑 /// public partial class CycleProcessBar1 : UserControl { public CycleProcessBar1() { InitializeComponent(); }public double CurrentValue1 { set { SetValue(value); } }/// /// 设置百分百,输入小数,自动乘100 /// /// private void SetValue(double percentValue) { /***************************************** 方形矩阵边长为34,半长为17 环形半径为14,所以距离边框3个像素 环形描边3个像素 ******************************************/ double angel = percentValue * 360; //角度double radius = 14; //环形半径//起始点 double leftStart = 17; double topStart = 3; //结束点 double endLeft = 0; double endTop = 0; //数字显示 lbValue.Content = (percentValue*100).ToString("0") + "%"; /*********************************************** * 整个环形进度条使用Path来绘制,采用三角函数来计算 * 环形根据角度来分别绘制,以90度划分,方便计算比例 ***********************************************/bool isLagreCircle = false; //是否优势弧,即大于180度的弧形//小于90度 if (angel <= 90) { /***************** * ** * * ra * * * * * * * * * * * * ******************/ double ra = (90 - angel) * Math.PI / 180; //弧度 endLeft = leftStart + Math.Cos(ra) * radius; //余弦横坐标 endTop = topStart + radius - Math.Sin(ra) * radius; //正弦纵坐标}else if (angel <= 180) { /***************** * * * * * * * * * * * * * * ra ** ** ******************/double ra = (angel - 90) * Math.PI / 180; //弧度 endLeft = leftStart + Math.Cos(ra) * radius; //余弦横坐标 endTop = topStart + radius + Math.Sin(ra) * radius; //正弦纵坐标 }else if (angel <= 270) { /***************** * * * * * * * * * * * * * * *ra* ** ******************/ isLagreCircle = true; //优势弧 double ra = (angel - 180) * Math.PI / 180; endLeft = leftStart - Math.Sin(ra) * radius; endTop = topStart + radius + Math.Cos(ra) * radius; }else if (angel < 360) { /***************** ** ** ra * * * * * * * * * * * * * * ******************/ isLagreCircle = true; //优势弧 double ra = (angel - 270) * Math.PI / 180; endLeft = leftStart - Math.Cos(ra) * radius; endTop = topStart + radius - Math.Sin(ra) * radius; } else { isLagreCircle = true; //优势弧 endLeft = leftStart-0.001; //不与起点在同一点,避免重叠绘制出非环形 endTop = topStart; }Point arcEndPt = new Point(endLeft, endTop); //结束点 Size arcSize = new Size(radius, radius); SweepDirection direction = SweepDirection.Clockwise; //顺时针弧形 //弧形 ArcSegment arcsegment = new ArcSegment(arcEndPt, arcSize, 0, isLagreCircle, direction, true); //形状集合 PathSegmentCollection pathsegmentCollection = new PathSegmentCollection(); pathsegmentCollection.Add(arcsegment); //路径描述 PathFigure pathFigure = new PathFigure(); pathFigure.StartPoint = new Point(leftStart, topStart); //起始地址 pathFigure.Segments = pathsegmentCollection; //路径描述集合 PathFigureCollection pathFigureCollection = new PathFigureCollection(); pathFigureCollection.Add(pathFigure); //复杂形状 PathGeometry pathGeometry = new PathGeometry(); pathGeometry.Figures = pathFigureCollection; //Data赋值 myCycleProcessBar1.Data = https://www.it610.com/article/pathGeometry; //达到100%则闭合整个 if (angel == 360) myCycleProcessBar1.Data = Geometry.Parse(myCycleProcessBar1.Data.ToString() +" z"); } } }


二、调用: 前台:

后台:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace CircleProgressBar { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { /// /// 定时器 /// private System.Windows.Threading.DispatcherTimer m_Timer1 = new System.Windows.Threading.DispatcherTimer(); double m_Percent = 0; bool m_IsStart = false; public MainWindow() { InitializeComponent(); m_Timer1.Interval = new TimeSpan(0, 0, 0,0,100); m_Timer1.Tick += M_Timer1_Tick; }private void M_Timer1_Tick(object sender, EventArgs e) { m_Percent += 0.01; if (m_Percent > 1) { m_Percent = 1; m_Timer1.Stop(); m_IsStart = false; StartChange(m_IsStart); } circleProgressBar.CurrentValue1 = m_Percent; }/// /// UI变化 /// /// private void StartChange(bool bState) { if (bState) btnStart.Content = "停止"; else btnStart.Content = "开始"; }private void Button_Click(object sender, RoutedEventArgs e) { if (m_IsStart) { m_Timer1.Stop(); m_IsStart = false; } else { m_Percent = 0; m_Timer1.Start(); m_IsStart = true; } StartChange(m_IsStart); }private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { m_Timer1.Stop(); } } }




    推荐阅读