我正在尝试为文本框的某些事件实现一个简单的撤消/重做机制(基于堆栈)。
在提出这个问题之前,我已经看到了很多撤销/重做实现,如
these
,但它们或多或少都是不完整的,显示了我已经知道的东西(另一方面,使用稀有接口的专业方式超出了我的理解,所以我想遵循这种基于堆栈的方式),因为这些示例不仅仅是编辑控件的撤消/重做示例,而是堆栈的推/弹出示例,但是撤销/重做比编写一个方法来弹出“
撤销栈
“和另一种弹出”
重做堆栈
“,因为在用户与控件交互的某个时刻,堆栈应该被清除/重置。
我的意思是,在编辑控件的真正撤消/重做机制中,“
重做堆栈
“当用户撤消并且用户在控件中进行文本修改时,应清除”
撤销栈
“仍然包含项,因此在这一点上没有什么可重做的,因为撤消时发生了更改。
考虑到控件中发生更改时必须如何操作撤消/重做堆栈,我还没有看到任何以这种方式执行撤消/重作机制的完整示例。
我需要帮助来正确地实现我的撤销/重做堆栈的逻辑,我开始自己尝试了几天,尝试了几次,但总是忽略了一些细节,因为当我让一个(撤销或重做)堆栈正常工作时,另一个堆栈会停止按预期工作,撤销它不应该撤销的或重做它不应该重做的,
所以我(再次)放弃了
all the conditional logic that I written
因为我的逻辑总是错误的,所以我应该用一个合适的条件算法从零开始,我的意思是在合适的时候用合适的条件推送或弹出堆栈项。
然后,不仅仅是文字或建议,我需要一个可以用我的算法解决问题的工作代码,我需要完成
AddUndoRedoItem
在下面的代码中,这是一个具体的问题。
如果我缺少遵循相同原则的更简单的解决方案(撤销和重做堆栈),我也会接受这个解决方案。
不管是在C#还是Vb.Net中。
零件编号:
如果因为我的英语不好,我没有正确地解释一些事情,而你也不完全确定我要的是什么样的撤销/重做,只是我要求的是一个听起来很像的撤销/重新做,那就测试一下
Ctrl+Z组合键
(撤销)和
Ctrl+Y组合键
当在记事本中执行撤消或重做的文本更改时,请查看它的行为,这是一个真正的撤消/重做实现,这是我试图用堆栈复制的。
这是当前代码:
Public Enum UndoRedoCommand As Integer
Undo
Redo
End Enum
Public Enum UndoRedoTextBoxEvent As Integer
TextChanged
End Enum
Public NotInheritable Class UndoRedoTextBox
Private ReadOnly undoStack As Stack(Of KeyValuePair(Of UndoRedoTextBoxEvent, Object))
Private ReadOnly redoStack As Stack(Of KeyValuePair(Of UndoRedoTextBoxEvent, Object))
Private lastCommand As UndoRedoCommand
Private lastText As String
Public ReadOnly Property Control As TextBox
Get
Return Me.controlB
End Get
End Property
Private WithEvents controlB As TextBox
Public ReadOnly Property CanUndo As Boolean
Get
Return (Me.undoStack.Count <> 0)
End Get
End Property
Public ReadOnly Property CanRedo As Boolean
Get
Return (Me.redoStack.Count <> 0)
End Get
End Property
Public ReadOnly Property IsUndoing As Boolean
Get
Return Me.isUndoingB
End Get
End Property
Private isUndoingB As Boolean
Public ReadOnly Property IsRedoing As Boolean
Get
Return Me.isRedoingB
End Get
End Property
Private isRedoingB As Boolean
Public Sub New(ByVal tb As TextBox)
Me.undoStack = New Stack(Of KeyValuePair(Of UndoRedoTextBoxEvent, Object))
Me.redoStack = New Stack(Of KeyValuePair(Of UndoRedoTextBoxEvent, Object))
Me.controlB = tb
Me.lastText = tb.Text
End Sub
Public Sub Undo()
If (Me.CanUndo) Then
Me.InternalUndoRedo(UndoRedoCommand.Undo)
End If
End Sub
Public Sub Redo()
If (Me.CanRedo) Then
Me.InternalUndoRedo(UndoRedoCommand.Redo)
End If
End Sub
' Undoes or redoues.
Private Sub InternalUndoRedo(ByVal command As UndoRedoCommand)
Dim undoRedoItem As KeyValuePair(Of UndoRedoTextBoxEvent, Object) = Nothing
Dim undoRedoEvent As UndoRedoTextBoxEvent
Dim undoRedoValue As Object = Nothing
Select Case command
Case UndoRedoCommand.Undo
Me.isUndoingB = True
undoRedoItem = Me.undoStack.Pop
Me.AddUndoRedoItem(UndoRedoCommand.Redo, UndoRedoTextBoxEvent.TextChanged, Me.lastText, undoRedoItem.Value)
Case UndoRedoCommand.Redo
Me.isRedoingB = True
undoRedoItem = Me.redoStack.Pop
Me.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.TextChanged, undoRedoItem.Value, Me.lastText)
End Select
undoRedoEvent = undoRedoItem.Key
undoRedoValue = undoRedoItem.Value
Select Case undoRedoEvent
Case UndoRedoTextBoxEvent.TextChanged
Me.controlB.Text = CStr(undoRedoValue)
End Select
Me.isUndoingB = False
Me.isRedoingB = False
End Sub
Private Sub AddUndoRedoItem(ByVal command As UndoRedoCommand, ByVal [event] As UndoRedoTextBoxEvent,
ByVal data As Object, ByVal lastData As Object)
Console.WriteLine()
Console.WriteLine("command :" & command.ToString)
Console.WriteLine("last command:" & lastCommand.ToString)
Console.WriteLine("can undo :" & Me.CanUndo)
Console.WriteLine("can redo :" & Me.CanRedo)
Console.WriteLine("is undoing :" & Me.isUndoingB)
Console.WriteLine("is redoing :" & Me.isRedoingB)
Console.WriteLine("data :" & data.ToString)
Console.WriteLine("last data :" & lastData.ToString)
Dim undoRedoData As Object = Nothing
Me.lastCommand = command
Select Case command
Case UndoRedoCommand.Undo
If (Me.isUndoingB) Then
Exit Select
End If
undoRedoData = lastData
Me.undoStack.Push(New KeyValuePair(Of UndoRedoTextBoxEvent, Object)([event], undoRedoData))
Case UndoRedoCommand.Redo
If (Me.isRedoingB) Then
Exit Select
End If
undoRedoData = lastData
Me.redoStack.Push(New KeyValuePair(Of UndoRedoTextBoxEvent, Object)([event], undoRedoData))
End Select
End Sub
Private Sub TextBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs) _
Handles controlB.TextChanged
Dim currentText As String = Me.controlB.Text
If Not String.Equals(Me.lastText, currentText, StringComparison.Ordinal) Then
Select Case Me.lastCommand
Case UndoRedoCommand.Undo
Me.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.TextChanged, currentText, Me.lastText)
Case UndoRedoCommand.Redo
Me.AddUndoRedoItem(UndoRedoCommand.Redo, UndoRedoTextBoxEvent.TextChanged, Me.lastText, currentText)
End Select
Me.lastText = currentText
End If
End Sub
End Class