WPF LineSeries not refreshing

Oystein Bjorke 10 years ago updated by Ed Walker 3 years ago 1
This discussion was imported from CodePlex

NinjaTuna wrote at 2014-03-25 19:18:

I am trying to use OxyPlot for real-time data plotting, but so far I've wasted a couple of hours without result. When I create a plot in XAML and add a LineSeries with ItemsSource binding, nothing happens when the binding source is updated (PropertyChanged eventhandlers and stuff are set-up correctly). Even calling RefreshPlot(true), which even seems to be unavailable in newer releases (240 has it, 261 does not), does not help.
The only time I actually see my data plotted is when I go zooming around the plot, the data is then updated at every zoom action (middle mouse drag).

To rule out compatibility errors, I created a small testing project.
MainWindow.xaml:
<Window x:Class="OxyPlotTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:oxy="http://oxyplot.codeplex.com"
        Title="MainWindow" Height="350" Width="525"
        MouseDown="Window_MouseDown">
    <Grid>
        <oxy:Plot Name="Plot" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <oxy:Plot.Axes>
                <oxy:LinearAxis Position="Bottom" Minimum="0" Maximum="10"/>
                <oxy:LinearAxis Position="Left" Minimum="-50" Maximum="50"/>
            </oxy:Plot.Axes>
            <oxy:Plot.Series>
                <oxy:LineSeries ItemsSource="{Binding PlotData}"/>
            </oxy:Plot.Series>
        </oxy:Plot>
    </Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            PlotData = new ObservableCollection<DataPoint>();
        }
        private ObservableCollection<DataPoint> _plotData = new ObservableCollection<DataPoint>();

        public ObservableCollection<DataPoint> PlotData
        {
            get { return _plotData; }
            set { _plotData = value; OnPropertyChanged("PlotData"); }
        }
        
        int x = 0;

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
//create random datapoint on mouseclick
            Random r = new Random();
            int y = r.Next(-50, 50);
            PlotData.Add(new DataPoint(x,y));
            x++;
        }

        #region binding stuff
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }
The above code (with version 261) also does not work, a graph is only drawn when zooming. Not automatically.

I hope someone can help me, the problem is absolutely driving me mad, partly because I have seen this working in a fellow student's project.

objo wrote at 2014-03-25 20:56:

  1. OxyPlot is handling most mouse button events by default. Set a breakpoint in the event handler you created and you will see it is not being called. Try to press "shift+ctrl" and the left mouse button. This combination is not handled by OxyPlot and your event handler should be called. I think you have at least two alternatives to solve this:
    1. Use the PreviewMouseDown event instead.
    2. Unbind the left mouse button down gesture in the plot controller. This can be done by Plot.ActualController.Unbind(PlotCommands.SnapTrack); or Plot.ActualController.Unbind(OxyMouseButton.Left); (new extension method I just added)
  2. OxyPlot is not observing collection changes, so you need to call Plot.InvalidatePlot() after you have added the point to the collection.

NinjaTuna wrote at 2014-03-25 22:54:

Thanks for your response!

The event-handler definitely was being called, when you click at the edge of the window, it will fire. Also, this is specific to my test set-up, in the actual project the binding source is updated by a DispatcherTimer.

I see copying the list, then adding the new datapoint to it and finally reassigning it to the binding source does update the graph like it should and so does Plot.InvalidatePlot(). However, it does not work in my original project with the DispatcherTimer, do you think the timer itself has something to do with the inability to update the plot (being on a another thread or something like that)?

NinjaTuna wrote at 2014-03-25 23:43:

Okay, I got this working in one specific way, the DispatcherTimer seems not to be the culprit. When I have this:
private List<DataPoint> _yPoints;

        public List<DataPoint> YPoints
        {
            get { return _yPoints; }
            set
            {
                _yPoints = value;
                RaisePropertyChanged("YPoints"); //OnPropertyChanged wrapper
            }
        }
I need to reassign YPoints completely with a new list, after adding a point as follows:
YPoints.Add(new DataPoint(x, y);
YPoints = new List<DataPoint>(YPoints);
If I don't perform the copy, no update, if I manually call RaisePropertyChanged("YPoints"), no update, if I call InvalidatePlot(true) on the plot, no update. Am I making sense here? Can you explain why it only works when I copy the data to a completely new list? It seems a rather inefficient way to handle data...

@NinjaTuna, did you manage to find any alternate solutions? I had the exact same problem initiating the data update in a way that results in a chart update. Like you, it worked for me when I re-instantiate the YPoints List with:

= new List<DataPoint>(YPoints);

but I agree this doesn't seem the most elegant way to do it...