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 = ;
int iTotal = ;
while (queueSend.TryDequeue(out iSingle)) { iTotal += iSingle;
} return iTotal;
} public int getUnsendCount() { int iSingle = ;
int iTotal = ;
while (queueUnsend.TryDequeue(out iSingle)) { iTotal += iSingle;
} return iTotal;
} public int getConfirmCount() { int iSingle = ;
int iTotal = ;
while (queueConfirm.TryDequeue(out iSingle)) { iTotal += iSingle;
} return iTotal;
} public int getDealCount() { int iSingle = ;
int iTotal = ;
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), ));
SimplePlotModel.Series.Add(lineSend); var lineUnsend = new LineSeries() { Title = "待报送" };
lineUnsend.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), ));
SimplePlotModel.Series.Add(lineUnsend); var lineConfirm = new LineSeries() { Title = "确认" };
lineConfirm.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), ));
SimplePlotModel.Series.Add(lineConfirm); var lineDeal = new LineSeries() { Title = "成交" };
lineDeal.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), ));
SimplePlotModel.Series.Add(lineDeal); //定义y轴
LinearAxis leftAxis = new LinearAxis() { Position = AxisPosition.Left,
Minimum = ,
Maximum = ,
Title = "笔数",//显示标题内容
TitlePosition = ,//显示标题位置
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()),
Title = "时间",
TitlePosition = ,
IntervalLength = ,
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 >= ) { bToMove = true;
}
} else { //左移时间轴,跨度维持在60秒
bottomAxis.Minimum = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(-));
bottomAxis.Maximum = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds()); //删除历史节点,防止DataPoint过多影响效率,也防止出现内存泄漏
lineSend.Points.RemoveAt();
lineConfirm.Points.RemoveAt();
lineUnsend.Points.RemoveAt();
lineDeal.Points.RemoveAt();
} //根据报单笔数判断是否需要更新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 + ( - iMax % );
leftAxis.IntervalLength = leftAxis.Maximum / ; //每秒刷新一次视图
SimplePlotModel.InvalidatePlot(true);
Thread.Sleep();
}
});
}
}
}
从上述代码可以看出,功能主要为实现了Y轴为笔数统计,而X轴为时间轴且当线画到了中间时开始左移时间轴。统计的间隔为1秒。
如果要导出DLL封装给其他程序使用的话,在项目属性中将输出类型调整为类库即可。这时可能会有例如不存在InitializeComponent()之类的报错,将App.xmal属性中的生成操作修改成无即可顺利导出。
自己写了个小DEMO调用该DLL,不停往里添加数据,效果图如下:
时间轴会左移,而Y轴的笔数统计也会根据当前DataPoint集合中的最大值进行相应的调整。
C#使用Oxyplot绘制监控界面的更多相关文章
- 【Java框架型项目从入门到装逼】第八节 - 用EasyUI绘制主界面
1.引入资源包 在上一节中,我们把基本的框架都搭好了,用了Spring,SPringMVC.这一节,我们先来画页面,前端框架采用EasyUI来实现. easyui是一种基于jQuery的用户界面插件集 ...
- SpringBoot2.0 基础案例(07):集成Druid连接池,配置监控界面
一.Druid连接池 1.druid简介 Druid连接池是阿里巴巴开源的数据库连接池项目.Druid连接池为监控而生,内置强大的监控功能,监控特性不影响性能.功能强大,能防SQL注入,内置Login ...
- 六:SpringBoot-集成Druid连接池,配置监控界面
SpringBoot-集成Druid连接池,配置监控界面 1.Druid连接池 1.1 Druid特点 2.SpringBoot整合Druid 2.1 引入核心依赖 2.2 数据源配置文件 2.3 核 ...
- kafka-eagle监控界面搭建
kafka-eagle监控界面搭建 一.背景 二 .mac上安装kafka-eagle 1.安装JDK 2.安装eagle 1.下载eagle 2.解压并配置环境变量 3.启用kafka的JMX 4. ...
- AspNet Core下利用 app-metrics+Grafana + InfluxDB实现高大上的性能监控界面
在日常系统工作中,我们为了洞察系统的问题和运作情况通常会记录日志的方式来进行分析,但是在很多情况下都是被动的在出问题后才会去查日志.在很多时候,我们可能更需要相对实时的了解整个系统或者某一时段的运行的 ...
- zabbix结合grafana打造炫酷监控界面
一.grafana介绍 grafana是一个开源的数据展示工具, 是一个开箱即用的可视化工具,具有功能齐全的度量仪表盘和图形编辑器,有灵活丰富的图形化选项,可以混合多种风格,支持多个数据源特点. za ...
- MFC 监控界面上所有文本框值的变化
//控件消息,菜单,按钮等 BOOL CXXDlg::OnCommand(WPARAM wParam, LPARAM lParam) { // TODO: 在此添加专用代码和/或调用基类 int wm ...
- druid 配置监控界面和开启spring支持
1.配置监控页面 <!-- JNDI方式配置数据源 --> <!-- <bean id="dataSource" class="org.sprin ...
- 利用微信小程序实现web监控界面
1.实现思路 利用小程序去调用公司zabbix的接口获取网站监控数据并展示出来. 2.准备阶段 1.小程序公众号 2.企业号 3.zabbix接口 3.实现过程
随机推荐
- Day 07--最终修改(三)
2.明天着重学一下逻辑层的语法,以及界面层的数据绑定,与队友交流进度 3.今天修改也终于完成,除了搞c++以外的全部身心都放在这个东西身上也觉得它有点难搞,说明计算机不是吃素的.甚至在使用xml语法的 ...
- Springboot整合html 报java.lang.IllegalArgumentException: Root element name cannot be null
解决: <!DOCTYPE><html> 改为 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitiona ...
- shiro实现session共享(本文转自店蛋蛋)
session共享:在多应用系统中,如果使用了负载均衡,用户的请求会被分发到不同的应用中,A应用中的session数据在B应用中是获取不到的,就会带来共享的问题. 假设:用户第一次访问,连接的A服务器 ...
- springBoot框架分布式部署定时任务重复执行之解决方案
问题描述: 在集群模式部署服务端时,会出现所有的定时任务在各自的节点处均会执行一遍,这显然不符合实际的开发场景,针对这种问题,本文给出一种springboot集成shedlock的解决方案 第一步:引 ...
- unity编辑器扩展_02(分别在Hierarchy,Project中创建一个选项)
在Hierarchy面板创建选项的代码: [MenuItem("GameObject/Test",false,1)] static void Test1() { ...
- 简单使用AspectJ
AspectJ是一个AOP框架,由于SpringAOP的配置过于繁琐,因此使用了AspectJ依赖注解开发 1.Aspecj依赖坐标,此处省略了Spring相关依赖 <dependency> ...
- xib上的控件属性为什么要使用weak
常规中,从xib拖出一个控件时,系统会自动生成一段代码,如下: 从这个图片中,可以看到控件的属性都是用的weak,这是为什么呢? 首先,如果把weak修改成strong其实也是可以的,但是会出现一个问 ...
- 求树的重心 DFS
树的重心 何谓重心 树的重心:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 树的重心可以通过简单的两次搜索求出,第一遍搜索求出每个结 ...
- CodeForces-714B-Filya and Homework+思路
Filya and Homework 题意: 给定一串数字,任选一个数a,把若干个数加上a,把若干个数减去a,能否使得数列全部相同: 思路: 我开始就想找出平均数,以为只有和偶数的可以,结果wa在 1 ...
- 牛客2018多校第五场E-room 最小费用最大流
题意:有n个寝室,每个寝室4个人,现在在搞搬寝室的活动,告诉你每个寝室之前的人员名单,和之后的人员名单,问最少需要几个人要搬寝室. 思路: 转化为最小费用最大流解决的二分图问题,对每个去年的宿舍,向每 ...