I'm doing an application real time data charting basing on WPF Charting Toolkit. I get the datas via serial port. The code of setting chart is below:
<chartingToolkit:Chart Margin="10,10,10,0" ClipToBounds="True" x:Name="chart1" Title="Chart Title">
<chartingToolkit:LineSeries IndependentValueBinding="{Binding Value1}" DependentValueBinding="{Binding Value2}" ItemsSource="{Binding}" Background="Transparent" Cursor="No">
<chartingToolkit:LineSeries.DataPointStyle>
<Style TargetType="{x:Type chartingToolkit:LineDataPoint}">
<Setter Property="Height" Value="0"/>
<Setter Property="Width" Value="0" />
<Setter Property="Background" Value="Green"/>
</Style>
</chartingToolkit:LineSeries.DataPointStyle>
</chartingToolkit:LineSeries>
</chartingToolkit:Chart>
It works good but I still need to set maximum and minimum values of X axis. The X Values (Value1) are as number of received samples and the Y axis values (Value2) are obviously as concrete values of the received samples.
My question is about the X axis range.
Currently, I'm getting minimum as 0 and maximinum as the highest number of sample which the serial port received in current moment.
But I want to set a permanent range of X axis which I want to see.
For example I want to see on the X axis range of 500 samples.
It means that when the number of samples exceeds 500, the max should be as the highest sample number and the min should be max-500.
The main difficulty is how to set it with real time data in WPF??
Can anyone help me, please??
Updated question
I'm updating my question after @jstreet advise.
I have this method which is running in separate thread within MainWindow class, likes below.
public partial class MainWindow : Window
{
public SerialPort serialPort1 = new SerialPort();
public string rx_str = "";
public string rx_str_copy;
public int a;
public double x, y;
ObservableCollection<ChartData> chartData;
ChartData objChartData;
Thread myThread;
public MainWindow()
{
InitializeComponent();
string[] port = SerialPort.GetPortNames();
foreach (string a in port)
{
comboPorts.Items.Add(a);
}
Array.Sort(port);
comboPorts.Text = port[0];
objChartData = new ChartData();
chartData.Add(objChartData);
chart1.DataContext = chartData;
myThread = new Thread(new ThreadStart(Run));
}
public void Run()
{
while (true)
{
serialPort1.Write("a");
rx_str = serialPort1.ReadTo("b");
rx_str_copy = rx_str;
x = a;
y = Double.Parse(rx_str_copy, CultureInfo.InvariantCulture);
a++;
Dispatcher.Invoke(new Action(delegate
{
chartData.Add(new ChartData() { Value1 = x,
Value2= y });
}));
}
}
This Run() method is responsible for receiving datas and adding it to the chart.
In another class I have handle of reaction on comming datas and settings properties Valeu1 and Value2:
public class ChartData : INotifyPropertyChanged
{
double _Value1;
double _Value2;
public double Value1
{
get
{
return _Value1;
}
set
{
_Value1 = value;
OnPropertyChanged("Value1");
}
}
public double Value2
{
get
{
return _Value2;
}
set
{
_Value2 = value;
OnPropertyChanged("Value2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
}
}
}
How can I adapt @jstreet's solution to my behind code example??
Create a MinValue
dependency property in your view model and bind it to your axis Minimum
property. Take a look:
XAML:
<Window
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:WpfApp31"
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
x:Class="WpfApp31.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<chartingToolkit:Chart Title="My Sample">
<chartingToolkit:Chart.Axes>
<chartingToolkit:LinearAxis Minimum="{Binding MinValue}" Orientation="X"></chartingToolkit:LinearAxis>
</chartingToolkit:Chart.Axes>
<chartingToolkit:LineSeries IndependentValueBinding="{Binding Value1}"
DependentValueBinding="{Binding Value2}"
ItemsSource="{Binding Data}">
</chartingToolkit:LineSeries>
</chartingToolkit:Chart>
</Grid>
</Window>
View Model:
public class MyViewModel : DependencyObject
{
public int MinValue
{
get { return (int)GetValue(MinValueProperty); }
set { SetValue(MinValueProperty, value); }
}
// Using a DependencyProperty as the backing store for MinValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register("MinValue", typeof(int), typeof(MyViewModel), new PropertyMetadata(default(int)));
public ObservableCollection<MyDataModel> Data { get; set; }
private Timer serialPort;
private Random y;
private int x;
private int range;
public MyViewModel()
{
range = 10;
Data = new ObservableCollection<MyDataModel>();
y = new Random(DateTime.Now.Millisecond);
serialPort = new Timer(DataReceived, null, 500, 500);
}
private void DataReceived(object state)
{
Application.Current.Dispatcher.Invoke(() => {
Data.Add(new MyDataModel { Value1 = x, Value2 = y.Next(10, 90) });
MinValue = x < range ? 0 : x - range;
x++;
});
}
}
EDIT: For the record, I would probably not write this code quite like below. I'm doing it here just so you can move forward with it.
XAML:
<Window
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:WpfApp1"
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
x:Class="WpfApp1.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<chartingToolkit:Chart Grid.Row="0" Margin="10,10,10,0" ClipToBounds="True" x:Name="chart1" Title="Chart Title">
<chartingToolkit:Chart.Axes>
<chartingToolkit:LinearAxis Minimum="{Binding MinValue}" Orientation="X"></chartingToolkit:LinearAxis>
</chartingToolkit:Chart.Axes>
<chartingToolkit:LineSeries IndependentValueBinding="{Binding Value1}" DependentValueBinding="{Binding Value2}" ItemsSource="{Binding chartData}" Background="Transparent" Cursor="No">
<chartingToolkit:LineSeries.DataPointStyle>
<Style TargetType="{x:Type chartingToolkit:LineDataPoint}">
<Setter Property="Height" Value="0"/>
<Setter Property="Width" Value="0" />
<Setter Property="Background" Value="Green"/>
</Style>
</chartingToolkit:LineSeries.DataPointStyle>
</chartingToolkit:LineSeries>
</chartingToolkit:Chart>
<Button Grid.Row="1" x:Name="btn1" Click="btn1_Click">START</Button>
</Grid>
CS:
public partial class MainWindow : Window
{
public double MinValue
{
get { return (double)GetValue(MinValueProperty); }
set { SetValue(MinValueProperty, value); }
}
// Using a DependencyProperty as the backing store for MinValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register("MinValue", typeof(double), typeof(MainWindow), new PropertyMetadata(default(double)));
//public SerialPort serialPort1 = new SerialPort();
//public string rx_str = "";
//public string rx_str_copy;
//public int a;
public double x, y;
public ObservableCollection<ChartData> chartData { get; set; }
ChartData objChartData;
Thread myThread;
Random r;
int range = 50;
public MainWindow()
{
InitializeComponent();
r = new Random();
DataContext = this;
/*
string[] port = SerialPort.GetPortNames();
foreach (string a in port)
{
comboPorts.Items.Add(a);
}
Array.Sort(port);
comboPorts.Text = port[0];
*/
objChartData = new ChartData();
chartData = new ObservableCollection<ChartData>();
chartData.Add(objChartData);
//chart1.DataContext = chartData;
myThread = new Thread(new ThreadStart(Run));
}
private void btn1_Click(object sender, RoutedEventArgs e)
{
myThread.Start();
}
public void Run()
{
while (true)
{
//serialPort1.Write("a");
//rx_str = serialPort1.ReadTo("b");
//rx_str_copy = rx_str;
//x = a;
//y = Double.Parse(rx_str_copy, CultureInfo.InvariantCulture);
//a++;
Dispatcher.Invoke(new Action(delegate
{
chartData.Add(new ChartData()
{
Value1 = x,
Value2 = r.NextDouble(),
//Value2 = y
});
MinValue = x < range ? 0 : x - range;
x++;
}));
Thread.Sleep(50);
}
}
}