效果圖
一共有三種方法
1.用弧度來畫進度條
2.用餅圖和圓取交集來畫進度條
3.用DashArray來畫進度條
方法一 用弧度來畫進度條
style
<Style x:Key="CircleProgressBar1" TargetType="{x:Type ProgressBar}">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<Rectangle Fill="#ef426f" RadiusX="5" RadiusY="5"/>
<Ellipse Stroke="#fc8b93"
StrokeThickness="6"
Width="86"
Height="86"/>
<Path Stroke="#fbbdaf"
StrokeThickness="6"
StrokeStartLineCap="Round"
StrokeEndLineCap="Round">
<Path.Data>
<MultiBinding Converter="{StaticResource Style1ArcConverter}">
<Binding ElementName="TemplateRoot" Path="ActualWidth" />
<Binding ElementName="TemplateRoot" Path="ActualHeight" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
</MultiBinding>
</Path.Data>
</Path>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="#FFF7ee"
Text="{Binding StringFormat={}{0}%, RelativeSource={RelativeSource TemplatedParent}, Path=Value}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
converter
public class Style1ArcConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double PIch2 = 2 * Math.PI;
double centerX = (double)values[0] / 2;
double centerY = (double)values[1] / 2;
double angle = (double)values[2] / 100 * PIch2;
double radius = 40;
var 起始弧度的X偏移 = Math.Sin(0);
var 起始弧度的Y偏移 = Math.Cos(0);
var 終止弧度的X偏移 = Math.Sin(angle);
var 終止弧度的Y偏移 = Math.Cos(angle);
Point 起點 = new Point(centerX + radius * 起始弧度的X偏移, centerY - radius * 起始弧度的Y偏移);
Point 終點 = new Point(centerX + radius * 終止弧度的X偏移, centerY - radius * 終止弧度的Y偏移);
var converter = TypeDescriptor.GetConverter(typeof(Geometry));
string data;
if (angle == 0)
{
data = string.Empty;
}
else if (angle > 0 && angle < Math.PI)
{
data = $"M{起點.X},{起點.Y} A{radius},{radius},0,0,1 {終點.X},{終點.Y}";
}
else if (angle >= Math.PI && angle < 2 * Math.PI)
{
data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X},{終點.Y}";
}
else
{
data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X - 0.001},{終點.Y} Z";
}
return (Geometry)converter.ConvertFrom(data);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
方法二 用餅圖和圓取交集來畫進度條
style
<Style x:Key="CircleProgressBar2" TargetType="{x:Type ProgressBar}">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<Rectangle Fill="#ef426f" RadiusX="5" RadiusY="5"/>
<Path Fill="#fbbdaf">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Intersect">
<CombinedGeometry.Geometry1>
<GeometryGroup FillRule="EvenOdd">
<EllipseGeometry RadiusX="46" RadiusY="46" Center="50,50"/>
<EllipseGeometry RadiusX="40" RadiusY="40" Center="50,50"/>
</GeometryGroup>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry>
<PathGeometry.Figures>
<MultiBinding Converter="{StaticResource Style2ArcConverter}">
<Binding ElementName="TemplateRoot" Path="ActualWidth" />
<Binding ElementName="TemplateRoot" Path="ActualHeight" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
</MultiBinding>
</PathGeometry.Figures>
</PathGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
<Path Fill="#fc8b93">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<GeometryGroup FillRule="EvenOdd">
<EllipseGeometry RadiusX="46" RadiusY="46" Center="50,50"/>
<EllipseGeometry RadiusX="40" RadiusY="40" Center="50,50"/>
</GeometryGroup>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry>
<PathGeometry.Figures>
<MultiBinding Converter="{StaticResource Style2ArcConverter}">
<Binding ElementName="TemplateRoot" Path="ActualWidth" />
<Binding ElementName="TemplateRoot" Path="ActualHeight" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
</MultiBinding>
</PathGeometry.Figures>
</PathGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
<StackPanel Margin="0,16,0,0"
VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="#FFF7ee"
Text="{Binding StringFormat={}{0}%, RelativeSource={RelativeSource TemplatedParent}, Path=Value}"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
converter
public class Style2ArcConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double PIch2 = 2 * Math.PI;
double centerX = (double)values[0] / 2;
double centerY = (double)values[1] / 2;
double angle = (double)values[2] / 100 * PIch2;
double radius = centerX;
var 起始弧度的X偏移 = Math.Sin(0);
var 起始弧度的Y偏移 = Math.Cos(0);
var 終止弧度的X偏移 = Math.Sin(angle);
var 終止弧度的Y偏移 = Math.Cos(angle);
Point 起點 = new Point(centerX + radius * 起始弧度的X偏移, centerY - radius * 起始弧度的Y偏移);
Point 終點 = new Point(centerX + radius * 終止弧度的X偏移, centerY - radius * 終止弧度的Y偏移);
var converter = TypeDescriptor.GetConverter(typeof(PathFigureCollection));
string data;
if (angle >= 0 && angle < Math.PI)
{
data = $"M{起點.X},{起點.Y} A{radius},{radius},0,0,1 {終點.X},{終點.Y} L{centerX},{centerY} Z";
}
else if (angle >= Math.PI && angle < 2 * Math.PI)
{
data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X},{終點.Y} L{centerX},{centerY} Z";
}
else
{
data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X - 0.001},{終點.Y} L{centerX},{centerY} Z";
}
return (PathFigureCollection)converter.ConvertFrom(data);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
方法三 用DashArray來畫進度條
style
<Style x:Key="CircleProgressBar3" TargetType="{x:Type ProgressBar}">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid>
<Rectangle Fill="#ef426f" RadiusX="5" RadiusY="5"/>
<Ellipse Stroke="#fc8b93"
StrokeThickness="6"
Width="80"
Height="80"/>
<Ellipse Width="80" Height="80" StrokeThickness="6" Stroke="#fbbdaf"
RenderTransformOrigin="0.5,0.5"
StrokeDashCap="{Binding Value, RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource Style3DashConverter}}">
<Ellipse.RenderTransform>
<RotateTransform Angle="-90" />
</Ellipse.RenderTransform>
<Ellipse.StrokeDashArray>
<MultiBinding Converter="{StaticResource Style3ArcConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
<Binding Path="StrokeThickness" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Ellipse.StrokeDashArray>
</Ellipse>
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="#FFF7ee"
Text="{Binding StringFormat={}{0}%, RelativeSource={RelativeSource TemplatedParent}, Path=Value}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
converter 有2個
public class Style3ArcConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double value = (double)values[0];
double thickness = (double)values[1];
double radius = 40;
double 周長 = Math.PI * (2 * radius - thickness) / thickness;
double showPrecent = value / 100 * 周長;
var converter = TypeDescriptor.GetConverter(typeof(DoubleCollection));
return (DoubleCollection)converter.ConvertFrom($"{showPrecent} {周長}");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class Style3DashConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double v = (double)value;
if (v == 0)
{
return PenLineCap.Flat;
}
else
{
return PenLineCap.Round;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
使用
前台代碼
<StackPanel Orientation="Vertical">
<ProgressBar x:Name="progress1"
Style="{StaticResource CircleProgressBar1}"
Margin="10"
Value="0" />
<ProgressBar x:Name="progress2"
Style="{StaticResource CircleProgressBar2}"
Margin="10"
Value="0" />
<ProgressBar x:Name="progress3"
Style="{StaticResource CircleProgressBar3}"
Margin="10"
Value="0" />
<StackPanel Orientation="Horizontal">
<Button Content="開始" Click="Button_Click" />
<Button Content="重置" Click="Button_Click_1" />
</StackPanel>
</StackPanel>
對應的背景代碼
using System;
using System.Timers;
using System.Windows;
namespace 圖形變換
{
public partial class Window4 : Window
{
private Timer _timer;
public Window4()
{
InitializeComponent();
_timer = new Timer
{
Interval = 50
};
_timer.Elapsed += Timer_Elapsed;
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
_timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() =>
{
if (progress1.Value < 100)
{
progress1.Value++;
progress2.Value++;
progress3.Value++;
}
else
{
_timer.Stop();
}
}));
}
private void Button_Click_1(object sender, System.Windows.RoutedEventArgs e)
{
progress1.Value = 0;
progress2.Value = 0;
progress3.Value = 0;
_timer.Stop();
}
}
}