zoukankan      html  css  js  c++  java
  • C#使用Oxyplot绘制监控界面

    C#中可选的绘图工具有很多,除了Oxyplot还有DynamicDataDisplay(已经改名为InteractiveDataDisplay)等等。不过由于笔者这里存在一些环境上的特殊要求,.Net Framework的版本被限制在4,而InteractiveDataPlay要求的最低版本是4.5.2。所以选择了对版本要求较低的Oxyplot。

    IDE为VS2012,创建工程后首先通过NuGet程序包管理器控制台,通过下述命令添加对Oxyplot的引用:

    PM> Install-Package Oxyplot.Core
    PM> Install-Package Oxyplot.Wpf

     XAML里添加上必要的引用:

    <Window x:Class="MonitorForm.MonitorForm"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:MonitorForm"
            xmlns:oxy="http://oxyplot.org/wpf"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <oxy:PlotView Model="{Binding Path= SimplePlotModel}"></oxy:PlotView>
    
        </Grid>
    </Window>

    笔者的开发场景是需要统计四类数据,因此开放四个公共方法供外部调用:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    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 System.ComponentModel;
    using System.Threading;
    
    namespace MonitorForm {
    
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MonitorForm : Window {
    
            private PlotViewModel _viewModel;
    
            public MonitorForm() {
    
                InitializeComponent();
    
                _viewModel = new PlotViewModel();
                this.DataContext = _viewModel;
            }
    
            public void addSendCount(int iCount) {
    
                _viewModel.addSendCount(iCount);
            }
    
            public void addUnsendCount(int iCount) {
    
                _viewModel.addUnsendCount(iCount);
            }
    
            public void addConfirmCount(int iCount) {
    
                _viewModel.addConfirmCount(iCount);
            }
    
            public void addDealCount(int iCount) {
    
                _viewModel.addDealCount(iCount);
            }
        }
    }

    MVVM模式,需要添加ViewModel层:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using OxyPlot;
    using OxyPlot.Series;
    using OxyPlot.Axes;
    using System.Threading.Tasks;
    using System.Threading;
    using System.Collections.Concurrent;
    
    namespace MonitorForm {
    
        public class PlotViewModel {
    
            /// <summary>
            /// 画直线
            /// </summary>
            public PlotModel SimplePlotModel { get; set; }
    
            //每条线对应一个队列用作实时数据统计
            private ConcurrentQueue<int> queueSend = new ConcurrentQueue<int>();
            private ConcurrentQueue<int> queueUnsend = new ConcurrentQueue<int>();
            private ConcurrentQueue<int> queueConfirm = new ConcurrentQueue<int>();
            private ConcurrentQueue<int> queueDeal = new ConcurrentQueue<int>();
    
            public void addSendCount(int iCount) {
    
                queueSend.Enqueue(iCount);
            }
    
            public void addUnsendCount(int iCount) {
    
                queueUnsend.Enqueue(iCount);
            }
    
            public void addConfirmCount(int iCount) {
    
                queueConfirm.Enqueue(iCount);
            }
    
            public void addDealCount(int iCount) {
    
                queueDeal.Enqueue(iCount);
            }
    
            public int getSendCount() { 
            
                int iSingle = 0;
                int iTotal = 0;
                while (queueSend.TryDequeue(out iSingle)) {
    
                    iTotal += iSingle;
                }
    
                return iTotal;
            }
    
            public int getUnsendCount() {
    
                int iSingle = 0;
                int iTotal = 0;
                while (queueUnsend.TryDequeue(out iSingle)) {
    
                    iTotal += iSingle;
                }
    
                return iTotal;
            }
    
            public int getConfirmCount() {
    
                int iSingle = 0;
                int iTotal = 0;
                while (queueConfirm.TryDequeue(out iSingle)) {
    
                    iTotal += iSingle;
                }
    
                return iTotal;
            }
    
            public int getDealCount() {
    
                int iSingle = 0;
                int iTotal = 0;
                while (queueDeal.TryDequeue(out iSingle)) {
    
                    iTotal += iSingle;
                }
    
                return iTotal;
            }        
    
            public PlotViewModel() {            
    
                SimplePlotModel = new PlotModel();            
    
                //创建于建立初始化数据节点
                var lineSend = new LineSeries() { Title = "报送" };
                lineSend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
                SimplePlotModel.Series.Add(lineSend);
    
                var lineUnsend = new LineSeries() { Title = "待报送" };
                lineUnsend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
                SimplePlotModel.Series.Add(lineUnsend);
    
                var lineConfirm = new LineSeries() { Title = "确认" };
                lineConfirm.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
                SimplePlotModel.Series.Add(lineConfirm);
    
                var lineDeal = new LineSeries() { Title = "成交" };
                lineDeal.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 0));
                SimplePlotModel.Series.Add(lineDeal);            
    
                //定义y轴
                LinearAxis leftAxis = new LinearAxis() {
    
                    Position = AxisPosition.Left,
                    Minimum = 0,
                    Maximum = 100,
                    Title = "笔数",//显示标题内容
                    TitlePosition = 0,//显示标题位置
                    MajorGridlineStyle = LineStyle.Solid,
                    MinorGridlineStyle = LineStyle.None,
                };
    
                //定义x轴 报盘监控界面x轴统一为时间
                DateTimeAxis bottomAxis = new DateTimeAxis() {
    
                    Position = AxisPosition.Bottom,
                    StringFormat = "hh:mm:ss",
                    Minimum = DateTimeAxis.ToDouble(DateTime.Now),
                    Maximum = DateTimeAxis.ToDouble(DateTime.Now.AddMinutes(1)),
                    Title = "时间",
                    TitlePosition = 0,
                    IntervalLength = 60,
                    MinorIntervalType = DateTimeIntervalType.Seconds,
                    IntervalType = DateTimeIntervalType.Seconds,
                    MajorGridlineStyle = LineStyle.Solid,
                    MinorGridlineStyle = LineStyle.None,
                };
    
                SimplePlotModel.Axes.Add(leftAxis);
                SimplePlotModel.Axes.Add(bottomAxis);
    
                bool bToMove = false;
                Task.Factory.StartNew(() => {
    
                    while (true) {
                            
                        lineSend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getSendCount()));
                        lineUnsend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getUnsendCount()));
                        lineConfirm.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getConfirmCount()));
                        lineDeal.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), getDealCount()));
    
                        if (!bToMove) {
    
                            //当前时间减去起始时间达到30秒后开始左移时间轴
                            TimeSpan timeSpan = DateTime.Now - DateTimeAxis.ToDateTime(bottomAxis.Minimum);
                            if (timeSpan.TotalSeconds >= 30) {
    
                                bToMove = true;
                            }
                        } else { 
                        
                            //左移时间轴,跨度维持在60秒
                            bottomAxis.Minimum = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(-30));
                            bottomAxis.Maximum = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(30));
    
                            //删除历史节点,防止DataPoint过多影响效率,也防止出现内存泄漏                        
                            lineSend.Points.RemoveAt(0);
                            lineConfirm.Points.RemoveAt(0);
                            lineUnsend.Points.RemoveAt(0);
                            lineDeal.Points.RemoveAt(0);
                        }
                               
                        //根据报单笔数判断是否需要更新y轴刻度                    
                        
                        //首先找出四条统计线中当前最大的节点
                        double iMax = lineSend.MaxY;
                        if (iMax < lineConfirm.MaxY) {
                            iMax = lineConfirm.MaxY;
                        }
                        if (iMax < lineUnsend.MaxY) {
                            iMax = lineUnsend.MaxY;
                        }
                        if (iMax < lineDeal.MaxY) {
                            iMax = lineDeal.MaxY;
                        }
                        
                        //如果当前的y轴最大刻度小于数据集中的最大值,放大
                        leftAxis.Maximum = iMax + (100 - iMax % 100);
                        leftAxis.IntervalLength = leftAxis.Maximum / 5;
    
                        //每秒刷新一次视图                                       
                        SimplePlotModel.InvalidatePlot(true);
                        Thread.Sleep(1000);
                    }
                });
            }
        }
    }

    从上述代码可以看出,功能主要为实现了Y轴为笔数统计,而X轴为时间轴且当线画到了中间时开始左移时间轴。统计的间隔为1秒。

    如果要导出DLL封装给其他程序使用的话,在项目属性中将输出类型调整为类库即可。这时可能会有例如不存在InitializeComponent()之类的报错,将App.xmal属性中的生成操作修改成无即可顺利导出。

    自己写了个小DEMO调用该DLL,不停往里添加数据,效果图如下:

    时间轴会左移,而Y轴的笔数统计也会根据当前DataPoint集合中的最大值进行相应的调整。

  • 相关阅读:
    Windows上使用“LogView”打开大文件
    windows CMD命令查看局域网内所有主机名及IP
    解决Sqlserver 2008 R2在创建登录名出错"此版本的 Microsoft Windows 不支持 MUST_CHANGE 选项。 (Microsoft SQL Server,错误: 15195)"
    解决 ASP.NET 编辑错误"CS0006: 未能找到元数据文件C:WINDOWSassemblyGAC_32System.EnterpriseServices2.0.0.0__b03f5f7f11d50a3aSystem.EnterpriseServices.dll"
    ASP 未结束的字符串常量
    Godaddy ssl续费更新问题总结
    [转]How to query posts filtered by custom field values
    SqlServer 在查询结果中如何过滤掉重复数据
    [UE4]C++的const类成员函数
    [UE4]C++三种继承方式
  • 原文地址:https://www.cnblogs.com/xuzichao/p/9638658.html
Copyright © 2011-2022 走看看