【WPF】環形進度條
WPF中自帶有長條形的進度條,大部分場景都算適用,但是仍然有一部分空間小的場景不太合適,此時我們想到安卓上常用的環形進度條,美觀,又不佔空間。
那麼WPF中的環形進度條控制元件在哪呢?
很遺憾,自帶元件中沒有,這需要我們自己繪製。
環形進度條的核心在於根據百分比繪製弧形長度,在WPF中,PathGeometry可以繪製複雜形狀,其中ArcSegment專門用於畫弧形。
示例工程直接下載:https://download.csdn.net/download/u010875635/10791096
示例動畫:
ArcSegment的弧形有幾個重要引數,起點(一般在PathFigure中設定,ArcSegment屬於PathFigure一部分)、兩個半徑
我們根據角度百分比計算弧長終點,由於弧長雖然是線性增長,但對應的終點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%時,終點與起點不要重合,最好偏移極小數值(太大弧形不好看),然後閉合圖形,否則無法組合成圓。
詳細程式碼如下:
一、自定義控制元件
前臺:
<UserControl x:Class="BeatfanControls.ProcessBars.CycleProcessBar1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BeatfanControls.ProcessBars"
mc:Ignorable="d" >
<Viewbox>
<Grid Width="34" Height="34">
<Path Name="myCycleProcessBar1" Data="M17,3 A14,14 0 0 1 16,3 " Stroke="Green" StrokeThickness="3" Height="34" Width="34" VerticalAlignment="Center" HorizontalAlignment="Center">
</Path>
<Label Name="lbValue" Content="50%" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="9" />
</Grid>
</Viewbox>
</UserControl>
後臺:
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
{
/// <summary>
/// CycleProcessBar1.xaml 的互動邏輯
/// </summary>
public partial class CycleProcessBar1 : UserControl
{
public CycleProcessBar1()
{
InitializeComponent();
}
public double CurrentValue1
{
set { SetValue(value); }
}
/// <summary>
/// 設定百分百,輸入小數,自動乘100
/// </summary>
/// <param name="percentValue"></param>
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 = pathGeometry;
//達到100%則閉合整個
if (angel == 360)
myCycleProcessBar1.Data = Geometry.Parse(myCycleProcessBar1.Data.ToString() + " z");
}
}
}
二、呼叫:
前臺:
<Window x:Class="CircleProgressBar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CircleProgressBar"
xmlns:MyProgress="clr-namespace:BeatfanControls.ProcessBars"
mc:Ignorable="d" Closing="Window_Closing"
Title="環形進度條" Height="350" Width="525">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<MyProgress:CycleProcessBar1 x:Name="circleProgressBar" CurrentValue1="0.9" />
<Button Name="btnStart" Content="開始" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click" />
</Grid>
</Window>
後臺:
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
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
/// <summary>
/// 定時器
/// </summary>
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;
}
/// <summary>
/// UI變化
/// </summary>
/// <param name="bState"></param>
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();
}
}
}