2 ответа

  1. Попробуйте сделать его пользовательским элементом управления с помощью компонента ring из доступных компонентов drag and drop. Добавьте метку и сделайте некоторые свойства, которые вы можете изменить, чтобы получить определенный результат.
    Смотрите этот пост.
    Или попробуйте учебник

  2. Попробовать это:

    using System;
    using System.Globalization;
    
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;
    
    namespace WpfApplication1
    {
        public class CircleProgress : Canvas
        {
            public static readonly DependencyProperty ValueProperty =
                 DependencyProperty.Register("Value", typeof(double),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(180d, OnValueChanged));
    
            public static readonly DependencyProperty MinimumProperty =
                 DependencyProperty.Register("Minimum", typeof(double),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(0d, OnValueChanged));
    
    
            public static readonly DependencyProperty MaximumProperty =
                 DependencyProperty.Register("Maximum", typeof(double),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(360d, OnValueChanged));
    
            public static readonly DependencyProperty BackgroundCircleStrokeProperty =
                 DependencyProperty.Register("BackgroundCircleStroke", typeof(Brush),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(Brushes.Gray, OnStrokeChanged));
    
            public static readonly DependencyProperty BackgroundCircleStrokeThicknessProperty =
                 DependencyProperty.Register("BackgroundCircleStrokeThickness", typeof(double),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(5d, OnStrokeChanged));
    
            public static readonly DependencyProperty MainCircleStrokeProperty =
                 DependencyProperty.Register("MainCircleStroke", typeof(Brush),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(Brushes.DeepSkyBlue, OnStrokeChanged));
    
            public static readonly DependencyProperty MainCircleStrokeThicknessProperty =
                 DependencyProperty.Register("MainCircleStrokeThickness", typeof(double),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(5d, OnStrokeChanged));
    
            public static readonly DependencyProperty TextStrokeProperty =
                 DependencyProperty.Register("TextStroke", typeof(Brush),
                 typeof(CircleProgress), new FrameworkPropertyMetadata(Brushes.Black, OnStrokeChanged));
    
            public double Value
            {
                get { return (double)GetValue(ValueProperty); }
                set { SetValue(ValueProperty, value); }
            }
            public double Minimum
            {
                get { return (double)GetValue(MinimumProperty); }
                set { SetValue(MinimumProperty, value); }
            }
            public double Maximum
            {
                get { return (double)GetValue(MaximumProperty); }
                set { SetValue(MaximumProperty, value); }
            }
            public Brush BackgroundCircleStroke
            {
                get { return (Brush) GetValue(BackgroundCircleStrokeProperty); }
                set { SetValue(BackgroundCircleStrokeProperty, value); }
            }
            public Brush MainCircleStroke
            {
                get { return (Brush)GetValue(MainCircleStrokeProperty); }
                set { SetValue(MainCircleStrokeProperty, value); }
            }
            public Brush TextStroke
            {
                get { return (Brush)GetValue(MainCircleStrokeProperty); }
                set { SetValue(MainCircleStrokeProperty, value); }
            }
            public double BackgroundCircleStrokeThickness
            {
                get { return (double)GetValue(MaximumProperty); }
                set { SetValue(MaximumProperty, value); }
            }
            public double MainCircleStrokeThickness
            {
                get { return (double)GetValue(MaximumProperty); }
                set { SetValue(MaximumProperty, value); }
            }
    
            private readonly Path _backEllipse = new Path()
            {
                StrokeThickness = 5,
                Stroke = Brushes.Gray
            };
    
            private readonly Path _mainEllipse = new Path()
            {
                StrokeThickness = 5,
                Stroke = Brushes.DeepSkyBlue
            };
    
            private readonly Path _text = new Path()
            {
                StrokeThickness = 1,
                Fill = Brushes.Black
            };
    
            private double _radius = 10;
            private Point _center = new Point(0,0);
            private Point _startPoint = new Point(0,0);
    
    
            public CircleProgress()
            {
                _backEllipse.Data = new EllipseGeometry(new Point(Width/2, Height/2), _radius, _radius);
                _mainEllipse.Data = new PathGeometry()
                {
                    Figures = new PathFigureCollection()
                    {
                        new PathFigure(new Point(Width/2, 0), new PathSegmentCollection()
                        {
                            new ArcSegment(
                                (new RotateTransform(Value*(360/(Maximum - Minimum)), _center.X, _center.Y)).Transform(
                                    _startPoint), new Size(_radius*2, _radius*2), 0, true, SweepDirection.Clockwise, false)
                        }, false)
                    }
                };
                var text = new FormattedText(Value.ToString(CultureInfo.CurrentCulture),
                    CultureInfo.CurrentCulture,
                    FlowDirection.LeftToRight,
                    new Typeface("Palatino"),
                    0.8* _radius,
                    Brushes.Black);
    
                _text.Data = text.BuildGeometry(new Point(_center.X - text.Width/2, _center.Y - text.Height/2));
    
    
                SizeChanged += OnSizeChanged;
                Children.Add(_backEllipse);
                Children.Add(_mainEllipse);
                Children.Add(_text);
            }
    
            private void OnSizeChanged(object sender, SizeChangedEventArgs e)
            {
                var quadSize = e.NewSize.Height <= e.NewSize.Width ? e.NewSize.Height : e.NewSize.Width;
                _radius = quadSize / 2;
                _center = new Point(e.NewSize.Width/2, e.NewSize.Height / 2);
                _startPoint = _center - new Vector(0, _radius);
    
                UpdateCircle(this);
            }
    
            /// <summary>
            /// Action when Value, Minimum or Maximum changed.
            /// </summary>
            /// <param name="d">Dependecy object.</param>
            /// <param name="e">EventArgs.</param>
            private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var circle = d as CircleProgress;
    
                UpdateCircle(circle);   
            }
    
            private static void OnStrokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var circle = d as CircleProgress;
                circle._backEllipse.Stroke = circle.BackgroundCircleStroke;
                circle._backEllipse.StrokeThickness = circle.BackgroundCircleStrokeThickness;
    
                circle._mainEllipse.Stroke = circle.BackgroundCircleStroke;
                circle._mainEllipse.StrokeThickness = circle.BackgroundCircleStrokeThickness;
    
                circle._text.Fill = circle.TextStroke;
            }
    
            /// <summary>
            /// Update Background and Main circles.
            /// </summary>
            /// <param name="circle">Reference to CircleProgress control.</param>
            private static void UpdateCircle(CircleProgress circle)
            {
                circle._backEllipse.Data = new EllipseGeometry(circle._center, circle._radius, circle._radius);
    
                if (Math.Abs(circle.Value*(360/(circle.Maximum - circle.Minimum)) - 360) < 0.0001)
                    circle._mainEllipse.Data = new EllipseGeometry(circle._center, circle._radius, circle._radius);
                else
                {
    
                    circle._mainEllipse.Data = new PathGeometry()
                    {
                        Figures = new PathFigureCollection()
                        {
                            new PathFigure(circle._startPoint, new PathSegmentCollection()
                            {
                                new ArcSegment(
                                    (new RotateTransform(circle.Value*(360/(circle.Maximum - circle.Minimum)),
                                        circle._center.X, circle._center.Y)).Transform(
                                            circle._startPoint), new Size(circle._radius, circle._radius), 0,
                                    !(circle.Value*(360/(circle.Maximum - circle.Minimum)) <= 180), SweepDirection.Clockwise,
                                    true)
                            }, false)
                        }
                    };
                }
                var text = new FormattedText($"{circle.Value:##}",
                    CultureInfo.CurrentCulture,
                    FlowDirection.LeftToRight,
                    new Typeface("Palatino"),
                    0.8 * circle._radius,
                    Brushes.Black);
    
                circle._text.Data = text.BuildGeometry(new Point(circle._center.X - text.Width / 2, circle._center.Y - text.Height / 2));
            }
        }
    }
    

    Использование XAML:

    <Window x_Class="WpfApplication1.MainWindow"
            
            
            
            
            
            mc_Ignorable="d"
            Title="MainWindow" Height="350" Width="525" x_Name="Main">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="472*"/>
                <ColumnDefinition Width="45*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="27"/>
            </Grid.RowDefinitions>
            <local:CircleProgress Grid.Row="0" Grid.Column="0" Margin="75" Minimum="{Binding ElementName=MinTextBox, Path=Text}" Maximum="{Binding ElementName=MaxTextBox, Path=Text}" Value="{Binding ElementName=slider, Path=Value}"/>
            <Slider x_Name="slider" Grid.Column="1" Grid.Row="0" Orientation="Vertical" TickPlacement="BottomRight" Minimum="{Binding ElementName=MinTextBox, Path=Text}" Maximum="{Binding ElementName=MaxTextBox, Path=Text}"/>
            <StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal">
                <Label Content="Minimum" />
                <TextBox Width="200" x_Name="MinTextBox"/>
                <Label Content="Maximum"/>
                <TextBox  Width="150" x_Name="MaxTextBox"/>
            </StackPanel>
        </Grid>
    </Window>
    

    Вот у меня есть:
    Круговой индикатор выполнения