Search code examples
c#winformsdesign-patternsmschart

Undo operation for series in chart


I want to restore previous series after changing some Yvalues[0] multiple times in the chart. So I have implemented undo operation using memento pattern.

But it's not working and there is no error while running. Below is the originator, memento and caretaker.

public class Originator
{

    private Series _series = new Series();

    public Originator(Series series)
    {
        _series = series;
    }
    public Series OSeries
    {
        get
        {
            return _series;
        }
        set
        {
            _series = value;
        }
    }

    public Memento SaveSeries()
    {
        return new Memento(_series);
    }

    public void RestoreSeries(Memento m)
    {
        this._series = m.MMseries;
    }
 }

public class Memento
{
    private Series _series =new Series();

    public Memento(Originator org)
    {
        this._series = org.OSeries;
    }
    public Memento(Series mseries)
    {
        _series = mseries;
    }

    public Series MMseries
    {
        get
        {
            return _series;
        }
        set
        {
            _series = value;
        }
    }
}
public class Caretaker
{
    //  private static Caretaker _instance;


    //  public Caretaker() { }

    private Memento chartMemento;
    public Memento Memento
    {
        get
        { return chartMemento; }

        set
        {  chartMemento = value; }

    }
}

Created objects in public partial class Form1 : Form

Originator _org;
Caretaker taker = new Caretaker();

While loading the chart from a file, open_file function contains this code:

_org = new Originator(global_series); //For undo/redo operation
while (chart1.Series.Count > 0) { chart1.Series.RemoveAt(0); }

chart1.Series.Add(global_series);
taker.Memento = _org.SaveSeries();

For Undo_button:

_org.RestoreSeries(taker.Memento);
while (chart1.Series.Count > 0) { chart1.Series.RemoveAt(0); }
global_series = _org.OSeries;
chart1.Series.Add(global_series);

Solution

  • Series is a reference variable.

    Therefore all changes you make will be be to the series and all references you have will point to the same (original) series.

    Fancy patterns, useful as they might be can't replace elementary knowledge. To allow undo/redo you need a copy of the data, not of the reference!

    Note the Series doesn't have a Clone method.

    So you need to do it yourself; in your case this boils down to copying the Points..

    But for a more general case you would want to copy other series properties, most notable the ChartType but basically everyting you did to prepare the Series. Maybe create it in a factory pattern..?

    But also note the DataPoint does have a Clone method. So you may change your Originator constructor like so:

    public Originator(Series series)
    {
        //_series = series;
        foreach (var dp in series.Points) _series.Points.Add(dp.Clone());
    }
    

    and things will start to work as expected. But you will need to do the restoring like this as well or else you would point to the saved series and the next changes would change it instead of the chart series..:

    public void RestoreSeries(Memento m)
    {
        // this._series = m.MMseries;
        this._series.Points.Clear();
        foreach (var dp in m.MMseries.Points) this._series.Points.Add(dp);
    }
    

    Btw: To clear all series use chart.Series.Clear() !