Reputation: 45931
I'm developing a WPF app with C#, .NET Framework 4.7. and Oxyplot 1.0.
I'm trying to update the graphic at runtime, but it doesn't do anything.
I have tried to use ObsevableCollection
and InvalidateFlag
but without success.
This is the XAML:
<oxy:Plot Title="{Binding Title}" InvalidateFlag="{Binding InvalidateFlag}">
<oxy:Plot.Series>
<oxy:LineSeries ItemsSource="{Binding BestFitness}"/>
<oxy:LineSeries ItemsSource="{Binding WorstFitness}"/>
<oxy:LineSeries ItemsSource="{Binding AverageFitness}"/>
</oxy:Plot.Series>
</oxy:Plot>
And this is the view model:
public class MainViewModel : ObservableObject
{
private int count;
private int _invalidateFlag;
public string Title { get; set; }
public int InvalidateFlag
{
get { return _invalidateFlag; }
set
{
_invalidateFlag = value;
RaisePropertyChangedEvent("InvalidateFlag");
}
}
public ObservableCollection<DataPoint> BestFitness { get; set; }
public ObservableCollection<DataPoint> WorstFitness { get; set; }
public ObservableCollection<DataPoint> AverageFitness { get; set; }
public ICommand StartCommand
{
get { return new DelegateCommand(Start); }
}
public ICommand RefereshCommand
{
get { return new DelegateCommand(Refresh); }
}
public MainViewModel()
{
this.Title = "Example 2";
this.BestFitness = new ObservableCollection<DataPoint>
{
new DataPoint(0, 4),
new DataPoint(10, 13),
new DataPoint(20, 15),
new DataPoint(30, 16),
new DataPoint(40, 12),
new DataPoint(50, 12)
};
}
private void Start()
{
Random rnd = new Random((int)DateTime.Now.Ticks);
Program program = new Program(rnd);
program.Algorithm.EvolutionEnded += Algorithm_EvolutionEnded;
count = 0;
this.BestFitness = new ObservableCollection<DataPoint>();
this.WorstFitness = new ObservableCollection<DataPoint>();
this.AverageFitness = new ObservableCollection<DataPoint>();
Task.Run(() => program.Run(null));
}
private void Refresh()
{
this.BestFitness.Clear();
}
private void Algorithm_EvolutionEnded(object sender, EventArgs e)
{
EvolutionEndedEventArgs args = (EvolutionEndedEventArgs)e;
BestFitness.Add(new DataPoint(count, args.BestFitness));
WorstFitness.Add(new DataPoint(count, args.WorstFitness));
AverageFitness.Add(new DataPoint(count, args.AverageFitness));
InvalidateFlag++;
}
}
Do I need to do anything else?
Upvotes: 1
Views: 1693
Reputation: 45931
I have created this small example to show how to update the graphic at runtime. I hope it helps!
ViewModel:
using System;
using System.Timers;
using OxyPlot;
using OxyPlot.Series;
namespace WpfApp1
{
public class MainViewModel
{
private LineSeries lineSeries;
private int count;
public MainViewModel()
{
this.MyModel = new PlotModel { Title = "Example 1" };
//this.MyModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 0.1, "cos(x)"));
//this.MyModel.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 0.1, "sin(x)"));
lineSeries = new LineSeries();
lineSeries.LineStyle = LineStyle.Solid;
lineSeries.StrokeThickness = 2.0;
lineSeries.Color = OxyColor.FromRgb(0, 0, 0);
this.MyModel.Series.Add(lineSeries);
Timer timer = new Timer(1000);
timer.Elapsed += Timer_Elapsed;
timer.Start();
count = 0;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
lineSeries.Points.Add(new DataPoint(count, Math.Pow(count, 2)));
this.MyModel.InvalidatePlot(true);
count++;
}
public PlotModel MyModel { get; private set; }
}
}
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:oxy="http://oxyplot.org/wpf" x:Class="WpfApp1.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<oxy:PlotView Model="{Binding MyModel}" />
</Grid>
</Window>
Upvotes: 0
Reputation: 11399
With
this.BestFitness = new ObservableCollection<DataPoint>();
...
you are replacing the complete ItemsSource
of the plot. Since there is no notification of the view by calling RaisePropertyChangedEvent
afterwards, the bound plot will not recognize the change and the plot will not update it's points.
There are two possible solutions:
1. Use the INotifyPropertychanged by calling RaisePropertyChangedEvent
after replacing the collection. Therefore
public ObservableCollection<DataPoint> BestFitness { get; set; }
should be extended to
private ObservableCollection<DataPoint> _BestFitness;
public ObservableCollection<DataPoint> BestFintess
{
get
{
return _BestFitness;
}
private set
{
_BestFitness = value;
RaisePropertyChangedEvent(nameof(BestFintess));
}
}
2. Don't replace the whole ObservableCollection. Simply clear the existing collections and use them again. This means use
this.BestFitniss.Clear();
instead of
this.BestFitness = new ObservableCollection<DataPoint>();
Both solutions notifying the view about changes and the plot will update it's points without using the InvalidateFlag
.
Note that it is required to use the UI thread to change the items of an ObservableCollection
as described in this question. Since you are using an other thread to add values invoking the UI like
Application.Current.Dispatcher.BeginInvoke(() =>
{
BestFitness.Add(new DataPoint(count, args.BestFitness));
});
is required.
Upvotes: 2