1、文件架构
2、加载依赖项
Newtonsoft.Json
OxyPlot.Wpf
3、NotifyBase.cs
namespace Accurate.Common
{public class NotifyBase : INotifyPropertyChanged{public event PropertyChangedEventHandler? PropertyChanged;public void DoNotify([CallerMemberName] string prppertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prppertyName));}}
}
4、CartesianChartViewModel.cs
namespace Accurate.Controls.ViewModels
{public class CartesianChartViewModel:NotifyBase{/// <summary>/// 定义plot属性/// </summary>private PlotModel? plotModel;public PlotModel? PlotModel{get { return plotModel; }set { plotModel = value; this.DoNotify(); }}/// <summary>/// 构造函数/// </summary>public CartesianChartViewModel(){PlotModel=new PlotModel();}}
}
5、CartesianChart.xaml
<UserControl x:Class="Accurate.Controls.CartesianChart"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:Accurate.Controls"xmlns:oxy="http://oxyplot.org/wpf"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><Border Name="Layout" BorderBrush="#DDD" BorderThickness="1" CornerRadius="10" Background="LightBlue"><Border.Effect><DropShadowEffect Color="#F2F2F2" ShadowDepth="10" BlurRadius="10" Opacity="0.4" Direction="270"/></Border.Effect><oxy:PlotView x:Name="PlotView" Model="{Binding Path= PlotModel}"></oxy:PlotView></Border>
</UserControl>
6、CartesianChart.xaml.cs
namespace Accurate.Controls
{/// <summary>/// CartesianChart.xaml 的交互逻辑/// </summary>public partial class CartesianChart : UserControl{//Plotpublic CartesianChartViewModel? viewModel;//曲线数组public LineSeries[] lineSeries;/// <summary>/// 曲线属性设置属性/// </summary>public string ChartSetStr{get { return (string)GetValue(ChartSetStrProperty); }set { SetValue(ChartSetStrProperty, value); }}// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...public static readonly DependencyProperty ChartSetStrProperty =DependencyProperty.Register("ChartSetStr", typeof(string), typeof(CartesianChart), new PropertyMetadata(default(string),new PropertyChangedCallback(OnSetPropertyChanged)));/// <summary>/// 设置改变处理/// </summary>/// <param name="d"></param>/// <param name="e"></param>private static void OnSetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){if(d is CartesianChart){CartesianChart chart = (CartesianChart)d;chart.CreatePlot();}}/// <summary>/// 构造函数/// </summary>public CartesianChart(){InitializeComponent();this.Loaded += CartesianChart_Loaded;Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;}/// <summary>/// 退出时释放资源/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void Dispatcher_ShutdownStarted(object? sender, EventArgs e){//timer?.Stop();}/// <summary>/// 控件加载/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void CartesianChart_Loaded(object sender, RoutedEventArgs e){CreatePlot();}/// <summary>/// 获取轴名称/// </summary>/// <param name="axisType"></param>/// <returns></returns>private string GetAxisKey(AxisType axisType){string str = "";switch (axisType){case AxisType.X:str = "x";break;case AxisType.Y:str = "y";break;case AxisType.X1:str = "x1";break;case AxisType.Y1:str = "y1";break;}return str;}/// <summary>/// 轴设置/// </summary>/// <param name="axisItemModels"></param>private void LinearAxisSet(ObservableCollection<AxisItemModel> axisItemModels){if(axisItemModels != null && axisItemModels.Count > 1){for(int i = 0; i < axisItemModels.Count; i++){var axis = new LinearAxis();//设置轴类型axis.Title = axisItemModels[i].Name;axis.Key = GetAxisKey(axisItemModels[i].Type);axis.Position = axisItemModels[i].Position;//axis.Minimum = axisItemModels[i].Minimum;//axis.MajorStep = axisItemModels[i].Step;//axis.Maximum = axisItemModels[i].Maximum;viewModel?.PlotModel?.Axes.Add(axis);}}}/// <summary>/// 曲线设置/// </summary>/// <param name="curveItemModels"></param>private void LineSeriesSet(ObservableCollection<CurveItemModel> curveItemModels){if(curveItemModels != null && curveItemModels.Count > 1){lineSeries = new LineSeries[curveItemModels.Count];for (int i=0;i<curveItemModels.Count;i++){lineSeries[i]=new LineSeries();lineSeries[i].Title = curveItemModels[i].Name;lineSeries[i].YAxisKey = GetAxisKey(curveItemModels[i].Type);lineSeries[i].Color = GetOxyColor(curveItemModels[i].ItemColor);lineSeries[i].MarkerType = curveItemModels[i].MarkType;lineSeries[i].MarkerStroke = GetOxyColor(curveItemModels[i].MarkStroke);lineSeries[i].MarkerSize = curveItemModels[i].MarkSize;viewModel?.PlotModel.Series.Add(lineSeries[i]);}} }private List<ChartData> GetChartData(){var data=new List<ChartData>(){new ChartData{Date=DateTime.Now.Date.AddDays(-10),Data1=120,Data2=20},new ChartData{Date=DateTime.Now.Date.AddDays(-8),Data1=100,Data2=80},new ChartData{Date=DateTime.Now.Date.AddDays(-6),Data1=60,Data2=100},new ChartData{Date=DateTime.Now.Date.AddDays(-4),Data1=100,Data2=70},new ChartData{Date=DateTime.Now.Date.AddDays(-2),Data1=50,Data2=90}};return data;}private void LineSeriesDataSourceSet(List<ChartData> list){for(int i=0;i<2;i++){lineSeries[i].ItemsSource = list;lineSeries[i].DataFieldX = "Date";lineSeries[i].DataFieldY = "Data" + i;viewModel?.PlotModel.Series.Add(lineSeries[i]);}}/// <summary>/// 颜色转换/// </summary>/// <param name="color"></param>/// <returns></returns>private OxyColor GetOxyColor(string color){OxyColor oxyColor= OxyColors.Black;switch(color){case "Black":oxyColor = OxyColors.Black;break;case "Red":oxyColor = OxyColors.Red;break;case "Blue":oxyColor = OxyColors.Blue;break;case "Green":oxyColor = OxyColors.Green;break;case "Yellow":oxyColor = OxyColors.Yellow;break;case "Violet":oxyColor = OxyColors.Violet;break;}return oxyColor;}/// <summary>/// Chart设置/// </summary>private void CreatePlot(){if (ChartSetStr == null) return;ChartSetModel ChartSet=JsonConvert.DeserializeObject<ChartSetModel>(ChartSetStr);//曲线设置viewModel = new CartesianChartViewModel();PlotView.DataContext = viewModel;viewModel.PlotModel.Title = ChartSet.Name;viewModel.PlotModel.Legends.Add(new Legend{LegendPlacement = LegendPlacement.Outside,LegendPosition = LegendPosition.BottomCenter,LegendOrientation = LegendOrientation.Horizontal,LegendBorderThickness = 0,LegendTextColor = OxyColors.LightGray});LinearAxisSet(ChartSet.AxisItemModels);LineSeriesSet(ChartSet.CurveItemModels);//LineSeriesDataSourceSet(GetChartData());}}
}
7、AxisItemModel.cs
namespace Accurate.Model
{/// <summary>/// 曲线设置类/// </summary>public class ChartSetModel{public string Name { get; set; } = "曲线示例";public ObservableCollection<AxisItemModel> AxisItemModels { get; set; }=new ObservableCollection<AxisItemModel>();public ObservableCollection<CurveItemModel> CurveItemModels { get; set; } = new ObservableCollection<CurveItemModel>();}/// <summary>/// 坐标轴设置/// </summary>public class AxisItemModel{public string Name { get; set; } = "X";public AxisType Type { get; set; } = 0;public AxisPosition Position { get; set; } = AxisPosition.Bottom;public double Minimum { get; set; } = 0;public double Step { get; set; } = 1;public double Maximum { get; set; } = 100;}/// <summary>/// 曲线设置/// </summary>public class CurveItemModel{public string Name { get; set; } = "时间";public AxisType Type { get; set; } = 0;public string ItemColor { get; set; } = "Black";public MarkerType MarkType { get; set; } = MarkerType.Circle;public string MarkStroke { get; set; } = "Black";public double MarkSize { get; set; } = 3;}/// <summary>/// 枚举轴类型/// </summary>public enum AxisType:int{X,Y,X1,Y1}/// <summary>/// 数据Item(单点数据添加)/// </summary>public class DataItem{public double DataX { get; set; }public List<double> DataYs { get; set; }=new List<double>();}public class ChartData{public DateTime Date { get; set; }public double Data1 { get; set; }public double Data2 { get; set; }}
}
8、MainWindowViewModel。cs
namespace Accurate.ViewModel
{public class MainWindowViewModel:NotifyBase{private string chartSetString;public string ChartSetString{get { return chartSetString; }set { chartSetString = value;this.DoNotify(); }}public ChartSetModel ChartSetData { get; set; } = new ChartSetModel();public MainWindowViewModel(){RefreshChartSet();}private void RefreshChartSet(){ChartSetData.Name = "正弦和余弦曲线";//设置坐标轴ChartSetData.AxisItemModels.Add(new AxisItemModel() { Name = "X", Type = AxisType.X, Position = OxyPlot.Axes.AxisPosition.Bottom, Minimum = 0, Step = 1, Maximum = 200 });ChartSetData.AxisItemModels.Add(new AxisItemModel() { Name = "Y", Type = AxisType.Y, Position = OxyPlot.Axes.AxisPosition.Left, Minimum = -10, Step = 1, Maximum = 10 });//设置曲线ChartSetData.CurveItemModels.Add(new CurveItemModel{Name = "正弦曲线",Type = AxisType.Y,ItemColor = "Red",MarkType = MarkerType.Circle,MarkStroke = "Blue",MarkSize = 3});ChartSetData.CurveItemModels.Add(new CurveItemModel{Name = "余弦曲线",Type = AxisType.Y,ItemColor = "Green",MarkType = MarkerType.Square,MarkStroke = "Yellow",MarkSize = 3});ChartSetString = JsonConvert.SerializeObject(ChartSetData);}}
}
9、MainWindow.xaml
<Window x:Class="Accurate.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:Accurate"xmlns:controls="clr-namespace:Accurate.Controls"mc:Ignorable="d"Title="MainWindow" Height="600" Width="800"><Grid><controls:CartesianChart x:Name="Cartesian" ChartSetStr="{Binding ChartSetString}" Height="450" Width="800" HorizontalAlignment="Center" VerticalAlignment="Center"/></Grid>
</Window>
10、MainWindow.xaml.cs
namespace Accurate
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{private DispatcherTimer? timer;public MainWindow(){InitializeComponent();this.DataContext = new MainWindowViewModel();//this.Cartesian.lineSeriestimer = new DispatcherTimer();timer.Interval = TimeSpan.FromMilliseconds(100);timer.Tick += TimerTick;timer.Start();}double x = 0;/// <summary>/// 定时更新数据/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void TimerTick(object? sender, EventArgs e){double y = 10 * Math.Sin(x / 10.0);this.Cartesian.lineSeries[0]?.Points.Add(new DataPoint(x, y));double y1 = 10 * Math.Cos(x / 10.0);this.Cartesian.lineSeries[1]?.Points.Add(new DataPoint(x, y1));x++;if (this.Cartesian.lineSeries[0]?.Points.Count > 200){this.Cartesian.lineSeries[0].Points.RemoveAt(0);}if (this.Cartesian.lineSeries[1]?.Points.Count > 200){this.Cartesian.lineSeries[1].Points.RemoveAt(0);}this.Cartesian.viewModel?.PlotModel.InvalidatePlot(true);}}
}