Это немного сложно, но не невозможно.Вот моя реализация, использующая плавную анимацию для руководства.Для создания CircularProgressBar следует использовать преобразователи значений.
CircularProgressBar.cs
public partial class CircularProgressBar : ProgressBar
{
public CircularProgressBar()
{
this.ValueChanged += CircularProgressBar_ValueChanged;
}
void CircularProgressBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
CircularProgressBar bar = sender as CircularProgressBar;
double currentAngle = bar.Angle;
double targetAngle = e.NewValue / bar.Maximum * 359.999;
DoubleAnimation anim = new DoubleAnimation(currentAngle, targetAngle, TimeSpan.FromMilliseconds(500));
bar.BeginAnimation(CircularProgressBar.AngleProperty, anim, HandoffBehavior.SnapshotAndReplace);
}
public double Angle
{
get { return (double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
// Using a DependencyProperty as the backing store for Angle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(0.0));
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
// Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StrokeThicknessProperty =
DependencyProperty.Register("StrokeThickness", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(10.0));
}
AngleToPointConverter.cs
class AngleToPointConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double angle = (double)value;
double radius = 50;
double piang = angle * Math.PI / 180;
double px = Math.Sin(piang) * radius + radius;
double py = -Math.Cos(piang) * radius + radius;
return new Point(px, py);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
AngleToIsLargeConverter.cs
class AngleToIsLargeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double angle = (double)value;
return angle > 180;
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
App.xaml
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
xmlns:my="clr-namespace:WpfApplication1">
<Application.Resources>
<my:AngleToPointConverter x:Key="prConverter"/>
<my:AngleToIsLargeConverter x:Key="isLargeConverter"/>
<Style x:Key="circularProgressBar" TargetType="my:CircularProgressBar">
<Setter Property="Value" Value="10"/>
<Setter Property="Maximum" Value="100"/>
<Setter Property="StrokeThickness" Value="10"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:CircularProgressBar">
<Canvas Width="100" Height="100">
<Ellipse Width="100" Height="100" Stroke="LightGray"
StrokeThickness="1"/>
<Path Stroke="{TemplateBinding Background}"
StrokeThickness="{TemplateBinding StrokeThickness}">
<Path.Data>
<PathGeometry>
<PathFigure x:Name="fig" StartPoint="50,0">
<ArcSegment RotationAngle="0" SweepDirection="Clockwise"
Size="50,50"
Point="{Binding Path=Angle, Converter={StaticResource prConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
IsLargeArc="{Binding Path=Angle, Converter={StaticResource isLargeConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
>
</ArcSegment>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Border Width="100" Height="100">
<TextBlock Foreground="Gray" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=Value, StringFormat={}%{0},
RelativeSource={RelativeSource TemplatedParent}}"
FontSize="{TemplateBinding FontSize}"/>
</Border>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
Itможно более индивидуально настроить, добавив еще несколько свойств, таких как InnerRadius, Radius и т. д.