对于我们的项目之一,我们必须从外部源检索数据,并将它们存储在数据库中。一个来自客户机的请求,是看在图形中检索到的数据。此外,他希望看到一个实时更新该图中检索数据的每一秒。
经过一番搜索,我发现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上。欢迎到餐桌,下载,...