1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
| public class CircularProgress : FrameworkElement
{
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register(
nameof(Progress),
typeof(double),
typeof(CircularProgress),
new FrameworkPropertyMetadata(
defaultValue: 0.0,
propertyChangedCallback: OnProgressChanged,
affectsRender: true), // 改了要重绘
validateValueCallback: IsValidProgress);
public double Progress
{
get => (double)GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
public static readonly DependencyProperty StrokeColorProperty =
DependencyProperty.Register(
nameof(StrokeColor),
typeof(Color),
typeof(CircularProgress),
new FrameworkPropertyMetadata(
defaultValue: Colors.DodgerBlue,
propertyChangedCallback: OnVisualPropChanged,
affectsRender: true));
public Color StrokeColor
{
get => (Color)GetValue(StrokeColorProperty);
set => SetValue(StrokeColorProperty, value);
}
private static bool IsValidProgress(object value)
=> value is double d && !double.IsNaN(d) && !double.IsInfinity(d);
private static void OnProgressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (CircularProgress)d;
// 在 0~100 之间是数据约束(用 Validate)
// 这里只是触发副作用:调试日志
Trace.WriteLine($"Progress: {e.OldValue} → {e.NewValue}");
}
private static void OnVisualPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((CircularProgress)d).InvalidateVisual();
}
protected override void OnRender(DrawingContext dc)
{
var size = Math.Min(ActualWidth, ActualHeight);
var center = new Point(ActualWidth / 2, ActualHeight / 2);
var radius = size / 2 - 10;
// 背景圆
dc.DrawEllipse(null, new Pen(Brushes.LightGray, 6), center, radius, radius);
// 进度圆弧
var angle = Progress / 100 * 360;
var pen = new Pen(new SolidColorBrush(StrokeColor), 6);
var start = new Point(center.X + radius, center.Y);
var arcEnd = ComputeArcEnd(center, radius, angle);
var geometry = new PathGeometry();
geometry.Figures.Add(new PathFigure(
start,
new[] { new ArcSegment(arcEnd, new Size(radius, radius), 0,
angle > 180 ? SweepDirection.Counterclockwise : SweepDirection.Clockwise, true) },
false));
dc.DrawGeometry(null, pen, geometry);
}
private static Point ComputeArcEnd(Point center, double radius, double angle)
{
var rad = angle * Math.PI / 180;
return new Point(
center.X + radius * Math.Cos(rad),
center.Y + radius * Math.Sin(rad));
}
}
|