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

Share Button

41 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! 🙂

Leave a Reply