使用WPF制作Livecharts.Wpf图标控件

发布于:2025-08-29 ⋅ 阅读:(15) ⋅ 点赞:(0)

摘要:本文演示了如何在WPF中使用LiveCharts.WPF创建双Y轴折线图控件,并集成到WinForms应用中。通过UcChart.xaml定义了包含左右双Y轴和动态X轴的图表布局,支持数据绑定和样式自定义。UcChart.xaml.cs实现了数据模型和属性变更通知,提供SetTitle方法动态设置轴标题。该控件具有缩放和拖拽功能,左右系列分别以蓝红两色显示,适用于需要展示多维度数据的场景。
在这里插入图片描述

在这里插入图片描述
WPF代码
UcChart.xaml代码

<UserControl x:Class="WpfChartControl.UcChart"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfChartControl"
             xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
             mc:Ignorable="d" 
             HorizontalAlignment="Stretch"
             VerticalAlignment="Stretch">
    <Grid Background="White" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
        <lvc:CartesianChart x:Name="chart" Zoom="Y" Pan="Y">
            <!-- 定义双 Y 轴 -->
            <lvc:CartesianChart.AxisY>
                <lvc:Axis x:Name="YLeftAxis" Title="{Binding LeftTitle}"  Foreground="Blue"  MaxValue="{Binding YLeftMax}"/>
                <lvc:Axis x:Name="YRightAxis" Title="{Binding RightTitle}" Position="RightTop" Foreground="Red"  MaxValue="{Binding YRightMax}">
                    <lvc:Axis.Separator>
                        <lvc:Separator></lvc:Separator>
                    </lvc:Axis.Separator>
                </lvc:Axis>
            </lvc:CartesianChart.AxisY>

            <!-- 定义 X 轴为动态分类轴 -->
            <lvc:CartesianChart.AxisX>
                <lvc:Axis x:Name="XAxis" Title="{Binding BottomTitle}" Labels="{Binding XLabels}" MaxValue="{Binding XAxisMax}">
                    <lvc:Axis.Separator>
                        <lvc:Separator StrokeThickness="1" Stroke="#DDDDDD"/>
                    </lvc:Axis.Separator>
                </lvc:Axis>
            </lvc:CartesianChart.AxisX>

            <!-- 定义两个数据系列 -->
            <lvc:CartesianChart.Series>
                
                <lvc:LineSeries 
                    Title="{Binding LeftTitle}"
                    Values="{Binding LeftValues}" 
                    ScalesYAt="0" 
                    LineSmoothness="0" 
                    Fill="Transparent"
                    StrokeThickness="2"
                    Stroke="Blue"/>
                
                <lvc:LineSeries 
                    Title="{Binding RightTitle}"
                    Values="{Binding RightValues}" 
                    ScalesYAt="1" 
                    LineSmoothness="0" 
                    Fill="Transparent"
                    StrokeThickness="2"
                    Stroke="Red"/>
            </lvc:CartesianChart.Series>
        </lvc:CartesianChart>
    </Grid>
</UserControl>

UcChart.xaml.cs代码

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using LiveCharts;
using LiveCharts.Wpf;

namespace WpfChartControl
{
    /// <summary>
    /// UcChart.xaml 的交互逻辑
    /// </summary>
    public partial class UcChart : UserControl, INotifyPropertyChanged
    {
        // 动态数据集合
        public ChartValues<double> LeftValues { get; set; } = new ChartValues<double>();
        public ChartValues<double> RightValues { get; set; } = new ChartValues<double>();
        // 动态 X 轴标签
        public ObservableCollection<string> XLabels { get; set; } = new ObservableCollection<string>();
        public string _LeftTitle { get; set; }
        public string _RightTitle { get; set; }
        public string BottomTitle { get; set; }

        public string LeftTitle 
        {
            get => _LeftTitle;
            set
            {
                _LeftTitle = value;
                OnPropertyChanged();
            }
        }
        public string RightTitle
        {
            get => _RightTitle;
            set
            {
                _RightTitle = value;
                OnPropertyChanged();
            }
        }
        public UcChart()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        public void SetTitle(string leftTitle, string rightTitle, string bottomTitle)
        {
            LeftTitle = leftTitle;
            RightTitle = rightTitle;
            BottomTitle = bottomTitle;
        }
        public void SetTitle(SeriesType seriesType, string title)
        {
            
            switch (seriesType)
            {
                case SeriesType.Left:
                    LeftTitle = title;
                    break;
                case SeriesType.Right:
                    RightTitle = title;
                    break;
                case SeriesType.Bottom:
                    BottomTitle = title;
                    break;
                default:
                    break;
            }
        }

        public void ChangeTitle(SeriesType seriesType, string title)
        {
            switch (seriesType)
            {
                case SeriesType.Left:
                    LeftTitle = title;
                    break;
                case SeriesType.Right:
                    RightTitle = title;
                    break;
                case SeriesType.Bottom:
                    BottomTitle = title;
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Clear清理数据 NaN使曲线图居中
        /// </summary>
        /// <param name="seriesType"></param>
        public void ClearData(SeriesType seriesType)
        {
            switch (seriesType)
            {
                case SeriesType.Left:
                    LeftValues.Clear();
                    YLeftAxis.MaxValue = double.NaN;//每次添加数据后,将代码恢复居中显示
                    YLeftAxis.MinValue = double.NaN;
                    break;
                case SeriesType.Right:
                    RightValues.Clear();
                    YRightAxis.MaxValue = double.NaN;
                    YRightAxis.MinValue = double.NaN;
                    break;
                case SeriesType.Bottom:
                    XLabels.Clear();
                    break;
                default:
                    break;
            }
        }

        // 外部调用方法:动态更新双轴数据和 X 轴标签
        public void UpdateData(double leftVal, double rightVal, string xLabel, int offset)
        {
            LeftValues.Add(leftVal);
            if (LeftValues.Count > 100)
            {
                LeftValues.RemoveAt(0);
            }
            RightValues.Add(rightVal);
            if (RightValues.Count > 100)
            {
                RightValues.RemoveAt(0);
            }
            XAxis.MaxValue = XLabels.Count + offset;
            XLabels.Add(xLabel);
            if (XLabels.Count > 100)
            {
                XLabels.RemoveAt(0);
            }
        }

        public void UpdateLeftData(double data)
        {
            LeftValues.Add(data);
            //if (LeftValues.Max() > 0)
            //{
            //    YLeftAxis.MaxValue = LeftValues.Max() * 1.1;
            //}
            //else 
            //{
            //    YLeftAxis.MaxValue = LeftValues.Max() * 0.9;
            //}
        }

        public void UpdateRightData(double data)
        {
            RightValues.Add(data);
            //if (RightValues.Max() > 0)
            //{
            //    YRightAxis.MaxValue = RightValues.Max() * 1.1;
            //}
            //else
            //{
            //    YRightAxis.MaxValue = RightValues.Max() * 0.9;
            //}
        }

        public void UpdateBottomData(string data, int offset)
        {
            XLabels.Add(data);
            XAxis.MaxValue = XLabels.Count + offset;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName=null)
        {
            PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
        }
    }

    public enum SeriesType : int
    {
        /// <summary>
        /// 左轴
        /// </summary>
        Left = 0,
        /// <summary>
        /// 右轴
        /// </summary>
        Right = 1,
        /// <summary>
        /// 底轴
        /// </summary>
        Bottom = 2
    }
}

Winform代码
LIvecharts控件dll和WPF制作的dll都要引入
在这里插入图片描述
代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using LiveCharts.Wpf;
using LiveCharts;
using WpfChartControl;

namespace WpfChartWinform
{
    public partial class Form1 : Form
    {
        private UcChart _wpfChart;
        private readonly System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();
        private readonly Random _random = new Random();
        private int _timeCounter = 0;
        ElementHost host = new ElementHost();
        public Form1()
        {
            InitializeComponent();
            InitializeChart();
            _wpfChart.SetTitle("左","右","底");
            InitializeTimer();
        }

        // 初始化 WPF 控件
        private void InitializeChart()
        {
            _wpfChart = new UcChart();
            host = new ElementHost
            {
                Dock = DockStyle.Fill,
                Child = _wpfChart
            };
            Controls.Add(host);
        }

        // 定时器驱动动态数据
        private void InitializeTimer()
        {
            _timer.Interval = 10; // 1秒更新一次
            _timer.Tick += (sender, e) =>
            {
                // 生成数据
                double leftValue = _random.Next(0, 100);
                double rightValue = _random.Next(100, 200);
                string xLabel = DateTime.Now.AddSeconds(_timeCounter++).ToString("ss.fff");
                this.Invoke(new Action(() =>
                {
                    // 更新图表
                    _wpfChart.UpdateData(leftValue, rightValue, xLabel, 0);
                }));

            };
            _timer.Start();
            //double[] douy1 = { 1, 3, 5, 7, 9 };
            ////double[] douy2 = { 2, 4, 6, 8, 10 };
            //double[] douy2 = { 0.0000000000099972, 0.0000000000100067, 0.0000000000100178, 0.0000000000099946, 0.0000000000099931 };
            //string[] doux = { "1", "2", "3", "4", "5" };
            //Task.Factory.StartNew(() => {

            //    for (int i=0;i<doux.Length;i++)
            //    {
            //        Thread.Sleep(100);
            //        this.Invoke(new Action(() =>
            //        {
            //            // 更新图表
            //            _wpfChart.UpdateBottomData(doux[i],0);
            //        }));
            //    }
            //    for (int i = 0; i < doux.Length; i++)
            //    {
            //        Thread.Sleep(100);
            //        this.Invoke(new Action(() =>
            //        {
            //            // 更新图表
            //            _wpfChart.UpdateLeftData(douy1[i]);
            //        }));
            //    }
            //    for (int i = 0; i < doux.Length; i++)
            //    {
            //        Thread.Sleep(100);
            //        this.Invoke(new Action(() =>
            //        {
            //            // 更新图表
            //            _wpfChart.UpdateRightData(douy2[i]);
            //        }));
            //    }

            //});
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            host.Size = this.ClientSize;
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到