使用OxyPlot在WPF中创建图表
- Using Nuget
- 包括OxyPlot在你的应用程序的最简单方法是使用NuGet包管理器在Visual Studio 运行 Visual Studio并开始创建一个新的WPF项目选择一个名称和位置并点击OK
- Create the ViewModel
- Adding the graph to the page
- Binding the model to the view
- Set up the Graph PlotModel
- The data
- Add the data to the PlotModel
- 在视图模型中我们将创建一个新的方法其中的loaddata我们会得到的数据并添加一些代码来绘制所有点在图形上
- Updating the graph real-time
- Update the view model
- Update from the view
- Avoid to many updates
- 渲染事件是理想的以避免异常但你会看到每秒许多更新以保持图形的可读性我们可以解决这个添加一个秒表在后面的代码只触发更新当最后更新至少一秒钟前
- Source code
经过一番搜索,我发现OxyPlot库。这个库可以在WPF,Silverlight中,Windows窗体,甚至在Windows Store应用程序中使用。在现在正在研究一个alpha版本为单声道。
虽然包已经下载超过10万次不会有这么多的博客文章查找有关实施库。虽然包已经下载超过10万次不会有这么多的博客文章查找有关实施库。
Using Nuget
包括OxyPlot在你的应用程序的最简单方法是使用NuGet包管理器在Visual Studio。 运行 Visual Studio,并开始创建一个新的WPF项目。选择一个名称和位置,并点击“OK”。
创建项目后,打开管理器控制台,在提示符下键入以下命令并打(每一个命令之后),请输入:
安装封装Oxyplot.Core
安装封装Oxyplot.Wpf
当然你也可以使用软件包管理器用户界面右击引用,然后选择管理NuGet软件包和他们的方式。
Create the ViewModel
我们将使用MVVM模式(部分)呈现在屏幕上的图表。第一步是创建视图模型为我们MainWindow.xaml。用鼠标右键单击该项目,并添加一个文件夹的ViewModels。用鼠标右键单击该文件夹并添加一个类MainWindowModel.cs。
要使用MVVM模式,我们需要使公共类和继承INotifyPropertyChanged接口。
继承接口后,我们必须实现PropertyChangedEventHandler事件,如下图所示。
1234567891011121314151617 |
using System.ComponentModel; using OxyPlotDemo.Annotations;
namespace OxyPlotDemo.ViewModels
{
public class MainWindowModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
|
现在,我们可以补充一点,我们会绑定到XAML页面的一些公共属性。首先创建一个PlotModel那是OxyPlot库的一部分。在构造函数中,我们将启动PlotModel参数。设定器将调用,将通知该视图中有物体上的变化,可能有要呈现的OnPropertyChanged方法。
123456789101112131415161718192021222324252627282930 |
using System.ComponentModel; using OxyPlot;
using OxyPlotDemo.Annotations;
namespace OxyPlotDemo.ViewModels
{
public class MainWindowModel: INotifyPropertyChanged
{
private PlotModel plotModel;
public PlotModel PlotModel
{
get { return plotModel; }
set { plotModel = value; OnPropertyChanged("PlotModel"); }
}
public MainWindowModel()
{
PlotModel = new PlotModel();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
|
Adding the graph to the page
现在,我们可以将图形添加到MainWindow.xaml页面。之前,我们可以添加图形,我们将需要添加的命名空间OxyPlot库(4号线)。我们将在PlotModel参数绑定到我们在视图模型创建的PlotModel。
12345678910 |
<Window x:Class="OxyPlotDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:oxy="http://oxyplot.codeplex.com"
Title="MainWindow" Height="350" Width="525">
<Grid>
<oxy:Plot x:Name="Plot1" Title="A Graph" Model="{Binding PlotModel}" Margin="10" Grid.Row="1">
</oxy:Plot>
</Grid>
</Window>
|
Binding the model to the view
最后一部分,我们的设置是视图模型绑定到视图。因为我们不使用任何的MVVM框架,我们将不得不手动绑定模型在MainWindow.xaml页面背后的代码。
加入我们的视图模型的私有财产,并启动该属性在构造函数中。点的DataContext这个属性,我们就大功告成了。
123456789101112131415161718192021 |
using System.Windows; namespace OxyPlotDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModels.MainWindowModel viewModel;
public MainWindow()
{
viewModel = new ViewModels.MainWindowModel();
DataContext = viewModel
InitializeComponent();
}
}
}
|
如果我们按F5键运行该应用程序,我们会看到一个空窗口打开。我们有一些数据的过程中添加到PlotModel图将呈现前。
Set up the Graph – PlotModel
回到我们的视图模型来建立我们的视图模型。第一次启动将增加轴的图,从而OxyPlot知道在哪里可以绘制点。
在我们的视图模型类中创建一个新的方法SetUpModel。在这里我们将定义图表的图例(位置,边界,陛下,...),并添加2轴。则DateTimeAxis我们的X轴(我们要在一条线上的时间绘制点)和ValuaAxis我们的Y轴。
1234567891011121314 |
private void SetUpModel() {
PlotModel.LegendTitle = "Legend";
PlotModel.LegendOrientation = LegendOrientation.Horizontal;
PlotModel.LegendPlacement = LegendPlacement.Outside;
PlotModel.LegendPosition = LegendPosition.TopRight;
PlotModel.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);
PlotModel.LegendBorder = OxyColors.Black;
var dateAxis = new DateTimeAxis(AxisPosition.Bottom, "Date", "dd/MM/yy HH:mm") { MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, IntervalLength = 80 };
PlotModel.Axes.Add(dateAxis);
var valueAxis = new LinearAxis(AxisPosition.Left, 0) { MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, Title = "Value" };
PlotModel.Axes.Add(valueAxis);
}
|
该设置为图形的传说是自我解释。在第10行,你会看到创建的DateTimeAxis的。我们选择的轴的位置,给它一个名称(日期)和字符串格式样式如何日期有显示。下一步,我们将添加一些参数来显示主要和次要网格线和间隔大小。创建后,我们将添加轴到PlotModel。
该ValueAxis类似于发起的,只是我们没有定位轴左侧,并告诉轴心国开始在0值。
如果我们添加一个调用此方法,我们认为模型的构造,并再次按F5键,我们会看到窗口打开,其中包含一个空图。
The data
现在,我们有我们的图形,并准备了一些图线添加到PlotModel。在这篇文章中,我们将添加四个不同的线路,这将是情节。
为了避免复杂性我添加了一个类Data.cs其中的数据是硬编码的。在现实生活中应用程序,您将实现一个数据库调用或一个服务调用或Web API请求,...
我将接收到的数据将是我创建了一个新的简单类的列表<T>:测量。这个类有3个公共参数:一个DetectorId(长),数据(int)和日期时间(DateTime的)。我已经添加了4种不同的探测器,每个有10个测量值的1到30之间的随机值。
Add the data to the PlotModel
在视图模型中,我们将创建一个新的方法,其中的loaddata我们会得到的数据,并添加一些代码来绘制所有点在图形上。
1234567891011121314151617181920212223 |
private void LoadData() {
List<Measurement> measurements = Data.GetData();
var dataPerDetector = measurements.GroupBy(m => m.DetectorId).ToList();
foreach (var data in dataPerDetector)
{
var lineSerie = new LineSeries
{
StrokeThickness = 2,
MarkerSize = 3,
MarkerStroke = colors[data.Key],
MarkerType = markerTypes[data.Key],
CanTrackerInterpolatePoints = false,
Title = string.Format("Detector {0}",data.Key),
Smooth = false,
};
data.ToList().ForEach(d=>lineSerie.Points.Add(new DataPoint(DateTimeAxis.ToDouble(d.DateTime),d.Value)));
PlotModel.Series.Add(lineSerie);
}
}
|
获取数据后,我将使用LINQ GROUPBY表达式创建一个IEnumerable的与关键的DetectorId和值和DateTime的集合作为值。 (第5行)
这将允许我们遍历的结果,然后添加每一个探测器LineSerie。在LineSerie的设置,我们将设置喜欢的颜色和线条粗细和标题的一些性质。 (9号线至18)。
之后我们建立了LineSerie我们可以添加指向LineSerie。我们利用DateTimeAxis将内置类函数来将日期转换为一张双人床和增加值(第20行)。
之后,我们添加了指向LineSerie我们仍然不得不在LineSerie添加到PlotModel。 (第21行)。
就是这样。按F5键,你会看到那些图形会被渲染,并且每探测器的线会出现绘制1到30之间的随机值。
Updating the graph real-time
对于我们的客户端的第二个问题,我们必须添加实时更新。对于这个演示中,我将添加一个新的测量到图形的每一秒。该OxyPlot库将确保我们的图表将每一起我们将添加一个新的测量时间移动。
我添加了一个静态方法在Data.cs类,将返回一个随机值,将沿发送的DateTime参数后一秒钟。在视图模型我已经创建了一个私有参数的DateTime参数(lasUpdate),将持有我们最后更新图表的时刻。
Update the view model
我们将添加一个新的方法到视图模型的UpdateModel。这一次,我们将它公开,因此它可以从视图中被调用,如图中的下一个篇章。
在的UpdateModel方法,我们会从出Data.cs类获取数据,并执行相同的分组行动,在方法的loaddata。
这将使我们能够获取每个探测器的数据。获取正确的LineSerie后就像我们在方法的loaddata做我们可以添加点。
12345678910111213141516 |
public void UpdateModel() {
List<Measurement> measurements = Data.GetUpdateData(lastUpdate);
var dataPerDetector = measurements.GroupBy(m => m.DetectorId).OrderBy(m => m.Key).ToList();
foreach (var data in dataPerDetector)
{
var lineSerie = PlotModel.Series[data.Key] as LineSeries;
if (lineSerie != null)
{
data.ToList()
.ForEach(d => lineSerie.Points.Add(new DataPoint(DateTimeAxis.ToDouble(d.DateTime), d.Value)));
}
}
lastUpdate = DateTime.Now;
}
|
Update from the view
我们不希望该模型的更新开始之前,前一个是完全呈现,以避免我们正在修改,同时渲染图形列表的例外。为此,我们利用一个CompositionTarget.Rendering事件。此事件会在每次视图渲染完成时触发。
在的MainWindow.xaml.cs类的构造函数中,我们将附加一个事件处理程序的渲染事件。
在事件处理程序中,我们会从视图模型调用update方法。在这个调用之后,我们将要告诉PlotModel,有一个更新,他必须刷新图形(第14行)。 (这是OxyPlot偏离了MVVM模式)。
123456789101112131415 |
public MainWindow() {
viewModel = new ViewModels.MainWindowModel();
DataContext = viewModel;
CompositionTarget.Rendering += CompositionTargetRendering;
InitializeComponent();
}
private void CompositionTargetRendering(object sender, EventArgs e)
{
viewModel.UpdateModel();
Plot1.RefreshPlot(true);
}
|
Avoid to many updates
渲染事件是理想的,以避免异常,但你会看到每秒许多更新,以保持图形的可读性。我们可以解决这个添加一个秒表在后面的代码,只触发更新当最后更新至少一秒钟前。
123456789101112131415161718192021222324 |
public MainWindow() {
viewModel = new ViewModels.MainWindowModel();
DataContext = viewModel;
CompositionTarget.Rendering += CompositionTargetRendering;
stopwatch.Start();
InitializeComponent();
}
private long frameCounter;
private System.Diagnostics.Stopwatch stopwatch = new Stopwatch();
private long lastUpdateMilliSeconds;
private void CompositionTargetRendering(object sender, EventArgs e)
{
if (stopwatch.ElapsedMilliseconds > lastUpdateMilliSeconds + 5000)
{
viewModel.UpdateModel();
Plot1.RefreshPlot(true);
lastUpdateMilliSeconds = stopwatch.ElapsedMilliseconds;
}
}
|
加入秒表后,我们会看到每一个(这个演示5秒),一个新的点添加到图形。
Source code
正如你所看到的,与OxyPlot你不需要写很多的代码来创建实时更新图表。
OxyPlot有更多的选项和图形样式,他们提供了一个演示页面,各种图形在Silverlight中呈现。您可以复制源代码与键盘组合键Ctrl+ Alt键+ C。如果您要复制的属性使用CTRL + ALT + R。
这篇文章的源代码可以为你找到Github上。欢迎到餐桌,下载,...
使用OxyPlot在WPF中创建图表的更多相关文章
- 在WPF中创建可换肤的用户界面
原文:在WPF中创建可换肤的用户界面 在WPF中创建可换肤的用户界面. ...
- 用OxyPlot在WPF中演示正演磁异常的变化规律
为了在展示实验成果时动态演示理论球体磁异常随其埋深.磁化倾角的变化规律,我用WPF写了一个小程序来作演示. MatLab计算磁异常数据 首先是计算理论球体磁异常数据,在Matlab中可以很方便地计算. ...
- WPF中的三维空间(1)
原文:WPF中的三维空间(1) WPF中可以创建三维几何图形,支持3D对象的应用,支持从3D Max等软件将3D文件obj导入设计中,但是目前还不支持将材质同时导入,这样需要在WPF中对3D对象重新设 ...
- Vue 爬坑之路(八)—— 使用 Echarts 创建图表
在后台管理系统中,图表是一个很普遍的元素.目前常用的图标插件有 charts, Echarts, highcharts.这次将介绍 Echarts 在 Vue 项目中的应用. 一.安装插件 使用 c ...
- WPF中DPI的问题
先搞清楚一下几个概念: DPI:dots per inch ,每英寸的点数.我们常说的鼠标DPI,是指鼠标移动一英寸的距离滑过的点数:打印DPI,每英寸的长度打印的点数:扫描DPI,每英寸扫描了多 ...
- WPF 自定义的图表(适用大量数据绘制)
原文:WPF 自定义的图表(适用大量数据绘制) 在WPF中绘制图表比较简单,有很多的第三方控件,但是在绘制大量数据的时候,就显得有些吃力,即便是自己用StreamGeometry画也达不到理想的效果, ...
- 在WPF中使用依赖注入的方式创建视图
在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...
- 转:WPF中ListBox的创建和多种绑定用法
先从最容易的开始演示ListBox控件的创建. Adding ListBox Items下面的代码是向ListBox控件中添加多项ListBoxItem集合.XAML代码如下:<ListBox ...
- WPF 中动态创建和删除控件
原文:WPF 中动态创建和删除控件 动态创建控件 1.容器控件.RegisterName("Name",要注册的控件) //注册控件 2.容器控件.FindName(" ...
随机推荐
- utmp, wtmp - 登 录 记 录(login records)
SYNOPSIS[总览] #include DESCRIPTION[描述] utmp 文 件 用 于 记 录 当 前 系 统 用 户 是 哪 些 人. 但 是 实 际 的 人 数 可 能 比 这 个 ...
- 笔记41 Spring Web Flow——Demo
订购披萨的应用整体比较比较复杂,现拿出其中一个简化版的流程:即用户访问首页,然后输入电话号(假定未注册)后跳转到注册页面,注册完成后跳转到配送区域检查页面,最后再跳转回首页.通过这个简单的Demo用来 ...
- ./vimrc代码解析全
""""""""""""""""&quo ...
- kali 修改默认语言
打开 /etc/default/locale将 LANG="en_US.UTF-8" 修改成 LANG="zh_CN.UTF-8"重启 reboot 配置方法 ...
- Centos7.5 安装sonarqube-7.1
下载sonarqube-7.1 wget -t 0 -c https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-7.1.z ...
- 解析Mybatis入门第一天
MyBatis是一个基于Java的持久层框架,内部对JDBC做了封装,使开发者只需要关注SQL语句,而不用关注JDBC的代码,使开发变得更加的简单. MyBatis通过XML或者注解的方式将要执行的各 ...
- Perl 运算符
Perl 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号,如: 3+2=5. Perl 语言内置了丰富的运算符,我们来看下常用的几种: 算术运算符 比较运算符 逻辑运算符 赋值运算符 位 ...
- JavaScript做个时间表 Date()
<span id="shiji"></span><script> window.setInterval("time()",5 ...
- 双目立体匹配经典算法之Semi-Global Matching(SGM)概述:代价聚合(Cost Aggregation)
由于代价计算步骤只考虑了局部的相关性,对噪声非常敏感,无法直接用来计算最优视差,所以SGM算法通过代价聚合步骤,使聚合后的代价值能够更准确的反应像素之间的相关性,如图1所示.聚合后的新的代价值保存 ...
- NX二次开发-算法篇-判断找到两个数组里不相同的对象
NX9+VS2012 #include <uf.h> #include <uf_curve.h> #include <uf_modl.h> #include < ...