2

Long ago I built a trace class to manage a list of measurement points:

public class TraceXYZ : CollectionBase, ICloneable { … }

Much later, after TraceXYZ was used pervasively throughout my software, I started using the excellent graphing package, ZedGraph. So, I added a TraceXYZ method .toLineItem() that returned a LineItem compatible with the zedGraph GraphPane‘s CurveList property.

As I increasingly use this feature I’m discovering it can be rather clunky in applications where I need to update a TraceXYZ object and have the modified values shown in a zedGraph pane. As it currently exists, I have to keep track of the TraceXYZ’s associated LineItem in the CurveList, remove it and then create and add a new LineItem reflecting the modified TraceXYZ.

Far preferable would be a capability allowing CurveList operate directly on a TraceXYZ, but since I already inherit from CollectionBase, I can’t also inherit from a LineItem too. More, there is no associated interfaces I can find (such as ILineItem) in zedGraph that would allow me to implement the functionality.

Finally, I recoded the guts of TraceXYZ to use a list of zedGraph PointPair objects and then tried to copy them by reference into a LineItem so modifications to the original point in TraceXYZ would be reflected in the zedGraph pane. This didn’t work, however. Apparently the CurveList.AddPoint() method does a clone of points and they are no longer referenced to the originals in TraceXYZ.

So… Any thoughts how I could accomplish something like this? Ideally, as an example I’d like to be able to do something like the following:

TraceXYZ trace = new TraceXYZ();

... populate trace points ...


pane.CurveList.Add( trace );
zedGraph.AxisChange( );
zedGraph.Refresh( );

trace[index].Y = aNewValue;

zedGraph.AxisChange( );
zedGraph.Refresh( );

and have the new value of Y be reflected in the zedGraph pane.

(Since I use TraceXYZ extensively, reforming it to inherit from LineItem a significant, risky effort that is only a last resort.)

Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
mroyer
  • 65
  • 6
  • How long is `TraceXYZ`'s code? – Thomas Ayoub Sep 06 '16 at 15:04
  • @Thomas It's a class that is a collection of PointXYZ objects. Combined TraceXYZ and PointXYZ are about 3000 lines of code. I have a pile of math, overloaded operators, conversions to other types and so on built into the classes. – mroyer Sep 07 '16 at 11:28

2 Answers2

1

Instead of making TraceXYZ derive from a LineItem, I would use another approach:

A LineItem is associated with a IPointList using its Points property. Each time the Invalidate method is invoked on the ZedGraph control (or the GraphPane object), ZedGraph queries the IPointList to get the whole list points to display.

This is one of the main features that make ZedGraph unique.

That means you can make your custom collection class inherits from IPointList and associate it directly to your LineItem. Basically, you will have to override a Count property, a Indexer that returns a PointPair object, a Clone function that can be fooled by a { return this; }, and that is it.

No need to populate a point list anymore, the ZedGraph will have it directly from your own collection.

As an example, I have designed a GenericPointList that allows to project almost evey kind of list as a IPointList, using lambda selectors to retrieve X and Y values. So you can see how easy it is to implement its own IPointList.

Its likely you only have to implement the indexer:

public class TraceXYZ : CollectionBase, ICloneable, IPointList {
    public PointPair this[int index] {
        get {
            // return new PointPair(double X, double Y);
            return new PointPair(...
        }
    }

    public object Clone() {
        // Apparently, your objects implements ICloneable, so you likely implemented Clone already
    }

    public int Count {
        get {
            // Your objects implements CollectionBase, so Count is likely already implemented
        }
    }
}

At some point you will have in some initialization code:

TraceXYZ trace = new TraceXYZ();
...
var li = new LineItem(...
li.Points = TraceXYZ;
pane.CurveList.Add(li);

Then at some time:

trace[index].Y = aNewValue;
zedGraph.AxisChange( );
zedGraph.Refresh( );
Larry
  • 17,605
  • 9
  • 77
  • 106
  • Thanks for this Larry(!), I will give this technique a go and report back on how it worked out. – mroyer Oct 03 '16 at 12:09
  • Uurgh... I already implement a public PointXYZ this[int index] so I can't also implement public PointPair this[int index]. Ironically, PointXYZ inherits from PointPair, but is highly featured overloading many operators and implementing many conversions. – mroyer Oct 03 '16 at 12:39
  • Argh. May be The GenericPointList could help then ? li.Points = new GenericPointList(TraceXYZ, ...) – Larry Oct 03 '16 at 12:42
  • If pointXYZ inherits from PointPair, I suppose you can use it as it. Some other specifics pointLists in ZedGraph sends specifics Points objects too :) – Larry Oct 03 '16 at 12:50
  • 1
    So, I went with an explicit implementation and it appears to be working!! (e.g, PointPair IPointList.this[int idx] ). More testing needed to prove it's really working. Also, I believe the line li.Points = TraceXYZ; from the example above should be li.Points = trace; Thanks again Larry, this is awesome! Will make so many plots more efficient. – mroyer Oct 03 '16 at 12:54
  • 1
    Frick... I can't up-vote your answer because my reputation is so low... sorry for that. – mroyer Oct 03 '16 at 12:56
  • Don't worry about that ! I am glad I could help ! – Larry Oct 03 '16 at 13:11
1

To elaborate on Larry's answer, the following is how I implemented his solution:

Since the TraceXYZ class already had an indexer, I implemented the indexer for a PointPair as an explicit implementation:

    PointPair IPointList.this[int idx] {
        get {
            PointXYZ point = (PointXYZ)List[idx];
            return point as PointPair;
        }
    }

I also extended the ZedGraph CurveList class as follows. This allows for handling other auxiliary properties and so on (like trace color).

    public static void Add( this CurveList curves, TraceXYZ trace ) {
        LineItem li = new LineItem("");
        li.Points = trace;
        li.Color = trace.plotInfo.color;

        ...

        curves.Add( li );
        return;
    }

This allows the calling context to directly add the TraceXYZ class object to the ZedGraph curve list:

TraceXYZ trace = new TraceXYZ();

... populate the trace and set auxiliary properties here ...

pane.CurveList.Add(trace);

... at some later time ...

trace[index].Y = aNewValue;
zedGraph.AxisChange( );
zedGraph.Refresh( );

and voila, the pane plot is updated.

Thanks again Larry, -Mark R.

mroyer
  • 65
  • 6