Home / WPF / HMI Controls / Real-time Line charts with WPF and Dynamic Data Display

Real-time Line charts with WPF and Dynamic Data Display

When logging datas from sensors it’s important to visualize them with a powerful real-time chart. In automation especially it’s common to analyze processes with the help of charts to predict and regulate the process.

One of the most powerful charts (and for many the best) is Dynamic Data Display, an open source library for WPF. This library is good because of the many features (panning, zooming, saving screenshots etc…) and because it renders fast with huge collections of points.

There are 2 versions, both hosted on codeplex:

I’ll show an example on how to realize a real time chart with a “customized observable collection” provided by this control, the RingArray. I will design a variable Sin, one point every 100ms on a datetime axis.

There are three points of interest that you should note:

  1. Creating the ViewModel for the chart
  2. Binding the chart to the ViewModel
  3. Write the Xaml for the chart

ViewModel

The ViewModel, as said before, is a RingArray. in this case i called it Voltage Point Collection

    public class VoltagePointCollection : RingArray
    {
        private const int TOTAL_POINTS = 300;

        public VoltagePointCollection()
            : base(TOTAL_POINTS) // here i set how much values to show
        {
        }
    }

    public class VoltagePoint
    {
        public DateTime Date { get; set; }

        public double Voltage { get; set; }

        public VoltagePoint(double voltage, DateTime date)
        {
            this.Date = date;
            this.Voltage = voltage;
        }
    }

Binding

The binding of the chart to this RingArray must be done in code behind on the constructor, in this way:

var ds = new EnumerableDataSource(voltagePointCollection);
ds.SetXMapping(x => dateAxis.ConvertToDouble(x.Date));
ds.SetYMapping(y => y.Voltage);
plotter.AddLineGraph(ds, Colors.Green, 2, "Volts"); // to use this method you need to add manually "using Microsoft.Research.DynamicDataDisplay;"

Xaml

The xaml chart is written in this way:

<d3:ChartPlotter x:Name="plotter" Grid.Row="1" Grid.Column="1">
<d3:ChartPlotter.HorizontalAxis>
<d3:HorizontalDateTimeAxis Name="dateAxis"/>
</d3:ChartPlotter.HorizontalAxis>
<d3:Header FontFamily="Georgia" Content="Voltage chart"/>
<d3:VerticalAxisTitle FontFamily="Georgia" Content="Voltage [V]" />
<d3:HorizontalAxisTitle FontFamily="Georgia" Content="Time"/>
<d3:HorizontalLine Value="{Binding MaxVoltage}" Stroke="Red" StrokeThickness="2"/>
<d3:HorizontalLine Value="{Binding MinVoltage}" Stroke="Red" StrokeThickness="2"/>
</d3:ChartPlotter>

Get the sample application and more examples

Get the sample application on GitHub: https://github.com/mesta1/DynamicDataDisplay-example
You can also find more examples here:http://d3future.codeplex.com/SourceControl/list/changesets

47 comments

  1. Hi,
    I am working on Wpf real time application.In this application i have to show thousands of record in a single time using chartplotter.My code works fine without any exception.
    Problem :
    Here problem is that it take long time to show output.For 30 thousands of record it takes 15 minutes.How i can modify my code so that i reduce the response time.

    Thanks in Advance

    • @Sunny:
      I should see the code to help you, anyway i already used big datasets (bigger than 30.000) without any problem.
      Let’s suppose that you have a Window that contains a button that pop-up the chart in another window.
      Looking at the example provided with this article, you should build your collection when you acquire the data, so if you are reading data from a device, you should process the data and you add them to a RingArray collection.
      In the main window you create your collection and update it, in the popup window you bind the collection to chart (like in the example in the “loaded event”).
      This chart’s databinding is strange, so you should use an existing collection provided by this chart (like RingArray) and not observable collections or lists.

  2. Just thank you ,the source was very nice and useful.

  3. Hi there… i am very happy to find this article… it’s very helpfully.

  4. how change amplitude range of sine wave

    • I can’t understand what you mean.
      The sin is calculated here:
      voltagePointCollection.Add(new VoltagePoint(Math.Sin(i*0.1),DateTime.Now));
      So if you multiply the sin for a factor you will obtain a bigger sin.
      If you need a different scaling from autoscaling, i think you have to modify the sources to adapt the chart to your needs. Check the discussions on codeplex.

  5. Hi, if we wanna add multiple line what we should do?

  6. Thanks for your article. It’s a diamond among many useless examples.

  7. Is there a way to create scatter plots with just points and no lines?

  8. Its very helpful.
    I have a requirement to click a each line plot. Is it possible?

    • This chart has a lot of examples in the sources, and for sure there is one that contain a clickable chart. You have to dig into the examples and find how to do it.

  9. I checked in examples, but I didn’t find any examples for clickable linegraph controls. if you know it please share to me. Also I want to set the width between x axis plot. for example , if set the width is 5, then marking the x axis scale is 5,10,15 etc?

  10. This is really helpful. Thanks.
    But I wanted to clear the screen after some time interval and continue the plotting. Just like a ECG plot for heart. Could you help with that ?

  11. Fantastic, really helpful. Thanks a lot

  12. Hi. Very nice work! How can i change the time interval when chart starts scrolling? and how do i set the minimum and maximum value for the chart?

  13. Hi mesta,

    Can you help me how to drag the graph by clicking with the mouse.Is it possible with the dynamic data display??

  14. Hi mesta,
    I have the “using Microsoft.Research.DynamicDataDisplay;” in my file but for some reasons I can’t use the function AddLineGraph and keep getting the error “‘Microsoft.Research.DynamicDataDisplay.ChartPlotter’ does not contain a definition for ‘AddLineGraph'”.
    Would you have any idea on what I’m missing?

    • I think you need to add

      using Microsoft.Research.DynamicDataDisplay.DataSources;

      Check the example at the bottom of the article.

  15. Thanks for the great example!

    We have download the example code from this site and notice that the project “DynamicDataDisplay” source code version is 0.3.0.0.

    The latest one is DynamicDataDisplay for WPF is 0.4.0.0.
    If we install through NuGet, we only get the dlls such as “DynamicDataDisplay.dll”, “DynamicDataDisply.Maps.dll”, “DynamicDataDisplay.Markers.dll”, and “DynamicDataDisplay.Markers2.dll” without source code.

    We want to get the source code.
    We went to the site: http://d3future.codeplex.com/SourceControl/latest
    download the zip file from Source code tab page and notice the dlls are version 0.3.2.0.

    Where could we download latest “DynamicDataDisplay” project with source code for WPF with version 0.4.0.0?
    thanks!

  16. Mesta,

    Thanks for providing the link.

    we just notice that using Dynamic Data Display library we get the following data binding errors:

    System.Windows.Data Error: 4 : Cannot find source for binding with reference ‘RelativeSource FindAncestor, AncestorType=’System.Windows.Controls.ItemsControl’, AncestorLevel=’1”. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is ‘MenuItem’ (Name=”); target property is ‘HorizontalContentAlignment’ (type ‘HorizontalAlignment’)
    System.Windows.Data Error: 4 : Cannot find source for binding with reference ‘RelativeSource FindAncestor, AncestorType=’System.Windows.Controls.ItemsControl’, AncestorLevel=’1”. BindingExpression:Path=VerticalContentAlignment; DataItem=null; target element is ‘MenuItem’ (Name=”); target property is ‘VerticalContentAlignment’ (type ‘VerticalAlignment’)

    We tried to run your example of application and get the same errors in the Output window.
    Have you noticed this data binding errors?

    we search “RelativeSource” in the Dynamic Data Display library and only found the following 8 instances of “RelativeSource”.

    Find all “RelativeSource”, Subfolders, Find Results 1, Entire Solution, “*.*”
    C:\CSharp\DynamicDataDisplaySample\DynamicDataDisplay\Charts\Legend.xaml(9):
    Matching lines: 8 Matching files: 3 Total files searched: 253

    Do you know how to remove those data binding errors? thx!

  17. Hi Mesta,

    We just found the link: http://dynamicdatadisplay.codeplex.com/discussions/73870

    In the Dynamic Data Display ChartPlotter constructor, comment out defaultContextMenu will remove the binding errors. However, this means there is no context menu anymore.

    Children.AddMany(
    horizontalAxis,
    verticalAxis,
    axisGrid,
    mouseNavigation,
    keyboardNavigation,
    //defaultContextMenu,
    horizontalAxisNavigation,
    verticalAxisNavigation,
    legend
    );

    If you know the better way to fix this binding error, please share! thx!

    • He basically means that you need to create your own ContextMenu.
      This is a quick and dirty example, hope it gets you started. Go on constructor of chart plotter, above the Children.AddMany, then keep the line commented out and add the part that build the ContextMenu.

      public ChartPlotter()
      {
          MenuItem fitToViewMenuItem = new MenuItem();
          fitToViewMenuItem.Header = "Fit to view";
          fitToViewMenuItem.ToolTip = "tooltip Fit to view";
          fitToViewMenuItem.Click += fitToViewMenuItem_Click;
          this.ContextMenu = new  ContextMenu();
          this.ContextMenu.ItemsSource = new List<MenuItem> { fitToViewMenuItem };
      }
      
      void fitToViewMenuItem_Click(object sender, RoutedEventArgs e)
      {
          this.FitToView();
      }
      
  18. Mesta,

    Thanks for providing the solution!

  19. very useful article

    Thou I sometimes get NullReferenceException at line 2 of your Binding example
    full stacktrace: http://pastebin.com/9StCbdFj

    here’s my code:

    public MainWindow()
    {
    Loaded += (sender, args) => SetupGraph();
    InitializeComponent();
    }

    private void SetupGraph()
    {
    Data = new ObservableDataSource(new List {new DataPoint(DataType.Ai0, 0) });

    Data.SetYMapping(y => y.Value);
    if(dateAxis == null)
    Console.WriteLine(“dateAxis”);

    Data.SetXMapping(x => dateAxis.ConvertToDouble(x.Date));
    plotter.AddLineGraph(Data, Colors.Green, 2, “Volt”);
    }

    It’s dateAxis.ConvertToDouble(x.Date)) that breaks and aparently it’s x.Date which is Null but that cannot be true because when I look at it with a breakpoint i can clearly see that the first Member of “Data” has a correct DateTime

    Would be cool if you could have a look on it

    • My formattings was nuked so please see here for the formatted code: http://pastebin.com/tHdP4Cf9

    • You didn’t post DataPoint class, but it seems that you added a point with a value and an integer, instead of a point with a value and a Date. This is why it can’t convert the date to double, so the ConvertToDouble method returns null.

      Data = new ObservableDataSource(new List {new DataPoint(DataType.Ai0, DateTime.Now) });
      
      • sharp thinking there but my DataPoint contains a valid DateTime. See here for the class: http://pastebin.com/zETkL4iK
        I cannot explain the error to me. It’s weird thats why i asked you, who probably knows how things work

      • I already tried having `DateTime Date` with a default value of 01.01.1970 but this didn’t stop the error
        Does that class somehow use the default constructor?

        • There are two things different from my example, one is that I used EnumerableDataSource and you use ObservableDataSource, and the second one is that you initialize everything in the loaded event and I initialized it in the constructor. My guess is that one of the two is causing the problem, but if not, upload the example somewhere and I’ll take a look.

        • A thank you. I’ll have a look on both

        • You really did helped me. Now I no longer get random NullReferenceExceptions. I’m sorry to waste your time again but I’m not happy with my current approach. My Graph only updates when i zoom or pan around in the view. Calling plotter.UpdateLayout(); had no effect.
          Could you please have a look on my code again? http://pastebin.com/cza0Vee5

          When you scroll down you see my old working code which is the one that crashed every second or third time i started the software but otherwise worked fine

        • I can create a minimal working examle .sIn for you if you want to but i think the code above is sufficent (doesn’t throw any exceptions but isn’t live)

          Plus I don’t know how to append data to an EnumerableDataSource. I looked really close on your example and I don’t know if the approach with the RingArray fit’s my needs

        • There are several examples that you can look at, not only mine.The documentation on DDD is scarce and the only way is to look through all the examples and read the source code, until you find what you need.
          Also there is another chart library that is more up to date and has better documentation, Oxyplot. Maybe you want to check also that.

  20. Great job! Congrats! 🙂

  21. Hello all,
    I’m currently using this for displaying spectrum on User interface, it works great but has a huge memory leak (about 1-2MB/cycle). Did any one else experienced this problem? and how would I go about solving it?
    Thanks in advance. See my code below

    Dim P1Chart As New Microsoft.Research.DynamicDataDisplay.ChartPlotter()
    Dim P1ChartDateTime As New Windows.Controls.Label()

    …………………….

    ‘First remove old line graph from chart
    ClearSpectrum(iTabIndex)

    ‘Load arrays with new spectral data
    For SpectCtr = 0 To ScanElements
    If Spectrum = ScanType.Absorbance Then
    x(SpectCtr) = SpectCtr * 0.5 + 800.0 ‘Convert to wavelength if absorbance spectra
    Else
    x(SpectCtr) = SpectCtr
    End If

    y(SpectCtr) = sr.ReadLine()
    Next

    Dim xDataSource = AsXDataSource(x)
    Dim yDataSource = AsYDataSource(y)

    Dim xyDataSource As New CompositeDataSource(xDataSource, yDataSource)

    Select Case StreamNumber
    Case 1
    P1Chart.AddLineGraph(xyDataSource, Colors.Red, 1, “Probe A”)
    P1ChartDateTime.Content = String.Format(“Time: {0}, X-Axis: {1}”, FileSaveTime, xAxisTitle)
    Case 2
    P2Chart.AddLineGraph(xyDataSource, Colors.Red, 1, “Probe B”)
    P2ChartDateTime.Content = String.Format(“Time: {0}, X-Axis: {1}”, FileSaveTime, xAxisTitle)
    End Select

    ………

    Private Sub ClearSpectrum(ByVal iTabIndex As Integer)
    Dim nIndex As Integer

    Select Case iTabIndex
    Case 1
    For Each child1 As IPlotterElement In P1Chart.Children
    If TypeOf child1 Is Microsoft.Research.DynamicDataDisplay.LineGraph Then
    RemoveList.Add(child1)
    End If
    Next

    For Each child1 In RemoveList
    P1Chart.Children.Remove(child1)
    Next
    ……………..

    • I used it in binding with a collection in my example. If you have a memory leak you are storing the points or you are recreating the graph every time and not disposing it.
      It’s better if you use it in binding with a collection.

  22. Maciej Klimczuk

    Have you ever tried LiveCharts https://lvcharts.net/ ?
    I just found it in internet by coincidence and i was wondering if its worth to try.

    • It’s the first time that I see it. Thanks for linking it, I will try it out the next days.

      • Maciej Klimczuk

        Hi there!
        I just made simple app to test this library and I’m disappointed. It looks really greate but performance is much slower then I expected. There is an option to pay for some kind of additional featres which increase performance but i didn’t tried it. Maybe this way it’s faster but anyway i’m looking for free options.
        If you want to save some of Your time here is link to repo. https://github.com/mklimczuk/Charts .
        Cheers

        • Thank you, I checked it today and with a lot of points (3000+) performance are really bad. Maybe you can have a look at OxyPlot for a comparison.

Leave a Reply

Share
Tweet
Share
+11