object oriented – DataGridView Undo/Redo Manager in C#

While answering this code review question, I proposed a solution with List<DataTable>. I am trying to encapsulate this data structure into a “DataGridView Undo/Redo Manager” class DataGridViewManager and there are several build-in methods including IsUndoable, IsRedoable, Push, Redo, Undo, etc. The following methods are implemented.

  • IsUndoable method handles the process for checking if the previous states is available.
  • IsRedoable method handles the process for checking if the next states is available.
  • Push method handles the process for pushing new states.

The experimental implementation

The experimental implementation of DataGridViewManager is as below.

class DataGridViewManager
{
    Stack<DataTable> dtStack = new Stack<DataTable>();
    private int RecordIndex = 0;

    public DataGridViewManager()
    { }

    public DataGridViewManager Push(DataTable dataTable)
    {
        ClearUnnecessaryHistory();
        this.dtStack.Push(dataTable);
        return this;
    }

    public DataTable Redo()
    {
        return dtStack.ToList()(--RecordIndex);
    }

    public DataTable Undo()
    {
        return dtStack.ToList()(++RecordIndex);
    }

    public DataTable GetCurrentState()
    {
        return dtStack.ElementAt(RecordIndex);
    }

    public bool IsUndoable()
    {
        if (RecordIndex == this.dtStack.Count - 1)
            return false;
        else
            return true;
    }

    public bool IsRedoable()
    {
        if (RecordIndex == 0)
            return false;
        else
            return true;
    }

    private void ClearUnnecessaryHistory()
    {
        while (RecordIndex > 0)
        {
            dtStack.Pop();
            RecordIndex--;
        }
        return;
    }
}

Test cases

The usage of DataGridViewManager class is like:

public partial class Form1 : Form
{
    DataGridViewManager DataGridViewManager1 = new DataGridViewManager();

    public Form1()
    {
        InitializeComponent();

        //    Construct Columns
        dataGridView1.ColumnCount = 1;
        dataGridView1.Columns(0).Name = "0";

        dataGridView1.Rows.Add(20);// Add row

        DataGridViewManager1.Push(GetDataTableFromDGV(dataGridView1));
        UpdateBtnStatus();
    }

    public DataTable GetDataTableFromDGV(DataGridView dgv)
    {
        var dt = new DataTable();

        foreach (DataGridViewColumn column in dgv.Columns)
        {
            dt.Columns.Add(column.Name);
        }

        object() cellValues = new object(dgv.Columns.Count);

        foreach (DataGridViewRow row in dgv.Rows)
        {
            for (int i = 0; i < row.Cells.Count; i++)
            {
                cellValues(i) = row.Cells(i).Value;
            }
            dt.Rows.Add(cellValues);
        }
        return dt;
    }

    public void datatablaToDataGrid(DataGridView dgv, DataTable datatable)
    {
        for (int i = 0; i < datatable.Rows.Count; i++)
        {
            for (int j = 0; j < datatable.Columns.Count; j++)
            {
                dgv.Rows(i).Cells(j).Value = datatable.Rows(i)(j).ToString();
            }
        }
    }

    private void UpdateBtnStatus()
    {
        btn_Redo.Enabled = DataGridViewManager1.IsRedoable();
        btn_Undo.Enabled = DataGridViewManager1.IsUndoable();
        return;
    }

    private void btn_Undo_Click(object sender, EventArgs e)
    {
        datatablaToDataGrid(dataGridView1, DataGridViewManager1.Undo());
        UpdateBtnStatus();
    }

    private void btn_Redo_Click(object sender, EventArgs e)
    {
        datatablaToDataGrid(dataGridView1, DataGridViewManager1.Redo());
        UpdateBtnStatus();
    }

    private void dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)
    {
        UpdateBtnStatus();

        DataGridView dgv = (DataGridView)sender;
        int r = e.RowIndex;
        int c = e.ColumnIndex;
        if (dgv.Rows(r).Cells(c).Value != null)
        {
            string dgvResult = dgv.Rows(r).Cells(c).Value.ToString();
            string dtResult = DataGridViewManager1.GetCurrentState().Rows(r)(c).ToString();
            if (dgvResult != dtResult)
            {
                DataGridViewManager1.Push(GetDataTableFromDGV(dataGridView1));
            }
        }
        UpdateBtnStatus();
    }
}

All suggestions are welcome.