I have looked at many topics related to this topic, but I could not find or understand for datagridview table. Finally I made the undo / redo implementation myself using datatable lists. This solution is valid for small tables, I think. I don't know if it will work efficient on tables with lots of data. I will share this solution here. Maybe I will contribute to this website where I learned lots of things. Also I am waiting for comments. Is this solution stupid? or does it work? I need your comments for improvement or give up. Thanx.
Firstly we will add a DataGridView and two buttons named "Undo" and "Redo" to our form
These are codes below;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
DataTable dt = null;
List<DataTable> dtList = new List<DataTable>(); // This list for recording every movements. If we want to do undo/redo, we will get data from this list to dataGridview
public Form1()
{
InitializeComponent();
dataGridView1.Rows.Add(20);// Add row
dt = GetDataTableFromDGV(dataGridView1);// make datatable at the beginning
dtList.Clear();
dtList.Add(dt);// Beginning data is added to List
}
public DataTable GetDataTableFromDGV(DataGridView dgv)// This methot makes a DataTable from DataGridView control.
{
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;
}
int counterUndo = 2;// This counts clicked redo button
int dtCount = 0;// This keeps DataTable count
bool clickedUndo;// This checks Undo button clicked or not.
private void btn_Undo_Click(object sender, EventArgs e)
{
clickedUndo = true;
dtCount = dtList.Count;
if (dtCount - counterUndo > -1)
{
int undoIndex = dtCount - counterUndo;
datatablaToDataGrid(dataGridView1, dtList[undoIndex]);
counterUndo++;
}
}
private void btn_redo_Click(object sender, EventArgs e)
{
int redoIndex = dtCount - counterUndo + 2;
if (counterUndo > 1&&redoIndex< dtCount)
{
datatablaToDataGrid(dataGridView1, dtList[redoIndex]);
counterUndo--;
}
}
public void datatablaToDataGrid(DataGridView dgv, DataTable datatable)// This methot gets data from DataTable to DataGridView control.
{
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 dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)// This event check if the cell value is change or stay same.
{
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 = dt.Rows[r][c].ToString();
if (dgvResult != dtResult)
{
if (clickedUndo)
{
doWhenClickedUndo(counterUndo, dtList);
}
dt = GetDataTableFromDGV(dataGridView1);
dtList.Add(dt);
counterUndo = 2;
}
}
}
private void doWhenClickedUndo(int _counterUndo, List<DataTable> _dtList)
{
if (_counterUndo != 2)
{
int f = counterUndo - 2;
int lastIndex = _dtList.Count - 1;
int i = lastIndex;
do
{
_dtList.RemoveAt(i);
i--;
} while (i > lastIndex - f);
}
clickedUndo = false;
}
}
}