1. 控件概述

圆环形进度条是一种常见的UI元素,相比传统直线型进度条,它具有以下优势:

  • 更美观的视觉效果
  • 节省空间的设计
  • 适合作为中心焦点元素
  • 可集成图标或文本

技术特性

特性 描述
进度显示 0-100%平滑过渡
完全可定制 颜色、宽度、动画均可配置
高性能 基于WPF矢量绘图
响应式设计 自动适应容器大小

2. 核心功能实现

关键特性

  • 平滑进度动画:支持0-100%的平滑过渡
  • 完全可定制:可调整颜色、宽度、圆角等样式
  • 响应式设计:自动适应容器尺寸变化
  • 高性能渲染:基于WPF矢量图形系统

依赖属性配置

属性 类型 默认值 描述
Progress double 0.0 当前进度值(0-100)
BackgroundBrush Brush Transparent 中心区域背景色
TrackBrush Brush LightGray 轨道(未完成部分)颜色
ProgressBrush Brush Blue 进度条颜色
TrackThickness int 5 轨道宽度(像素)
ProgressThickness int 13 进度条宽度(像素)

3. 完整控件代码

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WPF.Controls
{
/// <summary>
/// 圆环形进度条控件
/// 功能:显示0-100%的环形进度,支持完全自定义样式
/// </summary>
public class CircularProgressBar : Control
{
#region 依赖属性

public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register(
"Progress",
typeof(double),
typeof(CircularProgressBar),
new FrameworkPropertyMetadata(
0.0,
FrameworkPropertyMetadataOptions.AffectsRender,
OnProgressChanged,
CoerceProgressValue));

public static readonly DependencyProperty BackgroundBrushProperty =
DependencyProperty.Register(
"BackgroundBrush",
typeof(Brush),
typeof(CircularProgressBar),
new PropertyMetadata(Brushes.Transparent));

public static readonly DependencyProperty TrackBrushProperty =
DependencyProperty.Register(
"TrackBrush",
typeof(Brush),
typeof(CularProgressBar),
new PropertyMetadata(Brushes.LightGray));

public static readonly DependencyProperty ProgressBrushProperty =
DependencyProperty.Register(
"ProgressBrush",
typeof(Brush),
typeof(CircularProgressBar),
new PropertyMetadata(Brushes.DodgerBlue));

public static readonly DependencyProperty ProgressThicknessProperty =
DependencyProperty.Register(
"ProgressThickness",
typeof(double),
typeof(CircularProgressBar),
new PropertyMetadata(13.0));

public static readonly DependencyProperty TrackThicknessProperty =
DependencyProperty.Register(
"TrackThickness",
typeof(double),
typeof(CircularProgressBar),
new PropertyMetadata(5.0));

#endregion

#region 属性

/// <summary>
/// 当前进度值(0-100)
/// </summary>
public double Progress
{
get => (double)GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}

/// <summary>
/// 中心区域背景色
/// </summary>
public Brush BackgroundBrush
{
get => (Brush)GetValue(BackgroundBrushProperty);
set => SetValue(BackgroundBrushProperty, value);
}

/// <summary>
/// 轨道(未完成部分)颜色
/// </summary>
public Brush TrackBrush
{
get => (Brush)GetValue(TrackBrushProperty);
set => SetValue(TrackBrushProperty, value);
}

/// <summary>
/// 进度条颜色
/// </summary>
public Brush ProgressBrush
{
get => (Brush)GetValue(ProgressBrushProperty);
set => SetValue(ProgressBrushProperty, value);
}

/// <summary>
/// 进度条宽度(像素)
/// </summary>
public double ProgressThickness
{
get => (double)GetValue(ProgressThicknessProperty);
set => SetValue(ProgressThicknessProperty, value);
}

/// <summary>
/// 轨道宽度(像素)
/// </summary>
public double TrackThickness
{
get => (double)GetValue(TrackThicknessProperty);
set => SetValue(TrackThicknessProperty, value);
}

#endregion

static CircularProgressBar()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(CircularProgressBar),
new FrameworkPropertyMetadata(typeof(CircularProgressBar)));
}

private static object CoerceProgressValue(DependencyObject d, object value)
{
double progress = (double)value;
return Math.Min(100, Math.Max(0, progress));
}

private static void OnProgressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var progressBar = (CircularProgressBar)d;
progressBar.OnProgressChanged((double)e.OldValue, (double)e.NewValue);
}

protected virtual void OnProgressChanged(double oldValue, double newValue)
{
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(newValue));
}

/// <summary>
/// 进度变化事件
/// </summary>
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
}

public class ProgressChangedEventArgs : EventArgs
{
public double Progress { get; }

public ProgressChangedEventArgs(double progress)
{
Progress = progress;
}
}
}

4. XAML模板实现

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
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF.Controls">

<Style TargetType="{x:Type local:CircularProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CircularProgressBar}">
<Grid>
<!-- 背景轨道 -->
<Ellipse Stroke="{TemplateBinding TrackBrush}"
StrokeThickness="{TemplateBinding TrackThickness}"
Fill="Transparent"/>

<!-- 进度条 -->
<Path Stroke="{TemplateBinding ProgressBrush}"
StrokeThickness="{TemplateBinding ProgressThickness}"
StrokeLineCap="Round">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="100,50">
<ArcSegment Size="50,50"
SweepDirection="Clockwise"
IsLargeArc="{Binding Progress,
Converter={StaticResource ProgressToLargeArcConverter}}"
Point="{Binding Progress,
Converter={StaticResource ProgressToPointConverter}}"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>

<!-- 中心文本 -->
<TextBlock Text="{Binding Progress, StringFormat={}{0}%}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontSize="16"
FontWeight="Bold"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

5. 使用示例

基本使用

1
2
3
4
5
<local:CircularProgressBar Progress="75"
Width="100"
Height="100"
ProgressBrush="#FF4081"
TrackBrush="#E0E0E0"/>

数据绑定示例

1
2
3
4
<local:CircularProgressBar Progress="{Binding DownloadProgress}"
ProgressBrush="{DynamicResource PrimaryBrush}"
TrackThickness="8"
ProgressThickness="12"/>

动画效果

1
2
3
4
5
6
7
8
9
10
// 创建进度动画
var animation = new DoubleAnimation
{
From = 0,
To = 100,
Duration = TimeSpan.FromSeconds(2),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};

progressBar.BeginAnimation(CircularProgressBar.ProgressProperty, animation);

6. 最佳实践

  1. 性能优化

    • 避免频繁更新进度值(使用动画或定时器)
    • 对于静态进度显示,考虑使用模板绑定而非代码更新
    • 使用Double类型而非Int类型确保平滑动画
    • 考虑使用Composition API进行硬件加速渲染
  2. 样式定制

    • 通过重写ControlTemplate完全自定义外观
    • 使用DynamicResource实现主题切换
    • 考虑添加VisualStateManager支持不同状态
    • 使用Storyboard创建复杂动画效果
  3. 使用建议

    • 在ViewModel中管理进度状态
    • 使用MVVM模式进行数据绑定
    • 对于复杂场景,考虑继承并扩展功能
    • 使用行为(Behaviors)增强交互性

7. 常见问题解答

Q1: 为什么进度条不显示?

  • 检查是否设置了Progress属性(0-100)
  • 确认ControlTemplate是否正确应用
  • 验证ProgressBrush和TrackBrush是否有有效值

Q2: 动画效果不流畅?

  • 确保使用DoubleAnimation而非离散值
  • 考虑降低帧率或简化路径计算
  • 检查是否有其他高CPU使用率操作影响性能

Q3: 如何实现多色进度条?

  • 使用LinearGradientBrush作为ProgressBrush
  • 考虑分段绘制多个Arc
  • 使用自定义绘制逻辑实现高级效果

Q4: 如何适配高DPI屏幕?

  • 使用ViewBox容器确保缩放
  • 设置UseLayoutRounding=“True”
  • 考虑基于实际像素调整厚度

8. 应用场景

1. 文件上传/下载

1
2
3
4
<local:CircularProgressBar 
Progress="{Binding DownloadProgress}"
ProgressBrush="{StaticResource PrimaryColorBrush}"
ShowPercentage="True"/>

2. 系统健康状态

1
2
3
4
<local:CircularProgressBar 
Progress="{Binding CpuUsage}"
ProgressBrush="{Binding CpuUsage, Converter={StaticResource UsageToColorConverter}}"
WarningThreshold="80"/>

3. 游戏加载界面

1
2
3
4
5
<local:CircularProgressBar
Progress="{Binding LoadingProgress}"
ProgressBrush="#FF5722"
TrackBrush="#212121"
Style="{StaticResource GameLoadingStyle}"/>

4. 仪表盘控件

1
2
3
4
5
6
<local:CircularProgressBar
Progress="{Binding SalesTargetCompletion}"
ProgressBrush="#4CAF50"
TrackBrush="#F44336"
StartAngle="-90"
EndAngle="90"/>

9. 扩展资源