Skip to main content Skip to footer

Implementing Undo and Redo in C1Flexgrid for Winforms

While working with an editor control, everyone expects the availability of Undo/Redo operations. However, most of the known grid controls lack this functionality. Suppose user made changes to 20 cells and then needs them to revert to their original values or revert them to the changed state (if changes were made already). There is no direct way. When the grid is bound, you may handle the changed/original states of Row items using ADO.NET concepts. However, there is no such option when the grid is operating in unbound mode. Troublesome as this may sound, its really quite easy to implement with a bit of code. Lets see how we can do this with the help of C1FlexGrid for Winform. While modifying cell values in the grid, knowing that we may need the original values later, we need to save them somewhere. Even better approach is to save the row/col indices along with the cell values. For this, we'll use a class DataList which will store row index, col index, and the original cell value.

public class DataList  
{  
     private int _row;  
     private int _col;  
     private string _text;  

     public int Row  
     {  
         get { return _row; }  
         set { _row = value; }  
     }  

     public int Col  
     {  
         get { return _col; }  
         set { _col = value; }  
     }  

     public string Text  
     {  
         get { return _text; }  
         set { _text = value; }  
     }  
 }  

To proceed further, add few rows and columns to the grid, populate it with some data. You will also need to create two objects of type DataList Class to maintain list for both undo and redo operations.


   c1FlexGrid1.Cols.Count = 5;  
   c1FlexGrid1.Rows.Count = 10;  

   for (int \_row = this.c1FlexGrid1.Rows.Fixed; \_row < this.c1FlexGrid1.Rows.Count; _row++)  
   {  
        for (int \_col = this.c1FlexGrid1.Rows.Fixed; \_col < this.c1FlexGrid1.Cols.Count; _col++)  
        {  
             this.c1FlexGrid1[\_row, \_col] = string.Format("{0},{1}", \_row, \_col);  
         }  
   }  

   list_z = new List<DataList>();  
   list_y = new List<DataList>();  
   //let the grid handle clipboard operations  
   this.c1FlexGrid1.AutoClipboard = true;  

Now once the C1Flexgrid is up and running with data, user starts editing cells, via manual input and/or pasting data in each cell. For each editing, we need to save the cell's original values to the list object i.e. list_z.



        //Save original cell values to list before a new value gets pasted  
        void c1FlexGrid1_KeyDown(object sender, KeyEventArgs e)  
        {  
            if (e.KeyData == (Keys.Control | Keys.V))  
            {  
                int row = this.c1FlexGrid1.Row;  
                int col = this.c1FlexGrid1.Col;  
                string value = this.c1FlexGrid1.GetData(row, col).ToString();  
                list_z.Add(new DataList() { Row = row, Col = col, Text = value });  
            }  
        }  

        //Add values to "list" object as grid's contents get changed.  
        void c1FlexGrid1_ValidateEdit(object sender, C1.Win.C1FlexGrid.ValidateEditEventArgs e)  
        {  
             list_z.Add(new DataList() { Row = e.Row, Col = e.Col, Text = this.c1FlexGrid1[e.Row, e.Col].ToString() });  
        }  

To handle the CTRL+Z key press for Undo operation, we subscribe to KeyUp event of C1Flexgrid and fetch the row/col indices and cell value from the last item of list_z. Now, for Redo operation, we will save the cell's current text along with its row/col indices to another list "list_y". Similarly, when user uses CTRL+Y for Redo, the reverse operation is performed and this time, prior to insertion of "modified" value (the one that was inserted while we did Undo) into the cell, we save the cell's current text along with its row/col indices to list "list_z", in the event of another "Redo".


void c1FlexGrid1_KeyUp(object sender, KeyEventArgs e)  
{  
  //Undo changes  
if ((e.KeyData == (Keys.Control | Keys.Z) && list_z.Count != 0))  
{  
           //Iterate the list object from bottom to top; and fetch row,col and text  
           //save the same value to the same row,col of the grid  
           int row = list\_z[list\_z.Count - 1].Row;  
           int col = list\_z[list\_z.Count - 1].Col;  
           string value = list\_z[list\_z.Count - 1].Text;  

           //Add new cell values to Redo list  
           list_y.Add(new DataList() { Row = row, Col = col, Text = this.c1FlexGrid1[row, col].ToString() });  

           //Save original cell value to the grid and set focus to the cell  
           this.c1FlexGrid1[row, col] = value;  
           this.c1FlexGrid1.Row = row;  
           this.c1FlexGrid1.Col = col;  

           //remove last element from the list  
           list\_z.RemoveAt(list\_z.Count - 1);  
        }  

        //Redo changes  
        else if ((e.KeyData == (Keys.Control | Keys.Y) && list_y.Count != 0))  
        {  
           int row = list\_y[list\_y.Count - 1].Row;  
           int col = list\_y[list\_y.Count - 1].Col;  
           string value = list\_y[list\_y.Count - 1].Text;  

           //Add existing values to Undo list  
           list_z.Add(new DataListZ() { Row = row, Col = col, Text = this.c1FlexGrid1[row, col].ToString() });  

           //Save original cell value to the grid and set focus to the cell  
           this.c1FlexGrid1[row, col] = value;  
           this.c1FlexGrid1.Row = row;  
           this.c1FlexGrid1.Col = col;  
           //remove last element from the list  
           list\_y.RemoveAt(list\_y.Count - 1);  
       }  
}  

Download VB Sample Download C# Sample

MESCIUS inc.

comments powered by Disqus