在我们实际的工作中可能经常使用到圆形的进度条,但是这是怎么实现的呢?其实这只不过是修改了一下ProgressBar的模板,我们在下面的代码中我们将ProgressBar的Value值绑定到Border的Background上面,并且使用了一个ValueToProcessConverter的转换器进行相应地转换,这里重点介绍一下这个转换器
<ProgressBar Name="pb" Minimum="0" Maximum="100" > <ProgressBar.Template> <ControlTemplate TargetType="ProgressBar"> <Border Background="{TemplateBinding Value, Converter={StaticResource ValueToProcessConverter}, ConverterParameter=250}"/> </ControlTemplate> </ProgressBar.Template> </ProgressBar>
下面介绍这部分的源码,并做简要的分析:
首先,获取ProgressBar.Value,然后再获取ConverterParameter=250这个值,通过这两个值就能确定画的圆环的大小和ProgressBar显示的值,然后我们再调用DrawBrush(arg, 100, radius, radius, Thickness)这个函数来进行绘制,具体代码如下:
private Brush DrawBrush(double value, double maxValue, double radiusX, double radiusY, double thickness) { DrawingGroup drawingGroup = new DrawingGroup(); DrawingContext drawingContext = drawingGroup.Open(); DrawingGeometry(drawingContext, value, maxValue, radiusX, radiusY, thickness); DrawingBrush brush = new DrawingBrush(drawingGroup); return brush; }
这里需要注意的是绝不能直接实例化 DrawingContext;但可以通过某些方法(例如 DrawingGroup.Open 和 DrawingVisual.RenderOpen)获取绘图上下文。我们这里是使用DrawingGroup.Open的方法来进行相应的绘图,然后在里面调用里DrawingGeometry这个函数,在这个函数中开始绘制一些DrawEllipse和DrawGeometry,在这个函数中我们讲解一下FormattedText 这个类,使用 FormattedText 对象可以绘制多行文本,且可以单独对该文本中的每个字符设置格式。
private void DrawingGeometry(DrawingContext drawingContext, double value, double maxValue, double radiusX, double radiusY, double thickness) { drawingContext.DrawEllipse(null, new Pen(EllipseBrush, thickness), centerPoint, radiusX, radiusY); drawingContext.DrawGeometry(NormalBrush, new Pen(), GetGeometry(value, maxValue, radiusX, radiusY, thickness)); FormattedText formatWords = new FormattedText(percentString, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, SuccessRateTypeface, SuccessRateFontSize, NormalBrush); Point startPoint = new Point(centerPoint.X - formatWords.Width / 2, centerPoint.Y - formatWords.Height / 2 - SuccessRateFontCorrectionValue); drawingContext.DrawText(formatWords, startPoint); drawingContext.Close(); }
public class ValueToProcessConverter : IValueConverter { readonly double Thickness = 20; private Point centerPoint; private double radius; readonly SolidColorBrush NormalBrush = new SolidColorBrush(Colors.White); readonly SolidColorBrush EllipseBrush = new SolidColorBrush(Color.FromRgb(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, System.Globalization.CultureInfo culture) { if (value is double && !string.IsNullOrEmpty((string)parameter)) { double arg = (double)value; double width = double.Parse((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, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } /// <summary> /// 根据角度获取坐标 /// </summary> /// <param name="CenterPoint"></param> /// <param name="r"></param> /// <param name="angel"></param> /// <returns></returns> private Point GetPointByAngel(Point CenterPoint, double r, double angel) { Point p = new Point(); p.X = Math.Sin(angel * Math.PI / 180) * r + CenterPoint.X; p.Y = CenterPoint.Y - Math.Cos(angel * Math.PI / 180) * r; return p; } /// <summary> /// 根据4个坐标画出扇形 /// </summary> /// <param name="bigFirstPoint"></param> /// <param name="bigSecondPoint"></param> /// <param name="smallFirstPoint"></param> /// <param name="smallSecondPoint"></param> /// <param name="bigRadius"></param> /// <param name="smallRadius"></param> /// <param name="isLargeArc"></param> /// <returns></returns> private Geometry DrawingArcGeometry(Point bigFirstPoint, Point bigSecondPoint, Point smallFirstPoint, Point smallSecondPoint, double bigRadius, double smallRadius, bool isLargeArc) { PathFigure pathFigure = new PathFigure { IsClosed = true }; pathFigure.StartPoint = bigFirstPoint; pathFigure.Segments.Add( new ArcSegment { Point = bigSecondPoint, IsLargeArc = isLargeArc, Size = new Size(bigRadius, bigRadius), SweepDirection = SweepDirection.Clockwise }); pathFigure.Segments.Add(new LineSegment { Point = smallSecondPoint }); pathFigure.Segments.Add( new ArcSegment { Point = smallFirstPoint, IsLargeArc = isLargeArc, Size = new Size(smallRadius, smallRadius), SweepDirection = SweepDirection.Counterclockwise }); PathGeometry pathGeometry = new PathGeometry(); pathGeometry.Figures.Add(pathFigure); return pathGeometry; } /// <summary> /// 根据当前值和最大值获取扇形 /// </summary> /// <param name="value"></param> /// <param name="maxValue"></param> /// <returns></returns> private Geometry GetGeometry(double value, double maxValue, double radiusX, double radiusY, double thickness) { bool isLargeArc = false; double percent = value / maxValue; percentString = string.Format("{0}%", Math.Round(percent * 100, 2)); double angel = percent * 360D; if (angel > 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); } /// <summary> /// 画扇形 /// </summary> /// <param name="drawingContext"></param> /// <param name="value"></param> /// <param name="maxValue"></param> /// <param name="radiusX"></param> /// <param name="radiusY"></param> /// <param name="thickness"></param> private void DrawingGeometry(DrawingContext drawingContext, double value, double maxValue, double radiusX, double radiusY, double thickness) { drawingContext.DrawEllipse(null, new Pen(EllipseBrush, thickness), centerPoint, radiusX, radiusY); drawingContext.DrawGeometry(NormalBrush, new Pen(), GetGeometry(value, maxValue, radiusX, radiusY, thickness)); FormattedText formatWords = new FormattedText(percentString, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, SuccessRateTypeface, SuccessRateFontSize, NormalBrush); Point startPoint = new Point(centerPoint.X - formatWords.Width / 2, centerPoint.Y - formatWords.Height / 2 - SuccessRateFontCorrectionValue); drawingContext.DrawText(formatWords, startPoint); drawingContext.Close(); } /// <summary> /// 根据当前值和最大值画出进度条 /// </summary> /// <param name="value"></param> /// <param name="maxValue"></param> /// <returns></returns> private Brush DrawBrush(double value, double maxValue, double radiusX, double radiusY, double thickness) { DrawingGroup drawingGroup = new DrawingGroup(); DrawingContext drawingContext = drawingGroup.Open(); DrawingGeometry(drawingContext, value, maxValue, radiusX, radiusY, thickness); DrawingBrush brush = new DrawingBrush(drawingGroup); return brush; } }