title: Using Livecharts Constant Changes example to plot points based on time, and readings from a serial port?
body: <p>Okay, so basically I am wanting to make this graph to replace DateTime and plot the X axis based on a stop watch. Then I want data coming from a serial port to plot the data on the Y Axis. This should all should happen live, on the press of a button.</p>
  
  <p>Here is the example I followed:</p>
  
  <p><a href="https://lvcharts.net/App/examples/v1/wpf/Constant%20Changes" rel="nofollow noreferrer">https://lvcharts.net/App/examples/v1/wpf/Constant%20Changes</a></p>
  
  <p>The problem with it, is that it uses System Time rather than a stop watch of some sort. I am using WPF, and would love to expand the abilities of this graph.</p>
  
  <p>If someone could help me with this, either via skype or something. Please let me know!</p>
  
  <p>Eventually, I want to be able to take all that data from the graph and save it somewhere. So I can compare to previous points later. </p>
  
  <p>Thanks!</p>
  
  <p>Mainwindow.cs.xaml</p>
  
  <pre><code>&lt;Window x:Class="TestChartProject.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:local="clr-namespace:TestChartProject"
      xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
      mc:Ignorable="d"
      Title="MainWindow" Height="350" Width="525"&gt;
  &lt;Grid&gt;
      &lt;Grid.RowDefinitions&gt;
          &lt;RowDefinition Height="Auto"&gt;&lt;/RowDefinition&gt;
          &lt;RowDefinition Height="*"&gt;&lt;/RowDefinition&gt;
      &lt;/Grid.RowDefinitions&gt;
      &lt;Button Grid.Row="0" Click="InjectStopOnClick"&gt;
          Inject/Stop Data
      &lt;/Button&gt;
      &lt;!--Here we disable tooltips and hovering to get a better performance--&gt;
      &lt;lvc:CartesianChart Grid.Row="1" AnimationsSpeed="0:0:1" Hoverable="True" DataTooltip="{x:Null}"&gt;
          &lt;lvc:CartesianChart.Series&gt;
              &lt;lvc:LineSeries Values="{Binding ChartValues}" 
                              PointGeometry="{x:Null}" 
                              LineSmoothness="1"
                              StrokeThickness="6" 
                              Stroke="#F34336"
                              Fill="Transparent"/&gt;
          &lt;/lvc:CartesianChart.Series&gt;
  
  
  
          &lt;lvc:CartesianChart.AxisX&gt;
  
  
              &lt;lvc:Axis LabelFormatter="{Binding DateTimeFormatter}" 
                        MaxValue="{Binding AxisMax}" 
                        MinValue="{Binding AxisMin}"
                        Unit="{Binding AxisUnit}"&gt;
                  &lt;lvc:Axis.Separator&gt;
                      &lt;lvc:Separator Step="{Binding AxisStep}" /&gt;
                  &lt;/lvc:Axis.Separator&gt;
              &lt;/lvc:Axis&gt;
  
  
  
  
          &lt;/lvc:CartesianChart.AxisX&gt;
  
  
  
      &lt;/lvc:CartesianChart&gt;
  &lt;/Grid&gt;
  </code></pre>
  
  <p></p>
  
  <p>MainWindow.cs</p>
  
  <pre><code>    private double _axisMax;
      private double _axisMin;
      private double _trend;
  
      public MainWindow()
      {
          InitializeComponent();
  
          //To handle live data easily, in this case we built a specialized type
          //the MeasureModel class, it only contains 2 properties
          //DateTime and Value
          //We need to configure LiveCharts to handle MeasureModel class
          //The next code configures MeasureModel  globally, this means
          //that LiveCharts learns to plot MeasureModel and will use this config every time
          //a IChartValues instance uses this type.
          //this code ideally should only run once
          //you can configure series in many ways, learn more at 
          //http://lvcharts.net/App/examples/v1/wpf/Types%20and%20Configuration
  
          var mapper = Mappers.Xy&lt;MeasureModel&gt;()
              .X(model =&gt; model.Session.Ticks)   //use DateTime.Ticks as X
              .Y(model =&gt; model.Value);           //use the value property as Y
  
          //lets save the mapper globally.
          Charting.For&lt;MeasureModel&gt;(mapper);
  
          //the values property will store our values array
          ChartValues = new ChartValues&lt;MeasureModel&gt;();
  
          //lets set how to display the X Labels
          DateTimeFormatter = value =&gt; new DateTime((long)value).ToString("mm:ss");
  
          //AxisStep forces the distance between each separator in the X axis
          AxisStep = TimeSpan.FromSeconds(1).Ticks;
          //AxisUnit forces lets the axis know that we are plotting seconds
          //this is not always necessary, but it can prevent wrong labeling
          AxisUnit = TimeSpan.TicksPerSecond;
  
          SetAxisLimits(DateTime.Now);
  
          //The next code simulates data changes every 300 ms
  
          IsReading = false;
  
          DataContext = this;
      }
  
      public ChartValues&lt;MeasureModel&gt; ChartValues { get; set; }
      public Func&lt;double, string&gt; DateTimeFormatter { get; set; }
      public double AxisStep { get; set; }
      public double AxisUnit { get; set; }
  
      public double AxisMax
      {
          get { return _axisMax; }
          set
          {
              _axisMax = value;
              OnPropertyChanged("AxisMax");
          }
      }
      public double AxisMin
      {
          get { return _axisMin; }
          set
          {
              _axisMin = value;
              OnPropertyChanged("AxisMin");
          }
      }
  
      public bool IsReading { get; set; }
  
      private void Read()
      {
          var r = new Random();
  
          while (IsReading)
          {
              Thread.Sleep(1);
              var now = DateTime.Now;
  
  
              _trend = r.Next(100);
  
              ChartValues.Add(new MeasureModel
              {
                  Session = now,
                  Value = _trend
              });
  
              SetAxisLimits(now);
  
              //lets only use the last 150 values
              if (ChartValues.Count &gt; 10) ChartValues.RemoveAt(0);
          }
      }
  
      private void SetAxisLimits(DateTime now)
      {
          AxisMax = now.Ticks + TimeSpan.FromSeconds(1).Ticks; // lets force the axis to be 1 second ahead
          AxisMin = now.Ticks - TimeSpan.FromSeconds(8).Ticks; // and 8 seconds behind
      }
  
      private void InjectStopOnClick(object sender, RoutedEventArgs e)
      {
          IsReading = !IsReading;
          if (IsReading) Task.Factory.StartNew(Read);
      }
  
      #region INotifyPropertyChanged implementation
  
      public event PropertyChangedEventHandler PropertyChanged;
  
      protected virtual void OnPropertyChanged(string propertyName = null)
      {
          if (PropertyChanged != null)
              PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
  
      #endregion
  }
  </code></pre>
  
  <p>MeasureModel</p>
  
  <pre><code>    public class MeasureModel
  {
      public DateTime Session { get; set; }
      public double Value { get; set; }
  }
  </code></pre>