最近运维提出要实现撤销与反撤销的功能:
背景与图片示例。运行图如下:
针对上图,右键可以实现的如下功能
针对右键的功能可以对dev中的GridControl插入、剪切、删除等操作。操作过程中可能出错,需要返回上一步。
实现思路。使用Stack(后进先出),使用图像表示。
BackDataTable记录每一步的操作。如下图所示
当按下返回按键时,BackDataTable中的数据pop出栈,push压入AfterDataTable栈中.如下图所示
当按下返回按键时,AfterDataTable中的数据pop出栈,push压入BackDataTable栈中.
实现代码:
代码段一:在程序加载时,即Load事件中初始化和注册事件。
//撤销和反撤销有关信息 BackDataTable.Clear(); //清空堆栈 AfterDataTable.Clear(); //清空堆栈 simBtnBack.Visible = false; //回退按钮不可见 simBtnAfter.Visible = false; //前进按钮不可见 simBtnBack.Enabled = false; //回退按钮不可用 simBtnAfter.Enabled = false; //前进按钮不可用 simBtnBack.Click += (obj, arg) =>
UnDoBeforeOrAfter(ref BackDataTable, ref AfterDataTable, true); //注册回退按纽事件 simBtnAfter.Click += (obj, arg) =>
UnDoBeforeOrAfter(ref BackDataTable, ref AfterDataTable, false); //注册前进按纽事件 loaded = true; this.KeyDown += (obj, key) => { if (key.Control && key.KeyCode == Keys.Z) // 撤销 UnDoBeforeOrAfter(ref BackDataTable, ref AfterDataTable, true); if (key.Control && key.KeyCode == Keys.Y) // 反撤销 UnDoBeforeOrAfter(ref BackDataTable, ref AfterDataTable, false); };
代码段二:获取数据保存每一步操作的数据
var table = this.gridOperatorTask.DataSource as DataTable; if (table != null) { var copy = table.Copy(); if (!BackDataTable.Contains(copy)) { BackDataTable.Push(copy); //数据入栈 } simBtnBack.Visible = BackDataTable.Count > 0; simBtnBack.Enabled = BackDataTable.Count > 0; }
代码段三:UnDoBeforeOrAfter函数的实现
private void UnDoBeforeOrAfter(ref Stack<DataTable> backDataTable, ref Stack<DataTable> afterDataTable, bool flag) { DataTable dt = null; FlagUndo = false; if (flag) //撤销 { if (backDataTable.Count <= 0) return; dt = backDataTable.Pop(); //出栈 afterDataTable.Push(dt); //入栈 simBtnBack.Enabled = backDataTable.Count > 0; simBtnAfter.Visible = AfterDataTable.Count > 0; simBtnAfter.Enabled = afterDataTable.Count > 0; } if (!flag) //反撤销 { if (afterDataTable.Count <= 0) return; dt = afterDataTable.Pop(); //出栈 backDataTable.Push(dt); //入栈 simBtnBack.Enabled = backDataTable.Count > 0; simBtnAfter.Enabled = afterDataTable.Count > 0; } var dtTemp = gridOperatorTask.DataSource as DataTable; if (dtTemp == null) return; var dtTempCopy = dtTemp.Copy(); if (dtTempCopy.DataTableEquals(dt)) //DataTableEquals函数是扩展函数 { gridOperatorTask.DataSource = dt; UnDoBeforeOrAfter(ref BackDataTable, ref AfterDataTable, flag); //如果出栈的数据与gridOperatorTask.DataSource的数据相同,就递归调用函数 } else { gridOperatorTask.DataSource = dt; } }
代码段四:DataTableEquals扩展函数的实现
图像示意