前言
为什么我们需要动态图表?
日常开发中,我们经常需要将数据以图形化方式展示。最简单的做法是把数据导出到 Excel,然后手动绘制折线图或柱状图。但如果数据是实时变化的,比如监控 CPU、内存、网络流量等系统性能指标,手动绘图显然不现实。
本文将带大家使用 WPF + DynamicDataDisplay 实现一个实时动态更新的 CPU 使用率曲线图,支持自动刷新、缩放、拖拽浏览,并可一键复制图表到其他文档。
技术选型:DynamicDataDisplay 简介
DynamicDataDisplay (D3) 是微软研究院推出的一个强大的 WPF 数据可视化库,虽然项目已不再维护,但在轻量级实时图表场景下依然表现出色。
它支持:
- 
实时动态数据流绘图
 - 
坐标轴自定义(整型、浮点、时间等)
 - 
鼠标交互:拖拽、缩放、选择
 - 
多种图形类型:折线、散点、区域图等
 - 
图表导出与复制
 
注意:DynamicDataDisplay 仅支持 .NET Framework,不适用于 .NET Core / .NET 5+ 的跨平台项目。如需现代替代方案,可考虑 OxyPlot 或 LiveCharts。
项目搭建
1、创建 WPF 项目
使用 Visual Studio 创建一个新的 WPF 应用程序(.NET Framework)。
2、引入 DynamicDataDisplay
下载 DynamicDataDisplay.dll 并添加引用到项目中:
- 可从 微软官方存档 获取
 - 或通过 NuGet 安装(非官方包):
Install-Package DynamicDataDisplay 
3、添加命名空间
在 MainWindow.xaml 中引入 D3 命名空间:
            
            
              xml
              
              
            
          
          xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
        XAML 界面设计
            
            
              xml
              
              
            
          
          <Window x:Class="WpfPerformance.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
        Title="CPU Performance Monitor" 
        Loaded="Window_Loaded" 
        Height="400" Width="600">
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <!-- 顶部状态栏 -->
        <StackPanel Orientation="Horizontal" Margin="20,10,0,10">
            <TextBlock Text="CPU Usage: " 
                       FontSize="16" FontWeight="SemiBold" VerticalAlignment="Center"/>
            <TextBlock x:Name="cpuUsageText" 
                       FontSize="16" Foreground="Blue" VerticalAlignment="Center"/>
        </StackPanel>
        <!-- 图表区域 -->
        <d3:ChartPlotter x:Name="plotter" Grid.Row="1" Margin="10">
            <d3:ChartPlotter.HorizontalAxis>
                <d3:HorizontalIntegerAxis />
            </d3:ChartPlotter.HorizontalAxis>
            <d3:ChartPlotter.VerticalAxis>
                <d3:VerticalIntegerAxis />
            </d3:ChartPlotter.VerticalAxis>
            <d3:Header Content="CPU Performance History" FontSize="18"/>
            <d3:VerticalAxisTitle Content="Usage (%)" />
        </d3:ChartPlotter>
    </Grid>
</Window>
        控件说明
| 控件 | 作用 | 
|---|---|
<d3:ChartPlotter> | 
图表容器,提供绘图区域 | 
<d3:HorizontalIntegerAxis> | 
X 轴(时间/序号) | 
<d3:VerticalIntegerAxis> | 
Y 轴(百分比) | 
<d3:Header> | 
图表标题 | 
<d3:VerticalAxisTitle> | 
Y 轴标签 | 
C# 后台逻辑实现
1、成员变量定义
            
            
              csharp
              
              
            
          
          using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.DataSources;
namespace WpfPerformance
{
    public partial class MainWindow : Window
    {
        private ObservableDataSource<Point> dataSource = new ObservableDataSource<Point>();
        private PerformanceCounter cpuCounter = new PerformanceCounter();
        private DispatcherTimer timer = new DispatcherTimer();
        private int xIndex = 0; // X轴时间点
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}
        2、实时数据采集与绘图
            
            
              csharp
              
              
            
          
          private void AnimatedPlot(object sender, EventArgs e)
{
    // 配置性能计数器
    cpuCounter.CategoryName = "Processor";
    cpuCounter.CounterName = "% Processor Time";
    cpuCounter.InstanceName = "_Total"; // 所有核心平均值
    // 获取当前CPU使用率
    float cpuUsage = cpuCounter.NextValue();
    Point newPoint = new Point(xIndex, cpuUsage);
    // 异步追加数据点(线程安全)
    dataSource.AppendAsync(Dispatcher, newPoint);
    // 更新UI文本
    cpuUsageText.Text = $"{cpuUsage:F0}%";
    xIndex++;
}
        
NextValue()第一次调用会返回 0,建议在定时器启动前先调用一次预热。
3、窗口加载事件:初始化图表与定时器
            
            
              csharp
              
              
            
          
          private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // 将数据源绑定为折线图
    plotter.AddLineGraph(
        dataSource,
        Colors.Green,
        2.0,           // 线条粗细
        "CPU Usage"    // 图例名称
    );
    // 设置刷新频率:每秒一次
    timer.Interval = TimeSpan.FromSeconds(1);
    timer.Tick += AnimatedPlot;
    timer.Start();
    // 自动缩放视图以适应当前数据
    plotter.Viewport.FitToView();
}
        运行效果与交互功能
实时动态曲线
- 
每秒获取一次 CPU 使用率
 - 
数据点自动追加并绘制为绿色折线
 - 
图表自动滚动更新
 
鼠标交互操作
| 操作 | 功能 | 
|---|---|
| 左键拖拽 | 平移查看历史数据 | 
| 中键滚轮 | 缩放图表区域 | 
| 右键菜单 | 提供、Copy Plot"功能,可将图表复制为图像 | 
复制后可直接粘贴到 Word、PPT、OneNote 等办公软件中,非常方便!
常见问题
为什么第一次 NextValue() 返回 0?
PerformanceCounter.NextValue() 是差值计算,需要至少两次调用才能得出有效结果。建议在启动定时器前先调用一次、预热":
            
            
              csharp
              
              
            
          
          cpuCounter.NextValue(); // 预热
timer.Start();
        如何限制历史数据长度?
避免内存无限增长,可定期清理旧数据:
            
            
              csharp
              
              
            
          
          if (dataSource.Collection.Count > 100)
{
    dataSource.Collection.RemoveAt(0);
}
        替代方案推荐(适用于 .NET 5+)
由于 DynamicDataDisplay 不支持新版本 .NET,推荐以下现代库:
| 库名 | 特点 | 
|---|---|
| OxyPlot | 跨平台、轻量、支持 MVVM | 
| LiveCharts | 动画丰富、API 简洁、社区活跃 | 
| ScottPlot | 高性能、适合大数据量实时绘图 | 
完整源码下载
总结
本文通过一个完整的实例,展示了如何在 WPF 中使用 DynamicDataDisplay 实现:
- 
实时 CPU 使用率监控
 - 
动态曲线图绘制
 - 
用户交互与图表导出
 - 
多线程安全数据更新
 
尽管 DynamicDataDisplay 已停止维护,但它仍然是学习 WPF 图表编程的优秀起点。掌握其核心思想后,可以轻松迁移到更现代的图表库。
开发启示
- 实时数据可视化 ≠ 复杂
 - 选择合适的工具能极大提升开发效率
 - 图表不仅要好看,更要、好用
 
关键词: WPF、DynamicDataDisplay、CPU 使用率、实时图表、ObservableDataSource、PerformanceCounter
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
来源:cnblogs.com/gnielee/archive/2010/08/02/wpf-cpu-usage.html