WPF LineSeries not refreshing

Mar 25, 2014 at 6:18 PM
Edited Mar 25, 2014 at 6:19 PM
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.
Coordinator
Mar 25, 2014 at 7:56 PM
  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.
Mar 25, 2014 at 9:54 PM
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)?
Mar 25, 2014 at 10:43 PM
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...