简体   繁体   中英

How to refresh oxyplot plot when data changes

该程序的GUI

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.

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)

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);
}

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);

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.

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++;
}

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM