title: .NET LiveCharts, Performance Issues with real-time data
body: <p>I am using <a href="https://lvcharts.net/" rel="nofollow noreferrer">LiveCharts</a> for my WPF app and am currently facing an issue with performance when plotting multiple <code>LineSeries</code>/<code>GLineSeries</code> with real-time data. Here's a GIF showing what's happening:</p>
  
  <p><img src="https://user-images.githubusercontent.com/7090076/69064139-62275000-0a26-11ea-9c47-0ffa63d274e7.gif" alt="livecharts"></p>
  
  <p>Although it's subtle, my issue with this graph is that it noticeably "jumps" ahead quite often and does not appear "smooth" visually.</p>
  
  <p>I plotted the same data (10 lines with 400 points each) with OxyPlot:</p>
  
  <p><img src="https://user-images.githubusercontent.com/7090076/69064288-9bf85680-0a26-11ea-8151-493d3de1a38b.gif" alt="oxyplot"></p>
  
  <p>And although it is not ideal as well, it seems to generate a nicer "flow" of data.</p>
  
  <p>Here is my code:</p>
  
  <pre><code>public abstract class ObservableObject : INotifyPropertyChanged
      {
          public event PropertyChangedEventHandler PropertyChanged;
  
          protected bool SetProperty&lt;T&gt;(ref T field, T newValue, [CallerMemberName]string propertyName = null)
          {
              if (!EqualityComparer&lt;T&gt;.Default.Equals(field, newValue))
              {
                  field = newValue;
                  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                  return true;
              }
              return false;
          }
      }
  }
  
  public class MainWindowViewModel : ObservableObject, IDataListener
      {
          public ObservableCollection&lt;SignalGraphViewModel&gt; SignalGraphs { get; }
  
          private int _maxSeriesPoints = 400;
  
          public SeriesCollection Series { get; set;  }
          public CartesianChart Chart { get; set; }
          public int NChannels { get; set; }
  
          private bool _isReading = false;
          public bool IsReading {
              get =&gt; _isReading;
              set =&gt; SetProperty(ref _isReading, value);
          }
  
          public MainWindowViewModel(int nChannels)
          {
              NChannels = nChannels;
              SignalGraphs = new ObservableCollection&lt;SignalGraphViewModel&gt;();
  
              for (int i = 0; i &lt; nChannels; i++)
              {
                  SignalGraphs.Add(new SignalGraphViewModel(i));
              }
  
              Series = new SeriesCollection();
  
              for (var i = 0; i &lt; NChannels; i++)
              {
                  var series = new GLineSeries
                  {
                      Values = new GearedValues&lt;double&gt;(),
                      Fill = Brushes.Transparent,
                      StrokeThickness = 1,
                      PointGeometry = null
                  };
                  Series.Add(series);
              }
  
              Chart = new CartesianChart
              {
                  DisableAnimations = true,
                  DataTooltip = null,
                  Hoverable = false,
                  Zoom = ZoomingOptions.None,
                  Series = Series                
              };
          }
  
          public void Update(EEGSamples samples)
          {
              Application.Current.Dispatcher.BeginInvoke((Action)delegate {
                  UpdateChart(samples);
              });
          }
  
          private void UpdateChart(EEGSamples samples)
          {
              var watch = System.Diagnostics.Stopwatch.StartNew();
  
              for (var i = 0; i &lt; NChannels; i++)
              {
                  var channelSamples = samples.Data.Select(d =&gt; (object)(d.Measurements[i] + i * 3000)).AsGearedValues();
                  Chart.Series[i].Values.AddRange(channelSamples);
  
                  for (var j = Chart.Series[i].Values.Count; j &gt; _maxSeriesPoints; j--)
                  {
                      Chart.Series[i].Values.RemoveAt(0);
                  }
              }
          }
  </code></pre>
  
  <p>where <code>Update</code> method gets called every 50 milliseconds with 6 data points for each line.</p>
  
  <p>Am I expecting too much? Is this too heavy of a scenario to be handled flawlessly in the UI?</p>