使用OxyPlot在WPF中创建图表

对于我们的项目之一,我们必须从外部源检索数据,并将它们存储在数据库中。一个来自客户机的请求,是看在图形中检索到的数据。此外,他希望看到一个实时更新该图中检索数据的每一秒。 
经过一番搜索,我发现OxyPlot库。这个库可以在WPF,Silverlight中,Windows窗体,甚至在Windows Store应用程序中使用。在现在正在研究一个alpha版本为单声道。 
虽然包已经下载超过10万次不会有这么多的博客文章查找有关实施库。虽然包已经下载超过10万次不会有这么多的博客文章查找有关实施库。

Using Nuget

包括OxyPlot在你的应用程序的最简单方法是使用NuGet包管理器在Visual Studio。 运行 Visual Studio,并开始创建一个新的WPF项目。选择一个名称和位置,并点击“OK”。

使用OxyPlot在WPF中创建图表_第1张图片

创建项目后,打开管理器控制台,在提示符下键入以下命令并打(每一个命令之后),请输入: 
安装封装Oxyplot.Core 
安装封装Oxyplot.Wpf


使用OxyPlot在WPF中创建图表_第2张图片

当然你也可以使用软件包管理器用户界面右击引用,然后选择管理NuGet软件包和他们的方式。

Create the ViewModel

我们将使用MVVM模式(部分)呈现在屏幕上的图表。第一步是创建视图模型为我们MainWindow.xaml。用鼠标右键单击该项目,并添加一个文件夹的ViewModels。用鼠标右键单击该文件夹并添加一个类MainWindowModel.cs。

使用OxyPlot在WPF中创建图表_第3张图片

要使用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 ));
}
}
}
view raw gistfile1.cs hosted with ❤ by  GitHub

现在,我们可以补充一点,我们会绑定到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 ));
}
}
}
view raw gistfile1.cs hosted with ❤ by  GitHub

Adding the graph to the page

现在,我们可以将图形添加到MainWindow.xaml页面。之前,我们可以添加图形,我们将需要添加的命名空间OxyPlot库(4号线)。我们将在PlotModel参数绑定到我们在视图模型创建的PlotModel。

12345678910
           
           
           
           
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" >
x:Name= "Plot1" Title= "A Graph" Model= "{Binding PlotModel}" Margin= "10" Grid.Row= "1" >
view raw gistfile1.xml hosted with ❤ by  GitHub

Binding the model to the view

最后一部分,我们的设置是视图模型绑定到视图。因为我们不使用任何的MVVM框架,我们将不得不手动绑定模型在MainWindow.xaml页面背后的代码。 


加入我们的视图模型的私有财产,并启动该属性在构造函数中。点的DataContext这个属性,我们就大功告成了。

123456789101112131415161718192021
           
           
           
           
using System.Windows ;
 
namespace OxyPlotDemo
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
private ViewModels . MainWindowModel viewModel ;
 
public MainWindow ()
{
viewModel = new ViewModels . MainWindowModel ();
DataContext = viewModel
 
 
InitializeComponent ();
}
}
}
view raw gistfile1.cs hosted with ❤ by  GitHub

如果我们按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 );
}
view raw gistfile1.cs hosted with ❤ by  GitHub

该设置为图形的传说是自我解释。在第10行,你会看到创建的DateTimeAxis的。我们选择的轴的位置,给它一个名称(日期)和字符串格式样式如何日期有显示。下一步,我们将添加一些参数来显示主要和次要网格线和间隔大小。创建后,我们将添加轴到PlotModel。 
该ValueAxis类似于发起的,只是我们没有定位轴左侧,并告诉轴心国开始在0值。 
如果我们添加一个调用此方法,我们认为模型的构造,并再次按F5键,我们会看到窗口打开,其中包含一个空图。

使用OxyPlot在WPF中创建图表_第4张图片

The data

现在,我们有我们的图形,并准备了一些图线添加到PlotModel。在这篇文章中,我们将添加四个不同的线路,这将是情节。 
为了避免复杂性我添加了一个类Data.cs其中的数据是硬编码的。在现实生活中应用程序,您将实现一个数据库调用或一个服务调用或Web API请求,... 
我将接收到的数据将是我创建了一个新的简单类的列表:测量。这个类有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 );
}
}
view raw gistfile1.cs hosted with ❤ by  GitHub

获取数据后,我将使用LINQ GROUPBY表达式创建一个IEnumerable的与关键的DetectorId和值和DateTime的集合作为值。 (第5行) 
这将允许我们遍历的结果,然后添加每一个探测器LineSerie。在LineSerie的设置,我们将设置喜欢的颜色和线条粗细和标题的一些性质。 (9号线至18)。 
之后我们建立了LineSerie我们可以添加指向LineSerie。我们利用DateTimeAxis将内置类函数来将日期转换为一张双人床和增加值(第20行)。 
之后,我们添加了指向LineSerie我们仍然不得不在LineSerie添加到PlotModel。 (第21行)。 
就是这样。按F5键,你会看到那些图形会被渲染,并且每探测器的线会出现绘制1到30之间的随机值。

使用OxyPlot在WPF中创建图表_第5张图片

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 ;
}
view raw gistfile1.cs hosted with ❤ by  GitHub

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 );
}
view raw gistfile1.cs hosted with ❤ by  GitHub

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 ;
}
}
view raw gistfile1.cs hosted with ❤ by  GitHub

加入秒表后,我们会看到每一个(这个演示5秒),一个新的点添加到图形。

使用OxyPlot在WPF中创建图表_第6张图片

Source code

正如你所看到的,与OxyPlot你不需要写很多的代码来创建实时更新图表。 
OxyPlot有更多的选项和图形样式,他们提供了一个演示页面,各种图形在Silverlight中呈现。您可以复制源代码与键盘组合键Ctrl+ Alt键+ C。如果您要复制的属性使用CTRL + ALT + R。 
这篇文章的源代码可以为你找到Github上。欢迎到餐桌,下载,...


你可能感兴趣的:(oxyplot,WPF)