For one of our projects we had to retrieve data from an external source and store them in a database. One of the requests from the client was to see the retrieved data in a graph. Furthermore, he wanted to see a live update every second of the retrieved data in that graph.
After some searching I found the OxyPlot library. This library can be used in WPF, Silverlight, Windows Forms and even in Windows Store apps. The are now working on a alpha release for Mono.
Although the package is already downloaded more then 10 000 times there are not so many blog posts to find about implementing the library.
Open op Visual Studio and start with creating a new WPF project. Choose a name and a location and hit ‘OK’.
After the project is created open up the Package Manager Console and type following commands at the prompt and hit enter (after every command):
- Install-Package Oxyplot.Core
- Install-Package Oxyplot.Wpf
You can of course use the Package Manager UI by right clicking References and choose Manage Nuget Packages and them that way.
Create the ViewModel
We’ll use the MVVM model (partial) to render the Graph on the screen. First step is to create the ViewModel for our MainWindow.xaml. Right click on the project and add a folder ViewModels. Right click the folder and add a class MainWindowModel.cs.
To use the MVVM model we need to make the class public and inherit the INotifyPropertyChanged interface.
NOTE: The OxyPlot WPF package doesn’t completely support the MVVM model. You can’t use only the OnPropertyChanged method to update the graph. You’ll still need the manually refresh the graph from the code behind class of the XAML page. I saw a post in the discussions on codeplex that they are looking to add complete MVVM model but are short of time at the moment. Maybe the next version will support it. To be complete I add the necessary steps to implement the MVVM model in this post.
After inheriting the interface we have to implement the PropertyChangedEventHandler event as shown below.
Now we can add some public properties that we’ll bind to in the XAML page. First create a PlotModel that’s part of the OxyPlot library. In the constructor we’ll initiate the PlotModel parameter. The setter will call the OnPropertyChanged method that will notify the view there is a change on the object that may have to be rendered.
Adding the graph to the page
We can now add the graph to the MainWindow.xaml page. Before we can add the graph we’ll need to add the namespace for the OxyPlot library (line 4). We’ll bind the PlotModel parameter to the PlotModel we’ve created in the view model.
Binding the model to the view
Last part for our setup is to bind the view model to the view. Because we don’t use any MVVM frameworks we’ll have to manually bind the model in the code behind of the MainWindow.xaml page.
Add a private property of our view model and initiate this property in the constructor. Point the DataContext to this property and we’re done.
If we hit F5 to run the application we’ll see an empty window opening. We have of course add some data to the PlotModel before the graph will be rendered.
Set up the Graph – PlotModel
Back to our view model to set up our view model. First start will be adding the axes for the graph so OxyPlot knows where to plot the points.
Create a new method SetUpModel in our view model class. In here we’ll define the legend of the graph (position, border, sire, …) and add 2 axis. A DateTimeAxis for our X-axis (we want to plot points in a line of time) and a ValuaAxis for our Y-axis.
The set up for the legend of the graph is self explanatory. At line 10 you’ll see the creation of the DateTimeAxis. We’ll choose the position of the axis, give it a name (Date) and a string format pattern how the date has to be displayed. Next to that we’ll add some parameters to show major and minor gridlines and the interval size. After creation we’ll add the axis to the PlotModel.
The ValueAxis is similar initiated, only did we position the axis on the left side and tell the Axis to start at the value 0.
If we add a call to this method in the constructor of our view model and hit F5 again we’ll now see window opening that contains an empty graph.
Now we have our graph and are ready to add some graph lines to the PlotModel. In this post we’ll add four different lines that will be plot.
To avoid complexity I added a class Data.cs where the data is hardcoded. In a real life application you’ll be implementing a database call or a service invocation or a web API request, …
The data I’ll receive will be a List<T> of a new simple class I’ve created: Measurement. This class has 3 public parameters: a DetectorId (long), a Value (int) and a DateTime (DateTime). I’ve added 4 different detectors that each have 10 measurements of a random value between 1 and 30.
Add the data to the PlotModel
In the view model we’ll create a new method LoadData where we’ll get the data and add some code to plot all point on the graph.
After fetching the data I’ll use the LINQ GroupBy expression to create a IEnumerable with as key the DetectorId and a collection of Value and Datetime as values. (line 5)
This will allow us to loop over the result and then add a LineSerie per Detector. In the setup of the LineSerie we’ll set some properties like colors and line thickness and the title. (line 9 till 18).
After we’ve created the LineSerie we can add the points to the LineSerie. We make use of the DateTimeAxis builtin funtion to convert a date to a double and add the value (line 20).
After we added the points to the LineSerie we’ll still have to add the LineSerie to the PlotModel. (line 21).
And that’s it. Hit F5 and you’ll see that are graph will be rendered and that per detector a line will appear plotting the random values between 1 and 30.
Updating the graph real-time
For the second question of our client we had to add real-time updates. For this demo I’ll add an new measurement to the graph every second. The OxyPlot library will make sure our graph will move along every time we’ll add a new measurement.
I’ve added a static method in the Data.cs class that will return a random value a second after the DateTime parameter that will be send along. In the view model I’ve created a private parameter DateTime parameter (lasUpdate) that will hold the moment we last updated the graph.
Update the view model
We’ll add a new method to the view model UpdateModel. This time we’ll make it public so it can be called from the view as shown in the next chapter.
In the UpdateModel method we’ll fetch the data from out Data.cs class and perform the same grouping action as in the LoadData method.
This will allow us to fetch the data per detector. After retrieving the correct LineSerie we can add the points like we did in the LoadData method.
Update from the view
We don’t want the update of the model to initiate before the previous is completely rendered to avoid an exception that we are modifying a list while rendering the graph. Therefor we make use of a CompositionTarget.Rendering event. This event will be fired every time the view is done rendering.
In the constructor of the MainWindow.xaml.cs class we’ll attach an eventhandler on the Rendering event.
In the event handler we’ll call the update method from the view model. After that call we’ll have to tell the PlotModel that there was an update and that he has to refresh the graph (line 14). (This is where OxyPlot drifts away from the MVVM model).
Avoid to many updates
The rendering event is ideal to avoid exceptions but you’ll see to many updates per second to keep the graph readable. We can solve this to add a StopWatch in the code behind and only trigger the update when the last update is at least a second ago.
After adding the StopWatch we’ll see that every (for this demo 5 seconds) a new point is added to the graph.
As you can see, with OxyPlot you don’t need to write to many code to create real time updating charts.
OxyPlot has many more options and graph styles, they have provided a demo page where a variety of graphs are rendered in Silverlight. You can copy the source code with the keyboard combination Ctrl+Alt+C. If you want to copy the properties use Ctrl+Alt+R.
The source code of this post can you find on Github. Feel free to fork, download, …