My application calculates points by a given formula. With a small interval, new points are added to the chart. With the slider I can adjust the values of these points.
For optimization purposes, unnecessary points begin to be deleted from the list. But after deletion, the value of the next point starts to be calculated incorrectly when you move the slider.
How can this problem be solved?
Class:
using System.Windows;
using LiveCharts;
using LiveCharts.Wpf;
using LiveCharts.Defaults;
using System.Windows.Media;
using System.Threading.Tasks;
using System;
using System.Threading;
namespace TestChartApp
{
public partial class MainWindow : Window
{
SeriesCollection series = new SeriesCollection();
ChartValues<ObservableValue> observableValues = new ChartValues<ObservableValue>();
LineSeries lineSeries = new LineSeries
{
Stroke = Brushes.Blue,
Fill = Brushes.Transparent,
PointGeometry = null
};
double currentStep = 0;
public MainWindow()
{
InitializeComponent();
lineSeries.Values = observableValues;
series.Add(lineSeries);
myChart.Series = series;
myChart.DataTooltip = null;
myChart.Hoverable = false;
Task.Factory.StartNew(AddValues);
}
private void AddValues()
{
Application.Current.Dispatcher.Invoke(() =>
{
ObservableValue value = new ObservableValue(sAmplitude.Value * Math.Sin(2 * Math.PI * 0.25 * currentStep));
currentStep += 0.06;
observableValues.Add(value);
if (observableValues.Count > 100)
{
SetAxisLimits(observableValues.Count);
}
if (observableValues.Count > 150)
{
observableValues.RemoveAt(0);
}
});
Thread.Sleep(35);
Task.Factory.StartNew(AddValues);
}
private void SetAxisLimits(double value)
{
Axis axis = myChart.AxisX[0];
axis.MinValue += value - axis.MaxValue;
axis.MaxValue = value;
}
private void ChangeObservableValues()
{
int j = 0;
for (double i = 0.0; j < observableValues.Count; i += 0.06)
{
observableValues[j++].Value = sAmplitude.Value * Math.Sin(2 * Math.PI * 0.25 * i);
}
}
private void sAmplitude_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (lineSeries.Values != null)
{
ChangeObservableValues();
}
}
}
}
XAML:
<Window x:Class="TestChartApp.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:TestChartApp"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.8*"/>
<RowDefinition Height="0.2*"/>
</Grid.RowDefinitions>
<lvc:CartesianChart x:Name="myChart" DisableAnimations="True">
<lvc:CartesianChart.AxisX>
<lvc:Axis MaxValue="100" MinValue="0" Labels="" Unit="1">
<lvc:Axis.Separator>
<lvc:Separator Step="20">
<lvc:Separator.Stroke>
<SolidColorBrush Color="Gray" />
</lvc:Separator.Stroke>
</lvc:Separator>
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis MaxValue="100" MinValue="-100" Labels="">
<lvc:Axis.Separator>
<lvc:Separator>
<lvc:Separator.Stroke>
<SolidColorBrush Color="Gray" />
</lvc:Separator.Stroke>
</lvc:Separator>
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Slider x:Name="sAmplitude" HorizontalAlignment="Stretch" Margin="30 30 30 0" Grid.Row="1" VerticalAlignment="Top" Maximum="100" Value="50" LargeChange="10" ValueChanged="sAmplitude_ValueChanged"/>
</Grid>
</Grid>
</Window>
It looks like you are calculating the wrong values when updating the old data.
ChangeObservableValues
is calculating its own currentSteps
value, always starting from 0
, which is wrong as the original value for the current data point is bigger :
originalCurrentStepsOfCurrentDataPoint = absoluteCurrentDataPointPosition * 0.06.
To allow easier refactoring and also to eliminate a potential error source, the value 0.06
should be a constant field or read-only property.
private const double Step = 0.06;
private void ChangeObservableValues()
{
// Because the collection only contains the latest 150 values,
// we can calculate the absolute position using the collection's Count
var restoredCurrentStep =
this.currentStep - this.observableValues.Count * MainWindow.Step;
for (int index = 0; index < observableValues.Count; index++)
{
observableValues[index].Value =
sAmplitude.Value * Math.Sin(2 * Math.PI * 0.25 * restoredCurrentStep);
restoredCurrentStep += MainWindow.Step;
}
}