基于WPF的折线图和仪表盘实现
一、动态折线图实现(基于LiveCharts)
1. XAML界面
<Window x:Class="WpfCharts.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
Title="实时折线图" Height="600" Width="1200">
<Grid>
<!-- 折线图容器 -->
<lvc:CartesianChart Series="{Binding ChartSeries}"
AxisX="{Binding XAxis}"
AxisY="{Binding YAxis}"
Hoverable="True"
DataClick="Chart_OnDataClick">
<lvc:CartesianChart.LegendLocation>Right</lvc:CartesianChart.LegendLocation>
</lvc:CartesianChart>
<!-- 控制面板 -->
<StackPanel VerticalAlignment="Bottom" Margin="20">
<Button Content="添加数据" Click="AddData_Click"/>
<Slider Minimum="0" Maximum="100" Value="{Binding YAxis.MaxValue}" Width="200"/>
</StackPanel>
</Grid>
</Window>
2. ViewModel数据模型
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class ChartViewModel : INotifyPropertyChanged
{
private SeriesCollection _chartSeries;
private Axis _xAxis;
private Axis _yAxis;
private double _yMax = 100;
public SeriesCollection ChartSeries
{
get => _chartSeries;
set
{
_chartSeries = value;
OnPropertyChanged();
}
}
public Axis XAxis
{
get => _xAxis;
set
{
_xAxis = value;
OnPropertyChanged();
}
}
public Axis YAxis
{
get => _yAxis;
set
{
_yAxis = value;
OnPropertyChanged();
}
}
public ChartViewModel()
{
InitChart();
StartRealTimeUpdate();
}
private void InitChart()
{
// 初始化系列
var series = new LineSeries
{
Title = "温度监测",
Values = new ChartValues<double>(),
LineSmoothness = 0.5,
StrokeThickness = 2,
PointGeometrySize = 8,
Fill = new SolidColorBrush(Colors.Transparent)
};
ChartSeries = new SeriesCollection { series };
// 配置坐标轴
XAxis = new Axis
{
Labels = new[] { "00:00", "02:00", "04:00", "06:00", "08:00", "10:00" },
LabelsRotation = 45,
Separator = new Separator { Step = 1 }
};
YAxis = new Axis
{
MaxValue = _yMax,
MinValue = 0,
Title = "温度(℃)"
};
}
private void StartRealTimeUpdate()
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (s, e) =>
{
var rand = new Random();
var newValue = rand.NextDouble() * 100;
Application.Current.Dispatcher.Invoke(() =>
{
ChartSeries[0].Values.Add(newValue);
if (ChartSeries[0].Values.Count > 6)
ChartSeries[0].Values.RemoveAt(0);
});
};
timer.Start();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
二、自定义仪表盘实现
1. XAML控件模板
<UserControl x:Class="WpfCharts.GaugeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="400">
<Grid>
<Border CornerRadius="200"
Background="#2D2D2D"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ClipToBounds="True">
<Grid>
<!-- 刻度线 -->
<Canvas>
<Canvas.RenderTransform>
<RotateTransform Angle="-135"/>
</Canvas.RenderTransform>
<Canvas.Resources>
<Style TargetType="Line">
<Setter Property="Stroke" Value="White"/>
<Setter Property="StrokeThickness" Value="2"/>
</Style>
</Canvas.Resources>
<!-- 主刻度 -->
<Line X1="180" Y1="200" X2="180" Y2="220"/>
<Line X1="220" Y1="200" X2="220" Y2="220"/>
<!-- 动态指针 -->
<Path Data="M200,200 L200,180 L220,200"
Stroke="Red"
StrokeThickness="3">
<Path.RenderTransform>
<RotateTransform x:Name="rtPointer" Angle="-135"/>
</Path.RenderTransform>
</Path>
</Canvas>
<!-- 数值显示 -->
<TextBlock Text="{Binding Value}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="48"
Foreground="White"/>
</Grid>
</Border>
</Grid>
</UserControl>
2. 代码逻辑
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfCharts
{
public partial class GaugeControl : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(GaugeControl),
new PropertyMetadata(0.0, OnValueChanged));
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public GaugeControl()
{
InitializeComponent();
this.DataContext = this;
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var gauge = d as GaugeControl;
gauge?.UpdateAngle((double)e.NewValue);
}
private void UpdateAngle(double value)
{
// 将0-100映射到-135~135度
double angle = (value / 100.0) * 270 - 135;
rtPointer.Angle = angle;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
三、功能特性对比
| 组件 | 折线图 | 仪表盘 |
|---|---|---|
| 核心库 | LiveCharts | 自定义Canvas绘制 |
| 数据绑定 | 支持动态集合更新 | 依赖属性绑定数值 |
| 交互功能 | 悬停提示、点击事件 | 无交互(可扩展) |
| 性能 | 适合实时数据流(万级数据) | 适合静态/低频更新 |
| 定制化 | 预设多种图表样式 | 完全自定义外观 |
四、扩展应用场景
1. 折线图增强功能
// 添加多系列对比
var targetSeries = new LineSeries
{
Title = "目标值",
Values = new ChartValues<double> { 80, 82, 78, 85 },
StrokeDashArray = new DoubleCollection {5,3} // 虚线样式
};
// 添加报警区域
var areaSeries = new AreaSeries
{
Values = new ChartValues<double> { 90, 90, 90, 90 },
Fill = new SolidColorBrush(Colors.Red) { Opacity = 0.3 }
};
2. 仪表盘高级功能
// 添加刻度标签
for (int i = 0; i <= 100; i += 10)
{
double angle = (i / 100.0) * 270 - 135;
TextBlock tb = new TextBlock
{
Text = i.ToString(),
Foreground = new SolidColorBrush(Colors.LightGray),
RenderTransform = new RotateTransform(angle)
};
Canvas.SetLeft(tb, 160);
Canvas.SetTop(tb, 180);
this.ContentRoot.Children.Add(tb);
}
// 添加指针阴影
DropShadowEffect shadow = new DropShadowEffect
{
Color = Colors.DarkRed,
ShadowDepth = 3,
BlurRadius = 5
};
pointer.Effect = shadow;
参考代码 WPF折线图 以及 仪表盘 示例源码 www.youwenfan.com/contentcnr/93633.html
五、最佳实践建议
-
性能优化
- 折线图数据量超过1万点时启用
Chart.PlotOptions.DisableHover禁用悬停 - 使用
Chart.PlotOptions.Animate = false关闭动画提升渲染速度
- 折线图数据量超过1万点时启用
-
样式统一
<!-- 全局样式定义 --> <Style TargetType="lvc:CartesianChart"> <Setter Property="LegendLocation" Value="Right"/> <Setter Property="Hoverable" Value="False"/> </Style> -
数据预处理
// 数据平滑处理 public static ChartValues<double> SmoothData(ChartValues<double> source) { var smoothed = new ChartValues<double>(); for (int i = 1; i < source.Count - 1; i++) { smoothed.Add((source[i-1] + source[i] + source[i+1])/3); } return smoothed; }