I am trying to access the chart object created in the mainwindow.xaml file within my viewmodel file. The code I have included is for a simple program I made that uses a basic PID controller and graphs the output using an opensource charting tool called oxyplot (won't work for the speed I need for updating the plots and it bogs down my UI). The implementation is the same as what I have come across in some other charting tools as well (in terms of accessing the chart object from within the view model instead of from maindwindow.cs.
In the example code I included below, PlotModel is the reference I am able to bind to the chart object so that I can access it through the view model called MainViewModel.cs. I tried to reference the Nchartcontrol object in the same manner but I am only able to do that if I use it in Mainwindow.cs (I receive an error that the variable does exist within the current context). I have not been able to access it from MainViewModel.cs. Is there a property associated with an Nchartcontrol that allows me to access it within the viewmodel similar to the oxyplot model property I bound the chart control to in the sample below? I tried changing the datacontext away from mainwindow to the viewmodel, but that caused several errors. I am fairly new to wpf so I am probably missing something simple, but it is beyond me. Hopefully, this is enough information, but if that isn't clear enough let me know and I would be happy to provide more detail.
Thanks!
Mainwindow.xaml
<Window x:Class="PID_Charted.MainWindow"
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml" xmlns:oxy="
http://oxyplot.org/wpf" xmlns:local="clr-namespace
ID_Charted"
Title="PID Dynamic Chart Update Example" Height="600" Width="750">
<window.DataContext>
<local:MainViewModel/>
</window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="400"/>
<RowDefinition/>
</Grid.RowDefinitions>
<oxy
lotView Model="{Binding DataPlot}" Title="{Binding Title}">
<oxy
lotView.Axes>
<oxy:LinearAxis IsAxisVisible="False"/>
</oxy
lotView.Axes>
<oxy
lotView.Series>
<oxy:LineSeries ItemsSource="{Binding Points}"/>
</oxy
lotView.Series>
</oxy
lotView>
<Label VerticalAlignment="Bottom" Grid.Row="2" Content="{Binding SimRunTime, Mode=Default}"/>
</Grid>
</Window>
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OxyPlot;
using OxyPlot.Series;
using System.Windows.Threading;
using OxyPlot.Axes;
using System.Diagnostics;
using System.ComponentModel;
namespace PID_Charted
{
public class MainViewModel : INotifyPropertyChanged
{
//PID Variables
double Last_PV, PV;
double Last_MV, MV;
double Error, LError, IError, DError;
double Setpoint;
double Kp = 1, Ki = 1, Kd = 1;
public PlotModel DataPlot { get; set; }
LinearAxis xAxis;
LinearAxis yAxis;
//Timer
DispatcherTimer timer = new DispatcherTimer();
private int _xValue = 1;
Stopwatch StopWatch = new Stopwatch();
public MainViewModel()
{
Setpoint = 100;
Last_PV = 0;
Last_MV = 0;
LError = 0;
DataPlot = new PlotModel();
// now let's define X and Y axis for the plot model
xAxis = new LinearAxis();
xAxis.Position = AxisPosition.Bottom;
xAxis.Title = "Time (s)";
xAxis.MajorStep = 50;
yAxis = new LinearAxis();
yAxis.Position = AxisPosition.Left;
yAxis.Maximum = 150;
yAxis.Minimum = 0;
yAxis.Title = "Values";
DataPlot.Axes.Add(xAxis);
DataPlot.Axes.Add(yAxis);
DataPlot.Title = "PID";
DataPlot.Series.Add(new LineSeries());
DataPlot.Series.Add(new LineSeries());
timer.Tick += new EventHandler(Update);
timer.Interval = new TimeSpan(0, 0, 0, 0, 30);
timer.Start();
StopWatch.Start();
}
private int _SimRunTime;
public int SimRunTime
{
get
{
return _SimRunTime;
}
set
{
_SimRunTime = value;
OnPropertyChanged("SimRunTime");
}
}
public void Update(object sender, EventArgs e)
{
SimRunTime = (int)StopWatch.Elapsed.TotalSeconds;
//WTF????
int timering = ((int)StopWatch.ElapsedMilliseconds);
Setpoint_Setter();
PID();
Process();
//if (SimTicker % 10 == 0)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
{
(DataPlot.Series[1] as LineSeries).Points.Add(new DataPoint(timering, Setpoint));
(DataPlot.Series[0] as LineSeries).Points.Add(new DataPoint(timering, Last_PV));
if ((DataPlot.Series[0] as LineSeries).Points.Count > 100) //show only n last points
{
(DataPlot.Series[0] as LineSeries).Points.RemoveAt(0); //remove first point
(DataPlot.Series[1] as LineSeries).Points.RemoveAt(0);
}
//if you don't want to have the graph resize on the x axis as it starts off
//if (_xValue < 100)
//{
// xAxis.Minimum = 0;
// xAxis.Maximum = 100;
//}
//else
//{
// xAxis.Minimum = _xValue - 100;
// xAxis.Maximum = _xValue;
//}
xAxis.IsAxisVisible = false;
DataPlot.InvalidatePlot(true);
_xValue++;
});
}
}
//PID Controller
//
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/ public void PID()
{
Error = Setpoint - Last_PV;
IError += Error;
DError = Error - LError;
LError = Error;
Last_MV = Kp * Error + Ki * IError + Kd * DError;
if (Last_MV > 150)
{
Last_MV = 150;
}
if (Last_MV < 5)
{
Last_MV = 5;
}
}
public void Process()
{
Last_PV += (Last_MV - Last_PV) / 30;
}
public void Setpoint_Setter()
{
if (_SimRunTime < 40)
Setpoint = 100;
if (_SimRunTime >= 40)
Setpoint = 50;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}