我正在尝试为文本框的某些事件实现一个基于堆栈的简单撤销/重做机制。
在提问之前,我看到了很多像这些的撤销/重做实现,但它们或多或少都是不完整的,并且展示了已经知道的内容(另一方面,使用罕见接口的专业方式超出了我的理解能力,因此我想遵循这种基于堆栈的方法)。因为那些例子不仅是编辑控件的撤销/重做示例,而且还是堆栈的推入/弹出示例,但撤销/重做更多地是编写一种方法以弹出“撤销堆栈”的最后一项和另一种方法以弹出“重做堆栈”的最后一项,因为在用户与控件交互时,在某个点上,堆栈应该被清除/重置。
我是指在真正的编辑控件的撤销/重做机制中,“重做堆栈”应在用户撤消并且用户在控件中进行文本修改时被清除,而“撤消堆栈”仍然包含项,因此在这一点上没有可重做的内容,因为在撤消时发生了更改。 我没有看到任何完整的以这种方式实现撤销/重做机制的示例,考虑到当控件发生更改时必须如何操作撤销/重做堆栈。
我需要帮助来正确实现我的撤销/重做堆栈逻辑,我开始自己尝试了几天并进行了几次试错,但总有一些细节逃脱我的注意,因为当我让一个(撤消或重做)堆栈正常工作时,另一个就停止按预期工作,撤消了不该撤消的东西或重做了不该重做的东西, 所以我再次放弃了我编写的所有条件逻辑,因为我的逻辑总是错误的,我应该从零开始使用适当的条件算法,即在合适的时刻推送或弹出堆栈项的适当条件。
在提问之前,我看到了很多像这些的撤销/重做实现,但它们或多或少都是不完整的,并且展示了已经知道的内容(另一方面,使用罕见接口的专业方式超出了我的理解能力,因此我想遵循这种基于堆栈的方法)。因为那些例子不仅是编辑控件的撤销/重做示例,而且还是堆栈的推入/弹出示例,但撤销/重做更多地是编写一种方法以弹出“撤销堆栈”的最后一项和另一种方法以弹出“重做堆栈”的最后一项,因为在用户与控件交互时,在某个点上,堆栈应该被清除/重置。
我是指在真正的编辑控件的撤销/重做机制中,“重做堆栈”应在用户撤消并且用户在控件中进行文本修改时被清除,而“撤消堆栈”仍然包含项,因此在这一点上没有可重做的内容,因为在撤消时发生了更改。 我没有看到任何完整的以这种方式实现撤销/重做机制的示例,考虑到当控件发生更改时必须如何操作撤销/重做堆栈。
我需要帮助来正确实现我的撤销/重做堆栈逻辑,我开始自己尝试了几天并进行了几次试错,但总有一些细节逃脱我的注意,因为当我让一个(撤消或重做)堆栈正常工作时,另一个就停止按预期工作,撤消了不该撤消的东西或重做了不该重做的东西, 所以我再次放弃了我编写的所有条件逻辑,因为我的逻辑总是错误的,我应该从零开始使用适当的条件算法,即在合适的时刻推送或弹出堆栈项的适当条件。
我需要的不仅仅是建议或文字,而是能够解决我的算法问题的可行代码。我需要完成以下代码中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