23

GUI for the program

Oxyplot graphs 13 points which are derived from the 6 user input text boxes. The values in the text boxes are held in public variables in the MainWindow.xaml.cs class. The variables are updated when the user presses enter in the text box. How would I make the refresh button refresh the graph.

private void RefreshButton_Click(object sender, RoutedEventArgs e)
        {
            //Refresh The Graph
        }

I think that this would be done using the

PlotModel.RefreshPlot() 

method, but I am not sure how to implement it because of Oxyplot's poor documentation.

Jkallus
  • 431
  • 1
  • 4
  • 16
  • 7
    Sorry for being two years too late, but the best way is not given here. To do this properly, use oxyplot.wpf, DON'T use PlotModel - use Plot, and bind your data to the ItemsSourceProperty - no need to call Invalidate!! It updates as the data updates in real time. – AndrewBenjamin Jul 13 '16 at 17:59
  • Wow. I can't believe this is still active. This project was so long ago. – Jkallus Jul 13 '16 at 18:04
  • @AndrewBenjamin It looks like there is no more `Plot` in current version (2022) – Kamil Mar 10 '22 at 22:25
  • @Jkallus It is still active because you did not accept any answer. – Kamil Mar 10 '22 at 22:27

7 Answers7

40

I just updated to a new version of OxyPlot via NuGet. I'm using OxyPlot.Wpf v20014.1.277.1 and I think you now need to call InvalidatePlot(bool updateData) on the PlotModel instead of RefreshPlot (which is no longer available). I tested this in my sample code and it worked as expected.

If you want to refresh the plot and update the data collections, you need to pass true to the call:

PlotModel.InvalidatePlot(true)
ruttopia
  • 576
  • 5
  • 4
  • Slightly off-topic, but would you think using MVVM for a very little project which makes use of some Oxy-plotting makes sense? Or `Plot.Model = ...` and `InvalidatePlot` are good enough? I realize it's a tricky question, but what's your first thought? – PawelP Oct 21 '14 at 19:48
  • It's not necessary to pass `true` as an argument, it's the default value of the parameter. Also, I confirm that `RefreshPlot` is no longer available in new releases. – Ondrej Janacek Nov 04 '14 at 12:01
7

Give x:Name to OxyPlot instance in XAML:

<oxy:Plot x:Name="Plot1"/>

and on button click handler, refresh like this:

private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
   Plot1.RefreshPlot(true);
}
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
7

The cleanest way I've found to get "sort of" auto-update is reacting to CollectionChanged on the collection that is LineSeries' ItemsSource.

In ViewModel:

ObservableCollection<DataPoint> Data { get; set; } 
    = new ObservableCollection<DataPoint>();

public PlotModel PlotModel
{
    get { return _plot_model; }
    set
    {
        _plot_model = value;
        RaisePropertyChanged(() => PlotModel);
    }
}
PlotModel _plot_model;

// Inside constructor:
Data.CollectionChanged += (a, b) => PlotModel.InvalidatePlot(true);
heltonbiker
  • 26,657
  • 28
  • 137
  • 252
  • Great answer. This is how binding should be done. In mine I made the ViewModel a BindableObject and called OnPropertyChanged() instead of RaisePropertyChanged(). – DrCJones Jul 29 '17 at 05:43
  • @DrCJones the method that raises the `PropertyChanged` event depends on the MVVM library you are using. With MvvmLight, the ViewModels usually inherit from `ViewModelBase`, which has the `RaisePropertyChanged` method. Other libraries might be different. – heltonbiker Aug 17 '17 at 23:00
  • Good point @heltonbiker ! I failed to mention I was in a Xamarin project. – DrCJones Aug 22 '17 at 19:58
6

In the current OxyPlot.Wpf (1.0.0-unstable1983) you have two options:

  1. Bind the Series.ItemsSource property from XAML to a collection in your viewmodel and exchange the whole collection, when you need an update. This also allows for concurrent async updates with larger data sets.
  2. Bind the Plot.InvalidateFlag property of type int to your viewmodel and increment whenever you need an update. I haven't tested this approach, though.

The following code illustrates both options (pick one). XAML:

<oxy:Plot InvalidateFlag="{Binding InvalidateFlag}">
    <oxy:Plot.Series>
        <oxy:LineSeries ItemsSource="{Binding DataSeries}" />
      </oxy:Plot.Series>
 </oxy:Plot>

Updates on the ViewModel:

private async Task UpdateAsync()
{
    // TODO do some heavy computation here
    List<DataPoint> data = await ...

    // option 1: Trigger INotifyPropertyChanged on the ItemsSource.
    //           Concurrent access is ok here.
    this.DataSeries = data; // switch data sets

    // option 2: Update the data in place and trigger via flag
    //           Only one update at a time.
    this.DataSeries.Clear();
    data.ForEach(this.DataSeries.Add);
    this.InvalidateFlag++;
}
Patrick Stalph
  • 792
  • 1
  • 9
  • 19
4

After having the same question with the same issue, it would seem that the only working solution (at least to my point of view) is as followed :

PlotView.InvalidatePlot(true)

Doing so, after updating one or multple Series do refresh your PlotView.

The refresh rate depends on how often, or at which rate your serie(s) is/are updated.

Here is a code snippet (on Xamarin Android but should work anyway) :

PlotView resultsChart = FindViewById<PlotView>(Resource.Id.resultsChart);
PlotModel plotModel = new PlotModel
{
    // set here main properties such as the legend, the title, etc. example :
    Title = "My Awesome Real-Time Updated Chart",
    TitleHorizontalAlignment = TitleHorizontalAlignment.CenteredWithinPlotArea,
    LegendTitle = "I am a Legend",
    LegendOrientation = LegendOrientation.Horizontal,
    LegendPlacement = LegendPlacement.Inside,
    LegendPosition = LegendPosition.TopRight
    // there are many other properties you can set here
}

// now let's define X and Y axis for the plot model

LinearAxis xAxis = new LinearAxis();
xAxis.Position = AxisPosition.Bottom;
xAxis.Title = "Time (hours)";

LinearAxis yAxis = new LinearAxis();
yAxis.Position = AxisPosition.Left;
yAxis.Title = "Values";

plotModel.Axes.Add(xAxis);
plotModel.Axes.Add(yAxis);

// Finally let's define a LineSerie

LineSeries lineSerie = new LineSeries
 {
    StrokeThickness = 2,
    CanTrackerInterpolatePoints = false,
    Title =  "Value",
    Smooth = false
  };
plotModel.Series.Add(lineSerie);
resultsChart.Model = plotModel;

Now, whenever you need to add DataPoints to your LineSerie and to updated automatically the PlotView accordingly, just do as followed :

resultsChart.InvalidatePlot(true);

Doing so will automatically refresh your PlotView.

On a side note, the PlotView will also be updated when an event occurs such as a touch, a pinch to zoom, or any kind of UI-related events.

I hope I could help. I had trouble with this for a very long time.

Mackovich
  • 3,319
  • 6
  • 35
  • 73
3

Exists three alternatives how refresh plot (from OxyPlot documentation):

  • Change the Model property of the PlotView control
  • Call Invalidate on the PlotView control
  • Call Invalidate on the PlotModel
honzakuzel1989
  • 2,400
  • 2
  • 29
  • 32
1

Another two years later... this solution works for me, because I have no oxyplot models and I´m missing some of the named functions from above.

code behind:

public partial class LineChart : UserControl
{
    public LineChart()
    {
        InitializeComponent();

        DataContext = this;
        myChart.Title = "hier könnte Ihr Text stehen!";

        this.Points = new List<DataPoint>();
        randomPoints();
    }


    public IList<DataPoint> Points { get; private set; }

    public void randomPoints()
    {
        Random rd = new Random();
        String myText = "";

        int anz = rd.Next(30, 60);

        for (int i = 0; i < anz; i++)
            myText += i + "," + rd.Next(0, 99) + ";";

        myText = myText.Substring(0, myText.Length - 1);
        String[] splitText = myText.Split(';');

        for (int i = 0; i < splitText.Length; i++)
        {
            String[] tmp = splitText[i].Split(',');
            Points.Add(new DataPoint(Double.Parse(tmp[0].Trim()), Double.Parse(tmp[1].Trim())));
        }

        while (Points.Count > anz)
            Points.RemoveAt(0);

        myChart.InvalidatePlot(true);
    }
}

To update your data don't exchange the whole IList, rather add some new DataPoints to it and remove old ones at position 0.

XAML:

<UserControl x:Class="UxHMI.LineChart"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:UxHMI"
         xmlns:oxy="http://oxyplot.org/wpf"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="Container" Background="White">
    <oxy:Plot x:Name="myChart" Title="{Binding Title}" FontFamily="Bosch Sans Medium" Foreground="#FF0C6596" FontSize="19" Canvas.Left="298" Canvas.Top="32" Background="AliceBlue" Margin="0,0,10,0">
        <oxy:Plot.Series>
            <oxy:LineSeries x:Name="ls" Background="White" ItemsSource="{Binding Points}" LineStyle="Solid" Color="ForestGreen" MarkerType="None" MarkerSize="5" MarkerFill="Black">

            </oxy:LineSeries>
        </oxy:Plot.Series>
    </oxy:Plot>
    <Button x:Name="button" Content="Random" HorizontalAlignment="Left" Margin="0,278,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
</Grid>

important are the x:Name="myChart" and ItemsSource="{Binding Points}"

I hope this is useful for someone out there

prototype0815
  • 592
  • 2
  • 7
  • 24