SoFunction
Updated on 2025-03-08

C# implements a circular progress bar (ProgressBar)

In our actual work, circular progress bars may often be used, but how is this achieved? In fact, this is just a modification of the ProgressBar template. In the following code, we bind the Value value of ProgressBar to the Border Background, and use a ValueToProcessConverter converter for corresponding conversion. Here we will introduce this converter.

<ProgressBar Name="pb" Minimum="0" Maximum="100" >
  <>
    <ControlTemplate TargetType="ProgressBar">
     <Border Background="{TemplateBinding Value, Converter={StaticResource ValueToProcessConverter}, ConverterParameter=250}"/>
    </ControlTemplate>
  </>
</ProgressBar>

The source code of this part is introduced below and a brief analysis:

First, get, and then get the value ConverterParameter=250. Through these two values, you can determine the size of the circle drawn and the value displayed by ProgressBar. Then we call the function DrawBrush(arg, 100, radius, radius, Thickness) to draw. The specific code is as follows:

private Brush DrawBrush(double value, double maxValue, double radiusX, double radiusY, double thickness)
        {
            DrawingGroup drawingGroup = new DrawingGroup();
            DrawingContext drawingContext = ();
            DrawingGeometry(drawingContext, value, maxValue, radiusX, radiusY, thickness);
            DrawingBrush brush = new DrawingBrush(drawingGroup);
            return brush;
        }

It should be noted here that DrawingContext cannot be instantiated directly; but the drawing context can be obtained through certain methods (such as    and  ). Here we use the method to draw the corresponding drawing, and then call the DrawingGeometry function in it, and start drawing some DrawEllipse and DrawGeometry in this function. In this function, we will explain the FormattedText class. Using the FormattedText object, you can draw multiple lines of text and set the formatting for each character in the text separately.

private void DrawingGeometry(DrawingContext drawingContext, double value, double maxValue, double radiusX, double radiusY, double thickness)
       {
           (null, new Pen(EllipseBrush, thickness), centerPoint, radiusX, radiusY);
           (NormalBrush, new Pen(), GetGeometry(value, maxValue, radiusX, radiusY, thickness));
 
           FormattedText formatWords = new FormattedText(percentString, , , SuccessRateTypeface, SuccessRateFontSize, NormalBrush);
           Point startPoint = new Point( -  / 2,  -  / 2 - SuccessRateFontCorrectionValue);
           (formatWords, startPoint);
 
           ();
       }
public class ValueToProcessConverter : IValueConverter
    {
        readonly double Thickness = 20;
        private Point centerPoint;
        private double radius;
        readonly SolidColorBrush NormalBrush = new SolidColorBrush();
        readonly SolidColorBrush EllipseBrush = new SolidColorBrush((107, 132, 165));
 
        string percentString;
        private static readonly Typeface SuccessRateTypeface;
        private const int SuccessRateFontSize = 65;
        readonly double SuccessRateFontCorrectionValue = 12;
 
        static ValueToProcessConverter()
        {
            SuccessRateTypeface = new Typeface(new FontFamily("MSYH"), new FontStyle(), new FontWeight(), new FontStretch());
        }
        public ValueToProcessConverter()
        {
 
        }
        public object Convert(object value, Type targetType, object parameter,  culture)
        {
            if (value is double &amp;&amp; !((string)parameter))
            {
                double arg = (double)value;
                double width = ((string)parameter);
                radius = width / 2;
                centerPoint = new Point(radius, radius);
                return DrawBrush(arg, 100, radius, radius, Thickness);
            }
            else
            {
                throw new ArgumentException();
            }
        }
        public object ConvertBack(object value, Type targetType, object parameter,  culture)
        {
            throw new NotImplementedException();
        }
 
        /// &lt;summary&gt;
        /// Get coordinates according to angle        /// &lt;/summary&gt;
        /// &lt;param name="CenterPoint"&gt;&lt;/param&gt;
        /// &lt;param name="r"&gt;&lt;/param&gt;
        /// &lt;param name="angel"&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        private Point GetPointByAngel(Point CenterPoint, double r, double angel)
        {
            Point p = new Point();
             = (angel *  / 180) * r + ;
             =  - (angel *  / 180) * r;
            return p;
        }
 
        /// &lt;summary&gt;
        /// Draw fan shape based on 4 coordinates        /// &lt;/summary&gt;
        /// &lt;param name="bigFirstPoint"&gt;&lt;/param&gt;
        /// &lt;param name="bigSecondPoint"&gt;&lt;/param&gt;
        /// &lt;param name="smallFirstPoint"&gt;&lt;/param&gt;
        /// &lt;param name="smallSecondPoint"&gt;&lt;/param&gt;
        /// &lt;param name="bigRadius"&gt;&lt;/param&gt;
        /// &lt;param name="smallRadius"&gt;&lt;/param&gt;
        /// &lt;param name="isLargeArc"&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        private Geometry DrawingArcGeometry(Point bigFirstPoint, Point bigSecondPoint, Point smallFirstPoint, Point smallSecondPoint, double bigRadius, double smallRadius, bool isLargeArc)
        {
            PathFigure pathFigure = new PathFigure { IsClosed = true };
             = bigFirstPoint;
            (
              new ArcSegment
              {
                  Point = bigSecondPoint,
                  IsLargeArc = isLargeArc,
                  Size = new Size(bigRadius, bigRadius),
                  SweepDirection = 
              });
            (new LineSegment { Point = smallSecondPoint });
            (
             new ArcSegment
             {
                 Point = smallFirstPoint,
                 IsLargeArc = isLargeArc,
                 Size = new Size(smallRadius, smallRadius),
                 SweepDirection = 
             });
            PathGeometry pathGeometry = new PathGeometry();
            (pathFigure);
 
            return pathGeometry;
        }
 
        /// &lt;summary&gt;
        /// Get the sector according to the current value and maximum value        /// &lt;/summary&gt;
        /// &lt;param name="value"&gt;&lt;/param&gt;
        /// &lt;param name="maxValue"&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        private Geometry GetGeometry(double value, double maxValue, double radiusX, double radiusY, double thickness)
        {
            bool isLargeArc = false;
            double percent = value / maxValue;
            percentString = ("{0}%", (percent * 100, 2));
            double angel = percent * 360D;
            if (angel &gt; 180) isLargeArc = true;
            double bigR = radiusX + thickness / 2;
            double smallR = radiusX - thickness / 2;
            Point firstpoint = GetPointByAngel(centerPoint, bigR, 0);
            Point secondpoint = GetPointByAngel(centerPoint, bigR, angel);
            Point thirdpoint = GetPointByAngel(centerPoint, smallR, 0);
            Point fourpoint = GetPointByAngel(centerPoint, smallR, angel);
            return DrawingArcGeometry(firstpoint, secondpoint, thirdpoint, fourpoint, bigR, smallR, isLargeArc);
        }
 
        /// &lt;summary&gt;
        /// Draw a fan shape        /// &lt;/summary&gt;
        /// &lt;param name="drawingContext"&gt;&lt;/param&gt;
        /// &lt;param name="value"&gt;&lt;/param&gt;
        /// &lt;param name="maxValue"&gt;&lt;/param&gt;
        /// &lt;param name="radiusX"&gt;&lt;/param&gt;
        /// &lt;param name="radiusY"&gt;&lt;/param&gt;
        /// &lt;param name="thickness"&gt;&lt;/param&gt;
        private void DrawingGeometry(DrawingContext drawingContext, double value, double maxValue, double radiusX, double radiusY, double thickness)
        {
            (null, new Pen(EllipseBrush, thickness), centerPoint, radiusX, radiusY);
            (NormalBrush, new Pen(), GetGeometry(value, maxValue, radiusX, radiusY, thickness));
            FormattedText formatWords = new FormattedText(percentString, , , SuccessRateTypeface, SuccessRateFontSize, NormalBrush);
            Point startPoint = new Point( -  / 2,  -  / 2 - SuccessRateFontCorrectionValue);
            (formatWords, startPoint);
            ();
        }
 
        /// &lt;summary&gt;
        ///Draw progress bar according to current value and maximum value        /// &lt;/summary&gt;
        /// &lt;param name="value"&gt;&lt;/param&gt;
        /// &lt;param name="maxValue"&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        private Brush DrawBrush(double value, double maxValue, double radiusX, double radiusY, double thickness)
        {
            DrawingGroup drawingGroup = new DrawingGroup();
            DrawingContext drawingContext = ();
            DrawingGeometry(drawingContext, value, maxValue, radiusX, radiusY, thickness);
            DrawingBrush brush = new DrawingBrush(drawingGroup);
            return brush;
        }
 
    }

The above is the detailed content of the C# implementation of a circular progress bar (ProgressBar). For more information about the C# implementation of a progress bar, please pay attention to my other related articles!