Link to home
Start Free TrialLog in
Avatar of San24
San24

asked on

Undo Changes DataGridView

Experts,

I`ve spent too much time to figure out a way to undo changes [Delete Row, Add Row, Edit Cells] in a DataGridView. I have context menu with an "Undo" option.

The DataSource for the DataGridView is a regular List object.

I`m using C# .NET 3.5 VS2008

//Conext Menu
  //Edit Row Context Menu
        private void EditRowCM_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            switch (e.ClickedItem.Name)
            {
                case "AddRowTSMI":
                    AddMotRow();
                    break;

                case "DeleteRowTSMI":
                    DeleteMotRow();
                    break;

                case "DeleteSelectedTSMI":
                    DeleteSelected();
                    break;

                case "UndoEditTSMI":
                    UndoEdit();
                    break;

                default:
                    break;
            }
        }

    private void DeleteRow()
        {
            if (this.GridView.SelectedRows.Count > 0)
            {
                if (this.GridView.SelectedRows[0].Index == 0)
                {
                    MessageBox.Show("Cannot edit the first Row");
                }

                else if (this.GridView.SelectedRows[0].Index == this.GridView.Rows.Count - 1)
                {
                    MessageBox.Show("Cannot edit the last Row");
                }

                else
                {
                    this.GridView.Rows.RemoveAt(this.GridView.SelectedRows[0].Index);
                }
            }

            else
            {
                MessageBox.Show("No Row Selected - Or Select the full Row");
            }
        }

        private void DeleteSelected()
        {
            foreach (DataGridViewCell Cell in this.GridView.SelectedCells)
            {
                if (Cell.ColumnIndex == 0)
                {
                }

                else if (Cell.RowIndex == 0)
                {
                }

                else if (Cell.RowIndex == this.GridView.RowCount - 1)
                {
                }

                else
                {
                    Cell.Value = null;
                }
            }
        }

        private void UndoEdit()
        {
                 //What goes in here?
        }

One of the ideas I had was to create a temporary BindingSource which will hold all the information before the edit. When the user clicks the Undo contect menu, the temporary bindingsource becomes the source for the GridView, giving it the original information. Didn`t work!

Any guidance is greatly appreciated.
Avatar of graye
graye
Flag of United States of America image

Take a look at the CancelEdit method of the DataGridView
http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.canceledit.aspx 
Avatar of Bob Learned
I don't think that CancelEdit would begin to approach a solution for an undo operation.  You would need to monitor for deleted list objects, added list objects, and modified objects.

You could have classes like this:


internal class UndoManager
{

    private Stack<UndoAction> _undoStack;

    public void Push(UndoAction action)
    {
        _undoStack.Push(action);
    }

    public UndoAction Pop()
    {
        if (_undoStack.Count == 0)
            throw new InvalidOperationException("Stack empty.");

        return _undoStack.Pop();
    }
}

internal class UndoAction
{

    public enum ChangeMode
    {
        Add,
        Delete,
        Modify,
    }

    public UndoAction(object trackObject, ChangeMode change)
    {
        this.TrackObject = trackObject;
        this.Change = change;
    }

    public object TrackObject { get; private set; }
    public ChangeMode Change { get; private set; }
}

Open in new window

Avatar of San24
San24

ASKER

     Can I do something like this? I was thinking this would work.

       public BindingSource BSTemp = new BindingSource();

        private void DeleteMotRow()
        {
            BSTemp = BS;                  //Make a Temp Binding Source before the Edit
            //Code to Delete the rows
         }

       private void UndoEdit()
        {
            MotGridView.DataSource = null;
            MotGridView.DataSource = BSTemp;  
            BS = BSTemp;
        }
A BindingSource doesn't store any state information, so making a temporary reference, just makes another copy of the BindingSource, not the underlying data.
Avatar of San24

ASKER

I didn`t know that. That helps a lot. Maybe I could use the same idea and make a Temp List? What do you think about that idea?
I think that idea sounds good on the surface, but I prefer a more surgical approach.  You might want to allow multiple undo operations, and you would need to keep multiple copies of the list for each undo action.  That kind of operation would require a considerable amount of memory, versus keeping track of individual object changes.
CancelEdit only affects the current cell....
Avatar of San24

ASKER

@TheLearnedOne - Let me try your approach and get back. I might need some time.
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial