.Net Wpf OxyPlot波形控件使用

Posted シ゛甜虾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net Wpf OxyPlot波形控件使用相关的知识,希望对你有一定的参考价值。

程序下载

https://download.csdn.net/download/g313105910/31986447https://download.csdn.net/download/g313105910/31986447

官方网站  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波形控件使用的主要内容,如果未能解决你的问题,请参考以下文章

在 WPF 中使用数据绑定时,OxyPlot 不刷新

如何在 OxyPlot 图表上绘制 MULTIPLE LineSeries?

在 XAML 中设置 WPF OxyPlot PlotViews 的样式

OxyPlot WPF 不适用于按钮单击

OxyPlot 导出图片及 WPF 元素导出为图片的方法

InvalidatePlot 上 WPF 中大数据的 OxyPlot 性能问题