pBook‎ > ‎Plot C++‎ > ‎

C# powerful lib

posted Sep 30, 2014, 12:41 AM by Javad Taghia   [ updated Sep 30, 2014, 12:41 AM ]

Creating graphs in WPF using OxyPlot

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.

Using Nuget

The easiest way to include OxyPlot in you application is by using the Nuget package manager in Visual Studio.

Open op Visual Studio and start with creating a new WPF project. Choose a name and a location and hit ‘OK’.

image

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

image

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.

image

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.

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 rawgistfile1.cs hosted with ❤ by GitHub

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.

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 rawgistfile1.cs hosted with ❤ by GitHub

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.

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>
view rawgistfile1.xml hosted with ❤ by GitHub

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.

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

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.

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 rawgistfile1.cs hosted with ❤ by GitHub

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.

image

The data

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.

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 rawgistfile1.cs hosted with ❤ by GitHub

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.

image

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.

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 rawgistfile1.cs hosted with ❤ by GitHub

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).

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 rawgistfile1.cs hosted with ❤ by GitHub

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.

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 rawgistfile1.cs hosted with ❤ by GitHub

After adding the StopWatch we’ll see that every (for this demo 5 seconds) a new point is added to the graph.

image

Source code

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, …

 
 
 
6
inShare
This entry was posted in .NETDevelopmentMVVMWPF and tagged GraphsMVVMWPF on 14 March 2013.

Post navigation

 The hard search for missing Extension methods in Umbraco 4.7.0Creating a JIRA interface in 1-2-3 

12 thoughts on “Creating graphs in WPF using OxyPlot

  1. Stephanus18 May 2013 at 13:21

    Thanx got working after a bit of thinking but wouldn’t have been able to without the demo

    Reply 
  2. Geoffrey Hunter31 May 2013 at 00:47

    Hi, do you know how to plot smoothed lines (i.e. is there built in capability to do this).

    Reply 
    1. Ali G10 July 2013 at 12:40

      var lineSerie = new LineSeries
      {


      Smooth = True,
      };

      Reply 
      1. Geoffrey Hunter26 August 2013 at 09:42

        Thanks! It worked!

        Reply 
  3. Thomas23 July 2013 at 21:57

    Are there any additional documentation of this product

    Reply 
    1. Bart De MeyerPost author31 July 2013 at 00:46

      Thomas, you’ll find some more documentation on http://oxyplot.codeplex.com/. I learned the most from diggin around in the Silverlight example browser (http://www.oxyplot.org/examplebrowser/)

      TIP: use Ctrl+Alt+C to copy the code from the plot your looking at in the example browser. Use Ctrl+Alt+R to copy the properties.

      Reply 
  4. James Tweedie8 August 2013 at 14:20

    Just started looking at OxyPlot for some scientific data plots. This is a great intro to it’s usage. I tried just building up the app based on the code shown here – of course that doesn’t work because some bits aren’t shown in the blog. So do get the code from GitHub – even if just for further reference.

    A further point – the “using OxyPlotDemo.Annotations;” threw me because R#8 does not automatically create this. Not needed though if you omit [NotifyPropertyChangedInvocator] from above
    “protected virtual void OnPropertyChanged(string propertyName)”

    Thanks Bart – a really good learning article, especially as OxyPlot has little on-line documentation.

    Reply 
  5. Ben25 September 2013 at 19:43

    How do i make the oxyplot a pop up with a single click on the graph?

    Reply 
  6. Michael4 April 2014 at 09:16

    In MainWindow.xaml.cs : Plot1.RefreshPlot(true);
    I get an error : “‘OxyPlot.Wpf.Plot’ does not contain a definition for ‘RefreshPlot’….”
    I’m using OxyPlot version : “2014.1.267.1” How do I solve this?

    Reply 
  7. kadzbi22 April 2014 at 14:39

    In current version of oxyplot You need to change:
    Plot1.RefreshPlot(true);
    to:
    Plot1.InvalidatePlot(true);

    Reply 
  8. Ben16 May 2014 at 19:03

    Hey there, great post! Any idea how I could mark areas in the plot? I thought of plotting one ultra-fat horizontal lines.

    I want to implement a “only-export-selected-data” function.

    Thanks in advance!

    Reply 
  9. Huang Maoru3 June 2014 at 12:31

    OXY plot is a nice charting library, but will it be able to compete in features with the Nevron NOV Chart – a porting of their great .NET charting component to their portable framework:

    Nevron Chart for .NET

    One thing I know is that NOV Chart will not have 3D features initially – does OXY plot support 3D?

ċ
PlotterOXYExample.zip
(2973k)
Javad Taghia,
Sep 30, 2014, 12:41 AM
Comments