基于WPF的折线图和仪表盘实现

基于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. 性能优化

    • 折线图数据量超过1万点时启用Chart.PlotOptions.DisableHover禁用悬停
    • 使用Chart.PlotOptions.Animate = false关闭动画提升渲染速度
  2. 样式统一

    <!-- 全局样式定义 -->
    <Style TargetType="lvc:CartesianChart">
        <Setter Property="LegendLocation" Value="Right"/>
        <Setter Property="Hoverable" Value="False"/>
    </Style>
    
  3. 数据预处理

    // 数据平滑处理
    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;
    }
    

 

专注于matlab/simulink,电子电路,编程