How to perform undo or redo operation in WinForms GridGroupingControl?
Perform undo or redo operation
Description
GridGroupingControl does not support the Undo or Redo architecture that is provided by the GridControlBase.
Solution
When you want to use the Undo or Redo support, you can add custom commands derived from the abstract GridModelCommand, to manage the actions you want to support. The attached sample shows one way to handle undo or redo on the user changed values.
1. Creating a Custom Command Class:
A class GGCValueChangedCmd is created and inherited from the GridModelCommand class.
C#
//derived command to manage value changed action. public class GGCValueChangedCmd : GridModelCommand { private object oldValue; private object newValue; private string fieldName; private GridRecordRow record; public GGCValueChangedCmd(object newValue, object oldValue, string fieldName, GridRecordRow record) { this.record = record; this.fieldName = fieldName; this.oldValue = oldValue; this.newValue = newValue; } //perform the value changing in the execute method. public override void Execute() { this.record.ParentRecord.BeginEdit(); this.record.ParentRecord.SetValue(fieldName, oldValue); this.record.ParentRecord.EndEdit(); } //interchanging the oldvalue and newvalue. public void SwapValues() { object o = this.oldValue; this.oldValue = newValue; newValue = o; } }
VB
'derived command to manage value changed action. Public Class GGCValueChangedCmd Inherits GridModelCommand Private oldValue As Object Private newValue As Object Private fieldName As String Private record As GridRecordRow Public Sub New(ByVal newValue As Object, ByVal oldValue As Object, ByVal fieldName As String, ByVal record As GridRecordRow) Me.record = record Me.fieldName = fieldName Me.oldValue = oldValue Me.newValue = newValue End Sub 'perform the value changing in the execute method. Public Overrides Sub Execute() Me.record.ParentRecord.BeginEdit() Me.record.ParentRecord.SetValue(fieldName, oldValue) Me.record.ParentRecord.EndEdit() End Sub 'interchanging the oldvalue and newvalue. Public Sub SwapValues() Dim o As Object = Me.oldValue Me.oldValue = newValue newValue = o End Sub End Class
2. Working with CommandStack
The TableControlCurrentCellValidating is used to cache the old value. TableControlCurrentCellAcceptedChanges event is used to create an instance of the custom command and push it on the CommandStack.
C#
//used to cache old value so it will be easily available in //TableControlCurrentCellAcceptedChanges where the GGCValueChangedCmd object needs it. void gridGroupingControl1_TableControlCurrentCellValidating(object sender, GridTableControlCancelEventArgs e) { GridCurrentCell cc = e.TableControl.GetNestedCurrentCell(); GridTableCellStyleInfo style = e.TableControl.GetTableViewStyleInfo(cc.RowIndex, cc.ColIndex); //Console.WriteLine(style.CellValue); oldValue = style.CellValue; } //used to generate the undo command and push it on the CommandStack void gridGroupingControl1_TableControlCurrentCellAcceptedChanges(object sender, GridTableControlCancelEventArgs e) { GridCurrentCell cc = e.TableControl.GetNestedCurrentCell(); GridTableCellStyleInfo style = e.TableControl.GetTableViewStyleInfo(cc.RowIndex, cc.ColIndex); //Console.WriteLine(style.CellValue); GGCValueChangedCmd cmd = new GGCValueChangedCmd(style.CellValue, oldValue, style.TableCellIdentity.Column.MappingName, style.TableCellIdentity.DisplayElement as GridRecordRow); this.gridGroupingControl1.TableControl.Model.CommandStack.Push(cmd);//.UndoStack.Push(cmd); }
VB
'used to cache old value so it will be easily available in 'TableControlCurrentCellAcceptedChanges where the GGCValueChangedCmd object needs it. Private Sub gridGroupingControl1_TableControlCurrentCellValidating(ByVal sender As Object, ByVal e As GridTableControlCancelEventArgs) Dim cc As GridCurrentCell = e.TableControl.GetNestedCurrentCell() Dim style As GridTableCellStyleInfo = e.TableControl.GetTableViewStyleInfo(cc.RowIndex, cc.ColIndex) 'Console.WriteLine(style.CellValue); oldValue = style.CellValue End Sub 'used to generate the undo command and push it on the CommandStack Private Sub gridGroupingControl1_TableControlCurrentCellAcceptedChanges(ByVal sender As Object, ByVal e As GridTableControlCancelEventArgs) Dim cc As GridCurrentCell = e.TableControl.GetNestedCurrentCell() Dim style As GridTableCellStyleInfo = e.TableControl.GetTableViewStyleInfo(cc.RowIndex, cc.ColIndex) 'Console.WriteLine(style.CellValue); Dim cmd As New GGCValueChangedCmd(style.CellValue, oldValue, style.TableCellIdentity.Column.MappingName, TryCast(style.TableCellIdentity.DisplayElement, GridRecordRow)) Me.gridGroupingControl1.TableControl.Model.CommandStack.Push(cmd) '.UndoStack.Push(cmd); End Sub
3. Performing Undo/Redo operation
The Undo or Redo operations are performed by using buttons. UndoStack and RedoStack of CommandStack are used to retrieve the old or new values accordingly, to perform the undo or redo operation. The Execute() method of GridModelCommand is overridden for reassigning the values to a record. A helper method SwapValues() is used to swap the old or new values.
C#
//handle the undo commands until we change the value back //(may have currentcell selection undo commands also) and also push commands onto the Redo stack private void btn_undo_Click(object sender, EventArgs e) { if (this.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Count > 0) { GridModelCommand cmd = this.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Peek() as GridModelCommand; if (cmd != null) { do { cmd = this.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Pop() as GridModelCommand; if (cmd != null) { cmd.Execute(); if (cmd is GGCValueChangedCmd) { //call the swapvalues method ((GGCValueChangedCmd)cmd).SwapValues(); } this.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Push(cmd); } } while (this.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Count > 0 && cmd != null && !(cmd is GGCValueChangedCmd)); } } } //handle the redo and push commands back onto the UndoStack private void btn_redo_Click(object sender, EventArgs e) { if (this.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Count > 0) { GridModelCommand cmd = this.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Peek() as GridModelCommand; if (cmd != null) { do { cmd = this.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Pop() as GridModelCommand; if (cmd != null) { cmd.Execute(); if (cmd is GGCValueChangedCmd) { //call the swapvalues method ((GGCValueChangedCmd)cmd).SwapValues(); } this.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Push(cmd); } } while (this.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Count > 0 && cmd != null && !(cmd is GGCValueChangedCmd)); } } }
VB
'handle the undo commands until we change the value back. '(may have currentcell selection undo commands also) and also push commands onto the Redo stack. Private Sub btn_undo_Click(ByVal sender As Object, ByVal e As EventArgs) If Me.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Count > 0 Then Dim cmd As GridModelCommand = TryCast(Me.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Peek(), GridModelCommand) If cmd IsNot Nothing Then Do cmd = TryCast(Me.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Pop(), GridModelCommand) If cmd IsNot Nothing Then cmd.Execute() If TypeOf cmd Is GGCValueChangedCmd Then 'call the swapvalues method CType(cmd, GGCValueChangedCmd).SwapValues() End If Me.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Push(cmd) End If Loop While Me.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Count > 0 AndAlso cmd IsNot Nothing AndAlso Not(TypeOf cmd Is GGCValueChangedCmd) End If End If End Sub 'handle the redo and push commands back onto the UndoStack. Private Sub btn_redo_Click(ByVal sender As Object, ByVal e As EventArgs) If Me.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Count > 0 Then Dim cmd As GridModelCommand = TryCast(Me.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Peek(), GridModelCommand) If cmd IsNot Nothing Then Do cmd = TryCast(Me.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Pop(), GridModelCommand) If cmd IsNot Nothing Then cmd.Execute() If TypeOf cmd Is GGCValueChangedCmd Then 'call the swapvalues method CType(cmd, GGCValueChangedCmd).SwapValues() End If Me.gridGroupingControl1.TableControl.Model.CommandStack.UndoStack.Push(cmd) End If Loop While Me.gridGroupingControl1.TableControl.Model.CommandStack.RedoStack.Count > 0 AndAlso cmd IsNot Nothing AndAlso Not(TypeOf cmd Is GGCValueChangedCmd) End If End If End Sub
Samples:
C#: Undo_Redo
VB: Undo_Redo