.Net Wpf OxyPlot波形控件使用
Posted シ゛甜虾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net Wpf OxyPlot波形控件使用相关的知识,希望对你有一定的参考价值。
程序下载
官方网站 OxyPlot https://oxyplot.github.io/
官方帮助文档
WPF — OxyPlot 2015.1 documentationhttps://oxyplot.readthedocs.io/en/latest/getting-started/hello-wpf.html
简单示例打开串口,解析x,y,z数据并显示
协议字符型X:11,Y:111.01,Z:21.5\\r\\n
第一步通过Nuget安装OxyPlot.Wpf
新建Bootstrapper.cs类,引入Caliburn.Micro
using Caliburn.Micro;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace DisplayToolCollection
{
public class Bootstrapper : BootstrapperBase
{
SimpleContainer _container;
public Bootstrapper()
{
System.Windows.FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
Initialize();
}
protected override void Configure()
{
base.Configure();
_container = new SimpleContainer();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<MainWindowView>();
_container.Singleton<MainWindowViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<MainWindowViewModel>();
}
}
}
MainWindowView.xaml
<Window x:Class="DisplayToolCollection.MainWindowView"
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:oxy="http://oxyplot.org/wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DisplayToolCollection"
mc:Ignorable="d"
Title="数据可视化显示" Height="768" Width="1024">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="240"/>
</Grid.ColumnDefinitions>
<!--<Grid.ContextMenu >
<ContextMenu>
<MenuItem Header="复位" x:Name="Reset"/>
</ContextMenu>
</Grid.ContextMenu>-->
<oxy:PlotView Model="{Binding _model}"></oxy:PlotView>
<StackPanel Margin="2,20,2,2" Grid.Column="1">
<GroupBox Header="常用设置">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="串口号:" Width="80" VerticalAlignment="Center"/>
<TextBox Text="{Binding PortName}" Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="波特率:" Width="80" VerticalAlignment="Center"/>
<TextBox Text="{Binding BaudRate}" Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>
<!--<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="数据位数:" Width="80" VerticalAlignment="Center"/>
<TextBox Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="停止位:" Width="80" VerticalAlignment="Center"/>
<TextBox Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="奇偶校验位:" Width="80" VerticalAlignment="Center"/>
<TextBox Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="流控:" Width="80" VerticalAlignment="Center"/>
<TextBox Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>-->
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="窗口数据个数:" Width="80" VerticalAlignment="Center"/>
<TextBox Text="{Binding WindowsCount}" Width="120" VerticalAlignment="Center">
</TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="操作:" Width="80" VerticalAlignment="Center"/>
<Button Content="打开串口" Width="55" x:Name="OpenSerial" IsEnabled="{Binding IsOpenSerial}"/>
<TextBlock Text="" Width="10" VerticalAlignment="Center"/>
<Button Content="关闭串口" Width="55" x:Name="CloseSerial" IsEnabled="{Binding IsCloseSerial}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="图表:" Width="80" VerticalAlignment="Center"/>
<Button Content="复位" Width="55" x:Name="Reset"/>
<TextBlock Text="" Width="10" VerticalAlignment="Center"/>
<Button Content="清空" Width="55" x:Name="Clear"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="扩展:" Width="80" VerticalAlignment="Center"/>
<Button Content="截图" Width="55" x:Name="Snap"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="信息:" Width="80" VerticalAlignment="Center"/>
<TextBox Text="{Binding Info}" Width="120" Height="200" VerticalScrollBarVisibility="Visible" />
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
</Grid>
</Window>
MainWindowViewModel.cs
using Caliburn.Micro;
using DisplayToolCollection.Properties;
using Microsoft.Win32;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace DisplayToolCollection
{
public class MainWindowViewModel : PropertyChangedBase
{
private string _portName;
public string PortName
{
get { return _portName; }
set { _portName = value; Settings.Default.PortName = value; Settings.Default.Save(); NotifyOfPropertyChange(() => PortName); }
}
private int _baudRate;
public int BaudRate
{
get { return _baudRate; }
set { _baudRate = value; Settings.Default.BaudRate = value; Settings.Default.Save(); NotifyOfPropertyChange(() => BaudRate); }
}
private int _windowsCount;
public int WindowsCount
{
get { return _windowsCount; }
set { _windowsCount = value; Settings.Default.WindowsCount = value; Settings.Default.Save(); NotifyOfPropertyChange(() => WindowsCount); }
}
private string _info;
public string Info
{
get { return _info; }
set { _info = value; NotifyOfPropertyChange(() => Info); }
}
private bool _isOpenSerial = true;
public bool IsOpenSerial
{
get { return _isOpenSerial; }
set { _isOpenSerial = value; NotifyOfPropertyChange(() => IsOpenSerial); }
}
private bool _isCloseSerial = false;
public bool IsCloseSerial
{
get { return _isCloseSerial; }
set { _isCloseSerial = value; NotifyOfPropertyChange(() => IsCloseSerial); }
}
private SerialPort _serialPort = new SerialPort();
public PlotModel _model { get; private set; }
private LineSeries _oneLine;
private LineSeries _twoLine;
private LineSeries _threeLine;
//private LineSeries _fourLine;
//private LineSeries _fiveLine;
//private LineSeries _sixLine;
#if ShowTime
List<VoltagePoint> _oneData = new List<VoltagePoint>();
List<VoltagePoint> _twoData = new List<VoltagePoint>();
List<VoltagePoint> _threeData = new List<VoltagePoint>();
#else
List<DataPoint> _oneData = new List<DataPoint>();
List<DataPoint> _twoData = new List<DataPoint>();
List<DataPoint> _threeData = new List<DataPoint>();
#endif
//List<VoltagePoint> _fourData = new List<VoltagePoint>();
//List<VoltagePoint> _fiveData = new List<VoltagePoint>();
//List<VoltagePoint> _sixData = new List<VoltagePoint>();
public MainWindowViewModel()
{
PortName = Settings.Default.PortName;
BaudRate = Settings.Default.BaudRate;
WindowsCount = Settings.Default.WindowsCount;
this._model = new PlotModel { Title = "数据可视化显示" };
#if ShowTime
this._model.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, StringFormat = "HH:mm:ss" });
_oneLine = new LineSeries
{
StrokeThickness = 1,
Title = $"一",
ItemsSource = _oneData,
Color = OxyColors.Red,
MarkerType = MarkerType.None,
DataFieldX = "Time",
DataFieldY = "Voltage",
TrackerFormatString = "Time: {2:yyyy/MM/dd HH:mm:ss.fff}\\n值: {4}"
};
_twoLine = new LineSeries
{
StrokeThickness = 1,
Title = $"二",
ItemsSource = _twoData,
Color = OxyColors.Blue,
DataFieldX = "Time",
DataFieldY = "Voltage",
TrackerFormatString = "Time: {2:yyyy/MM/dd HH:mm:ss.fff}\\n值: {4}"
};
_threeLine = new LineSeries
{
StrokeThickness = 1,
Title = $"三",
ItemsSource = _threeData,
Color = OxyColors.Green,
DataFieldX = "Time",
DataFieldY = "Voltage",
TrackerFormatString = "Time: {2:yyyy/MM/dd HH:mm:ss.fff}\\n值: {4}"
};
#else
_oneLine = new LineSeries
{
StrokeThickness = 1,
Title = $"x轴数据",
ItemsSource = _oneData,
Color = OxyColors.Red,
MarkerType = MarkerType.None,
};
_twoLine = new LineSeries
{
StrokeThickness = 1,
Title = $"y轴数据",
ItemsSource = _twoData,
Color = OxyColors.Blue
};
_threeLine = new LineSeries
{
StrokeThickness = 1,
Title = $"z轴数据",
ItemsSource = _threeData,
Color = OxyColors.Green
};
#endif
_model.Series.Add(_oneLine);
_model.Series.Add(_twoLine);
_model.Series.Add(_threeLine);
//for (int i = 0; i < WindowsCount; i++)
//{
// _oneData.Add(new DataPoint(_oneData.Count, 0));
// _twoData.Add(new DataPoint(_twoData.Count, 0));
// _threeData.Add(new DataPoint(_threeData.Count, 0));
//}
_model.InvalidatePlot(true);
}
public void OpenSerial()
{
try
{
if (_serialPort.IsOpen == false)
{
_serialPort.BaudRate = BaudRate; //波特率
_serialPort.PortName = PortName; //串口号
_serialPort.StopBits = StopBits.One; //停止位
_serialPort.DataBits = 8; //数据位
_serialPort.Parity = Parity.None; //奇偶校验位
_serialPort.Handshake = Handshake.None; //流控
_serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.SerialPortDataReceived);
_serialPort.Open();
Info = $"打开成功!";
IsOpenSerial = false;
IsCloseSerial = !IsOpenSerial;
}
}
catch(Exception ex)
{
Info = $"打开失败!错误信息:{ex.Message}!";
}
}
public void CloseSerial()
{
try
{
if (_serialPort.IsOpen == true)
{
_serialPort.Close();
Info = $"关闭成功!";
}
}
catch (Exception ex)
{
Info = $"关闭失败!错误信息:{ex.Message}!";
}
IsOpenSerial = true;
IsCloseSerial = !IsOpenSerial;
}
public void Reset()
{
_model.ResetAllAxes();
_model.InvalidatePlot(true);
}
public void Clear()
{
_oneData.Clear();
_twoData.Clear();
_threeData.Clear();
_model.InvalidatePlot(true);
}
private void SerialPortDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
try
{
System.IO.Ports.SerialPort com = (System.IO.Ports.SerialPort)sender;
int n = com.BytesToRead;//先记录下来,避免丢失
byte[] buf = new byte[n];
com.Read(buf, 0, n);//读取缓冲区数据
DataLine dataLine = null;
Buffs.Protocol(buf, buf.Length, out dataLine);
if (dataLine != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
if (_oneData.Count >= WindowsCount)
{
_oneData.RemoveAt(0);
}
if (_twoData.Count >= WindowsCount)
{
_twoData.RemoveAt(0);
}
if (_threeData.Count >= WindowsCount)
{
_threeData.RemoveAt(0);
}
#if ShowTime
_oneData.Add(new VoltagePoint() { Time = DateTime.Now, Voltage = dataLine.X });
_twoData.Add(new VoltagePoint() { Time = DateTime.Now, Voltage = dataLine.Y });
_threeData.Add(new VoltagePoint() { Time = DateTime.Now, Voltage = dataLine.Z });
#else
_oneData.Add(new DataPoint(_oneData.Count, dataLine.X));
for (int i=0;i< _oneData.Count();i++)
{
_oneData[i] = new DataPoint(i, _oneData[i].Y);
}
_twoData.Add(new DataPoint(_twoData.Count, dataLine.Y));
for (int i = 0; i < _twoData.Count(); i++)
{
_twoData[i] = new DataPoint(i, _twoData[i].Y);
}
_threeData.Add(new DataPoint(_threeData.Count, dataLine.Z));
for (int i = 0; i < _threeData.Count(); i++)
{
_threeData[i] = new DataPoint(i, _threeData[i].Y);
}
#endif
_model.InvalidatePlot(true);
});
}
}
catch(Exception ex)
{
Info = ex.Message;
}
}
/// <summary>
/// 界面截图
/// </summary>
public void Snap()
{
var mainWindow = System.Windows.Application.Current.Windows.OfType<MainWindowView>().FirstOrDefault();
if (mainWindow != null)
{
//弹出保存对话框
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "jpg files(*.jpg)|*.jpg|All files(*.*)|*.*";
if (sfd.ShowDialog() == true)
{
string fileName = sfd.FileName;
SaveFrameworkElementToImage(mainWindow, fileName);
}
}
}
/// <summary>
/// 保存截图
/// </summary>
/// <param name="ui">控件名称</param>
/// <param name="filename">图片文件名</param>
public void SaveFrameworkElementToImage(FrameworkElement ui, string filename)
{
try
{
System.IO.FileStream ms = new System.IO.FileStream(filename, System.IO.FileMode.Create);
System.Windows.Media.Imaging.RenderTargetBitmap bmp = new System.Windows.Media.Imaging.RenderTargetBitmap((int)ui.ActualWidth, (int)ui.ActualHeight, 96d, 96d, System.Windows.Media.PixelFormats.Pbgra32);
bmp.Render(ui);
System.Windows.Media.Imaging.JpegBitmapEncoder encoder = new System.Windows.Media.Imaging.JpegBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bmp));
encoder.Save(ms);
ms.Close();
}
catch (Exception ex)
{
Info = ex.Message;
}
}
}
}
协议解析ProtocalBasic.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DisplayToolCollection
{
public static class Buffs
{
static Byte[] _resolverBuf = new Byte[8192000];
static int _resolverBufLen = 0;
public static void Protocol(Byte[] buf, int len , out DataLine dataLine)
{
AddResolverBuf(buf, len);
var dataBuf = LookResolverBuf();
var deleteLen = DeleteResolver(dataBuf, out dataLine);
DeleteResolverBuf(deleteLen);
}
private static int DeleteResolver(Byte[] buf,out DataLine dataLine)
{
dataLine = null;
int byteCount = 0;
int len = 0;
if (buf == null)
{
return byteCount;
}
if (buf.Length >= 8)
{
for (int i = 0; i < buf.Length - 1; i++)
{
if (buf[i] == '\\r' && buf[i + 1] == '\\n')
{
len = i + 1 + 1;
dataLine = ProtocalBasic.Protocol(buf, len);
return len;
}
}
}
return byteCount;
}
private static Byte[] LookResolverBuf()
{
Byte[] tempByte = new Byte[_resolverBufLen];
Buffer.BlockCopy(_resolverBuf, 0, tempByte, 0, _resolverBufLen);
return tempByte;
}
private static bool AddResolverBuf(Byte[] buf, int len)
{
bool isOk = false;
if (len < 0)
{
return isOk;
}
if ((len + _resolverBufLen) < _resolverBuf.Length)
{
Buffer.BlockCopy(buf, 0, _resolverBuf, _resolverBufLen, len);
_resolverBufLen += len;
isOk = true;
}
return isOk;
}
private static bool DeleteResolverBuf(int len)
{
bool isOk = false;
if (len < 0)
{
return isOk;
}
if (len == 0)
{
isOk = true;
return isOk;
}
if (len <= _resolverBufLen)
{
Buffer.BlockCopy(_resolverBuf, len, _resolverBuf, 0, _resolverBufLen - len);
_resolverBufLen = _resolverBufLen - len;
isOk = true;
}
return isOk;
}
}
}
以上是关于.Net Wpf OxyPlot波形控件使用的主要内容,如果未能解决你的问题,请参考以下文章
如何在 OxyPlot 图表上绘制 MULTIPLE LineSeries?