【【WPF】环形进度条】WPF中自带有长条形的进度条,大部分场景都算适用,但是仍然有一部分空间小的场景不太合适,此时我们想到安卓上常用的环形进度条,美观,又不占空间。
那么WPF中的环形进度条控件在哪呢?
很遗憾,自带组件中没有,这需要我们自己绘制。
环形进度条的核心在于根据百分比绘制弧形长度,在WPF中,PathGeometry可以绘制复杂形状,其中ArcSegment专门用于画弧形。
示例工程直接下载:https://download.csdn.net/download/u010875635/10791096
示例动画:
文章图片
ArcSegment的弧形有几个重要参数,起点(一般在PathFigure中设置,ArcSegment属于PathFigure一部分)、两个半径、是否优势弧(大于180度为优势弧)、终点、旋转角度等。
我们根据角度百分比计算弧长终点,由于弧长虽然是线性增长,但对应的终点X、Y坐标并非是线性变化,我们需要区分4个象限来计算终点。
我们将弧形放置在一个方形的Grid中,半边长为R,设置弧形进度条的粗细为x,设置弧形半径为r(r要小于R-x),起始点坐标为 (leftStart, topStart)
一、第一象限终点计算
文章图片
第一象限终点坐标为:X = leftStart + r*cos(α);
Y = topStart + r*sin(α);
二、第二象限终点计算
文章图片
第二象限终点坐标为:X = leftStart + r*cos(α);
Y = topStart + r + r*sin(α);
三、第三象限终点计算
文章图片
第三象限终点坐标为:X = leftStart- r*sin(α);
Y = topStart + r + r*cos(α);
四、第四象限终点计算
文章图片
第四象限终点坐标为: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();
}
}
}
推荐阅读
- 【C】题目|【C语言】题集 of ⑥
- 游戏|2022年如何学习前端前沿技术,破卷而出()
- 分布式|《Python3网络爬虫开发实战(第二版)》内容介绍
- 机器学习|机器学习Sklearn学习总结
- Python|Python实战(使用线性回归预测房价)
- docker|Docker
- 腾讯|SaaS的收入模型有哪些(终于有人讲明白了)
- python|Python绘制冬奥吉祥物“冰墩墩”
- 大数据|【新书速递】流量运营教科书
- 编程语言|TIOBE 5月编程语言排行(Python再次挤掉Java,夺下榜二!)