OxyPlot paint Exception : XAxis not defined

Jul 14, 2014 at 4:51 PM
Edited Jul 14, 2014 at 4:52 PM
Thanks for Oxyplot!
I had this bug (OxyPlot paint Exception : XAxis not defined ) in my code. If I scale the plot with mouse it goes away but I can always see it when something is newly plotted. I did add axes to my Model.

Here's the code
newPlot.DrawGraph(graphMain);// this calls DrawGraph function

public void DrawGraph(OxyPlot.WindowsForms.PlotView iptGraph)
        {
            var tmp=new OxyPlot.Series.LineSeries();
            tmp.StrokeThickness= 0.4;
            for (int i = 0; i < _resampledPoints.Count(); i++)
            {
                tmp.Points.Add(_resampledPoints[i]);
            }
            iptGraph.Model.Series.Add(tmp);
        }
Thanks a lot!
Jul 14, 2014 at 6:08 PM
So just a few things, first of all you can use a foreach loop or even just AddRange over what you're currently doing (and it's less bug prone):
foreach (var p in _resampledPoints)
    tmp.Points.Add(p);
Alternatively:
tmp.Points.AddRange(_resampledPoints);
Both require _resampledPoints to be an IEnumerable<DataPoint> collection.

Second, please provide the code you use to assign the horizontal axis (or just all of them) to the PlotModel, can't really help when you're just adding points to a series =P.
Jul 14, 2014 at 6:27 PM
Edited Jul 14, 2014 at 6:48 PM
Thank you for your suggestion! I'm pretty new to C# :)
These are the codes that I use to add axies to the PlotModel (graphMain).
        OxyPlot.Axes.LinearAxis XAxis = new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Bottom, MinimumPadding = 10, MaximumPadding = 15, AbsoluteMinimum = 0,Maximum=80};
        OxyPlot.Axes.LinearAxis YAxis = new OxyPlot.Axes.LinearAxis { Position = OxyPlot.Axes.AxisPosition.Left, MinimumPadding = 10, MaximumPadding = 15, AbsoluteMinimum = 0, Maximum=400};
            graphMain.Model.Axes.Add(YAxis);
            graphMain.Model.Axes.Add(XAxis);
If I try to define plotmodel under DrawGraph function this exception actually disappears. But I need to add multiple lines to PlotModel so I can't really do that.
Jul 14, 2014 at 7:11 PM
Edited Jul 14, 2014 at 7:16 PM
Just out of curiosity, why are you setting a min and max for the axes? They will actually automatically resize based on the points in the series collection, so unless you want to see a certain range it's better to leave them to do the work instead. I'd suggest setting their min and max until after you add data to the series (same with the padding, it'll calculate that stuff itself). Also, it's probably best to use an axis Key, and a YAxisKey on the series. Just as a quick example:
using System.Linq;

using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;

// cut out namespace and class definitions to save indention levels
private void InitializeChart() 
{
    // didn't see it in your code, but you should initialize your own PlotModel and assign it to the
    // PlotView before using PlotView.Model
    var model = new PlotModel();

    model.Axes.Add(new LinearAxis
    {
        Key = "xAxis",
        Position = AxisPosition.Bottom,
        Title = "X Axis"
    });

    // just to show another way of using the new axis
    var yAxis = new LinearAxis 
    {
        Key = "yAxis",
        Position = AxisPosition.Left,
        Title = "Y Axis"
    };

    // the series title will show up in the legend
    var series = new LineSeries {Title = "Resampled Points", YAxisKey = "yAxis"};
    foreach (var p in _resampledPoints)
        series.Points.Add(p);

    // two examples of how to get the axes, highly suggest LINQ if you're in dotNet 3.0+
    model.Axes.OfType<LinearAxis>().First(a => a.Key == "xAxis").AbsoluteMinimum = 0;
    model.Axes.OfType<LinearAxis>().First(a => a.Key == "xAxis").Maximum = 80;

    yAxis.AbsoluteMinimum = 0;
    yAxis.Maximum = 400;

    // I'm guessing your graphMain is your PlotView
    graphMain.Model = model;
}
Note: this might have some errors, just put it together quickly in Sublime Text from memory.

Not really sure what you mean by defining it under the drawgraph function, you'll have to elaborate.

Also, welcome to C#! I've been in this space for around 5 months now, came from Python and Java myself, mainly for work. Quite enjoying the language (don't really like the framework restrictions though, being a linux guy), hope you like it here! I'm currently playing around with F# myself, really falling for functional programming lately.
Jul 14, 2014 at 7:30 PM
Edited Jul 14, 2014 at 7:37 PM
Thanks for your response. I did initialized model. But my problem is, I am passing PlotView to another function to add a line series. Then the exception comes. If I put add line series at where i define the Model, the bug disappears. I don't understand why.

Thanks!
public void DrawGraph(OxyPlot.WindowsForms.PlotView iptGraph)//this function will add line series to PlotView
        {
            var tmpModel = new OxyPlot.PlotModel();
            int xpixels = iptGraph.Width;
            double xratio = (double)_points.Count() / (double)xpixels;
            var tmp = new OxyPlot.Series.LineSeries();
            _resampledPoints.Clear();
            for (int i = 0; i < xpixels; i++)//set resampled points
            {
                int idx = (int)(i * xratio);
                _resampledPoints.Add(new OxyPlot.DataPoint(_points[idx].X+_xOffset, _points[idx].Y+_yOffset));
                if (_xMax < _points[idx].X) _xMax = _points[idx].X;
                if (_yMax < _points[idx].Y) _yMax = _points[idx].Y;
            }
            tmp.StrokeThickness= 0.4;
            tmp.Points.AddRange(_resampledPoints);
            iptGraph.Model.Series.Add(tmp); //Here I add tmp line to PlotView graph
        }

Jul 14, 2014 at 7:54 PM
Tip: put a space and "C#" after the triple back tick for code highlighting =). DrawGraph sounds a bit misleading, since you're just adding data to the graph, not actually effecting how it will draw itself. I'd consider moving the functionality of adding points to series into the class that handles the PlotView directly over passing the PlotView around like that (or at least pass the PlotModel instead, don't need the view).

For example in your chart class you can have an InitializeChart() method that sets up basic axes and series, (or for more dynamic control, InitializeChart(), AddSeries(various series info arguments), and AddAxis(various axes info arguments), and some kind of AddPoints(object tag, IEnumerable<DataPoints>) to add data to the series, using the tag to look up the correct one with linq (view.Model.Series.First(s => s.Tag == tag), or Single if you're only expecting one result).

I'm actually doing something similar to this in a simplified custom control I'm making for work, if you need a more comprehensive example I'd be happy to share.
Jul 14, 2014 at 8:13 PM
Thanks for your suggestion! I finally fixed the bug. What I did is every time I plot the data set, I create a new model and add all the lines to the model. Then I assign it to PlotView. I'm guessing I should not add line separately? This will require extra work because each time a new line is added, I need to plot old ones together. But I guess it's just linear time so I'll leave it like that.
I'll try the Linq! It looks very handy.

Thanks again!
Jul 14, 2014 at 8:19 PM
Hmm that's a good point, kind of annoyed I missed that lol, yea you're remaking and assigning the PlotModel everytime that function is called. It'd probably be better to add the series and points separately, that way you can separate adding a new line series and adding data to an already existing line series on the plot. Also, if you don't already know, you need to make the PlotView refresh manually, calling Model.InvalidatePlot(bool), boolean being true/false depending on if you want the axes to recalculate and resize themselves based on new data in the line series (this is actually called every time you pan or zoom automatically).
Jul 14, 2014 at 9:05 PM
It's great to know the InvalidatePlot thing! I was required to add zoom functionality to zoom specific part according to user input.
Mar 17, 2015 at 2:32 PM
I had this problem and found that adding Model.InvalidatePlot(bool) after the code which was clearing old line-series from the plot model did the trick.
Thanks!