Undo/Redo 基于堆栈的编辑控件实现
Undo/Redo implementation for an edit-control, based on stacks
我正在尝试为文本框的某些事件实现一个简单的 undo/redo 机制(基于堆栈)。
在问这个问题之前,我已经看到了很多 undo/redo 实现,例如 these,但或多或少它们是不完整的并且显示了一些东西我已经知道(另一方面,从我的理解中使用稀有接口的专业方式,所以我想遵循这种基于堆栈的方式),因为这些例子不仅仅是 undo/redo 编辑控件的例子,是堆栈的 push/pop 示例,但是 undo/redo 比编写弹出“undo stack”的最后一项的方法和另一种方法多一点弹出“redo stack”的最后一项,因为在用户与控件交互的某个时刻,堆栈应该是 cleared/resetted.
我的意思是,在真正的 undo/redo 编辑控件机制中,“redo stack”应该在用户撤消和用户创建文本时清除在“undo stack”仍然包含项目的情况下在控件中进行修改,因此此时没有什么可重做的,因为在撤消时发生了更改。
我没有看到任何完整的撤消-重做机制示例,请记住当控件中发生更改时必须如何操作 undo/redo 堆栈。
我需要帮助来正确实现我的 undo/redo 堆栈的逻辑,我开始自己尝试了几天,经过几次试验和错误,但总是逃避一些细节,因为当我得到一个(撤消或重做)堆栈正常工作,另一个按预期停止工作,撤消不应撤消的内容或重做不应重做的内容,
所以我(再次)丢弃了 all the conditional logic that I written 因为我的逻辑总是错误的,我应该使用适当的条件算法再次从零开始,我的意思是正确的条件来推送或弹出在适当的时候堆叠项目。
那么,除了文字或建议,我需要一个可以用我的算法解决问题的工作代码,我需要在下面的代码中完成AddUndoRedoItem
方法的算法逻辑,这是一个关于这个的具体问题。
如果我缺少遵循相同原则(撤消和重做堆栈)的更简单的解决方案,我也会接受该解决方案。
无论是 C# 还是 Vb.Net。
PD:
如果因为我的英语不好,我没有正确解释某些事情,而你不完全确定我要的是哪种 undo/redo,只是我要听起来像 undo/redo,只需在记事本中测试 Ctrl+Z(undo) 和 Ctrl+Y(redo) 键,同时执行撤消或重做的文本更改,看看它是如何工作的,这是一个真正的 undo/redo 实现,我正在尝试用堆栈重现。
这是当前代码:
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
感谢@Plutonix 我解决了它,我真的不相信一个简单的注释可以帮助我解决逻辑问题,但是是的,我让事情变得比他们真的是。
我仍然需要考虑如何管理一次性对象,但或多或少的想法已经完成,下面的代码按预期工作(至少符合我的预期)。
这些是基础 undo/redo class 控件的部分:
Public Enum UndoRedoCommand As Integer
Undo
Redo
End Enum
Public Class UndoRedoItem
Public Property [Event] As Integer
Public Property LastValue As Object
Public Property CurrentValue As Object
End Class
Public MustInherit Class UndoRedo(Of T As Control)
#Region " Private Fields "
Private ReadOnly undoStack As Stack(Of UndoRedoItem)
Private ReadOnly redoStack As Stack(Of UndoRedoItem)
#End Region
#Region " Properties "
Public ReadOnly Property Control As T
Get
Return Me.controlB
End Get
End Property
Protected WithEvents controlB As T
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
#End Region
#Region " Constructors "
Private Sub New()
End Sub
Public Sub New(ByVal ctrl As T)
Me.undoStack = New Stack(Of UndoRedoItem)
Me.redoStack = New Stack(Of UndoRedoItem)
Me.controlB = ctrl
End Sub
#End Region
#Region " Public Methods "
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
#End Region
#Region " Private Methods "
Private Sub InternalUndoRedo(ByVal command As UndoRedoCommand)
Dim undoRedoItem As UndoRedoItem = Nothing
Select Case command
Case UndoRedoCommand.Undo
Me.isUndoingB = True
undoRedoItem = Me.undoStack.Pop
Me.AddUndoRedoItem(UndoRedoCommand.Redo, undoRedoItem.Event, undoRedoItem.LastValue, undoRedoItem.CurrentValue)
Case UndoRedoCommand.Redo
Me.isRedoingB = True
undoRedoItem = Me.redoStack.Pop
End Select
Me.DoUndo(undoRedoItem.Event, undoRedoItem.CurrentValue)
Me.isUndoingB = False
Me.isRedoingB = False
End Sub
Protected MustOverride Sub DoUndo(ByVal [event] As Integer, ByVal data As Object)
Protected Sub AddUndoRedoItem(ByVal command As UndoRedoCommand,
ByVal [event] As Integer,
ByVal currentData As Object,
ByVal lastData As Object)
Dim undoRedoItem As New UndoRedoItem
undoRedoItem.Event = [event]
Select Case command
Case UndoRedoCommand.Undo
If (Me.isUndoingB) Then
Exit Select
End If
If (Me.CanUndo) AndAlso (Me.CanRedo) AndAlso Not (Me.IsRedoing) Then
Me.redoStack.Clear()
End If
undoRedoItem.CurrentValue = lastData
undoRedoItem.LastValue = currentData
Me.undoStack.Push(undoRedoItem)
Case UndoRedoCommand.Redo
If (Me.isRedoingB) Then
Exit Select
End If
undoRedoItem.CurrentValue = currentData
undoRedoItem.LastValue = lastData
Me.redoStack.Push(undoRedoItem)
End Select
End Sub
#End Region
End Class
这是文本框上 undo/redo 的实现:
Public Enum UndoRedoTextBoxEvent As Integer
TextChanged
FontChanged
BackColorChanged
ForeColorChanged
End Enum
Public NotInheritable Class UndoRedoTextBox : Inherits UndoRedo(Of TextBox)
Private lastText As String
Private lastFont As Font
Private lastBackColor As Color
Private lastForeColor As Color
Public Sub New(ByVal tb As TextBox)
MyBase.New(tb)
End Sub
Protected Overrides Sub DoUndo([event] As Integer, data As Object)
Select Case DirectCast([event], UndoRedoTextBoxEvent)
Case UndoRedoTextBoxEvent.TextChanged
MyBase.controlB.Text = CStr(data)
Case UndoRedoTextBoxEvent.FontChanged
MyBase.controlB.Font = DirectCast(data, Font)
Case UndoRedoTextBoxEvent.BackColorChanged
MyBase.controlB.BackColor = DirectCast(data, Color)
Case UndoRedoTextBoxEvent.ForeColorChanged
MyBase.controlB.ForeColor = DirectCast(data, Color)
End Select
End Sub
Private Sub TextBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs) _
Handles controlB.TextChanged
Dim currentText As String = MyBase.controlB.Text
If Not String.Equals(Me.lastText, currentText, StringComparison.Ordinal) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.TextChanged, currentText, Me.lastText)
Me.lastText = currentText
End If
End Sub
Private Sub TextBox_FontChanged(sender As Object, e As EventArgs) _
Handles controlB.FontChanged
Dim currentFont As Font = MyBase.controlB.Font
If (Me.lastFont IsNot currentFont) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.FontChanged, currentFont, Me.lastFont)
Me.lastFont = currentFont
End If
End Sub
Private Sub TextBox_BackColorChanged(sender As Object, e As EventArgs) _
Handles controlB.BackColorChanged
Dim currentBackColor As Color = MyBase.controlB.BackColor
If (Me.lastBackColor <> currentBackColor) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.BackColorChanged, currentBackColor, Me.lastBackColor)
Me.lastBackColor = currentBackColor
End If
End Sub
Private Sub TextBox_ForeColorChanged(sender As Object, e As EventArgs) _
Handles controlB.ForeColorChanged
Dim currentForeColor As Color = MyBase.controlB.ForeColor
If (Me.lastForeColor <> currentForeColor) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.ForeColorChanged, currentForeColor, Me.lastForeColor)
Me.lastForeColor = currentForeColor
End If
End Sub
End Class
我正在尝试为文本框的某些事件实现一个简单的 undo/redo 机制(基于堆栈)。
在问这个问题之前,我已经看到了很多 undo/redo 实现,例如 these,但或多或少它们是不完整的并且显示了一些东西我已经知道(另一方面,从我的理解中使用稀有接口的专业方式,所以我想遵循这种基于堆栈的方式),因为这些例子不仅仅是 undo/redo 编辑控件的例子,是堆栈的 push/pop 示例,但是 undo/redo 比编写弹出“undo stack”的最后一项的方法和另一种方法多一点弹出“redo stack”的最后一项,因为在用户与控件交互的某个时刻,堆栈应该是 cleared/resetted.
我的意思是,在真正的 undo/redo 编辑控件机制中,“redo stack”应该在用户撤消和用户创建文本时清除在“undo stack”仍然包含项目的情况下在控件中进行修改,因此此时没有什么可重做的,因为在撤消时发生了更改。 我没有看到任何完整的撤消-重做机制示例,请记住当控件中发生更改时必须如何操作 undo/redo 堆栈。
我需要帮助来正确实现我的 undo/redo 堆栈的逻辑,我开始自己尝试了几天,经过几次试验和错误,但总是逃避一些细节,因为当我得到一个(撤消或重做)堆栈正常工作,另一个按预期停止工作,撤消不应撤消的内容或重做不应重做的内容, 所以我(再次)丢弃了 all the conditional logic that I written 因为我的逻辑总是错误的,我应该使用适当的条件算法再次从零开始,我的意思是正确的条件来推送或弹出在适当的时候堆叠项目。
那么,除了文字或建议,我需要一个可以用我的算法解决问题的工作代码,我需要在下面的代码中完成AddUndoRedoItem
方法的算法逻辑,这是一个关于这个的具体问题。
如果我缺少遵循相同原则(撤消和重做堆栈)的更简单的解决方案,我也会接受该解决方案。
无论是 C# 还是 Vb.Net。
PD: 如果因为我的英语不好,我没有正确解释某些事情,而你不完全确定我要的是哪种 undo/redo,只是我要听起来像 undo/redo,只需在记事本中测试 Ctrl+Z(undo) 和 Ctrl+Y(redo) 键,同时执行撤消或重做的文本更改,看看它是如何工作的,这是一个真正的 undo/redo 实现,我正在尝试用堆栈重现。
这是当前代码:
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
感谢@Plutonix 我解决了它,我真的不相信一个简单的注释可以帮助我解决逻辑问题,但是是的,我让事情变得比他们真的是。
我仍然需要考虑如何管理一次性对象,但或多或少的想法已经完成,下面的代码按预期工作(至少符合我的预期)。
这些是基础 undo/redo class 控件的部分:
Public Enum UndoRedoCommand As Integer
Undo
Redo
End Enum
Public Class UndoRedoItem
Public Property [Event] As Integer
Public Property LastValue As Object
Public Property CurrentValue As Object
End Class
Public MustInherit Class UndoRedo(Of T As Control)
#Region " Private Fields "
Private ReadOnly undoStack As Stack(Of UndoRedoItem)
Private ReadOnly redoStack As Stack(Of UndoRedoItem)
#End Region
#Region " Properties "
Public ReadOnly Property Control As T
Get
Return Me.controlB
End Get
End Property
Protected WithEvents controlB As T
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
#End Region
#Region " Constructors "
Private Sub New()
End Sub
Public Sub New(ByVal ctrl As T)
Me.undoStack = New Stack(Of UndoRedoItem)
Me.redoStack = New Stack(Of UndoRedoItem)
Me.controlB = ctrl
End Sub
#End Region
#Region " Public Methods "
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
#End Region
#Region " Private Methods "
Private Sub InternalUndoRedo(ByVal command As UndoRedoCommand)
Dim undoRedoItem As UndoRedoItem = Nothing
Select Case command
Case UndoRedoCommand.Undo
Me.isUndoingB = True
undoRedoItem = Me.undoStack.Pop
Me.AddUndoRedoItem(UndoRedoCommand.Redo, undoRedoItem.Event, undoRedoItem.LastValue, undoRedoItem.CurrentValue)
Case UndoRedoCommand.Redo
Me.isRedoingB = True
undoRedoItem = Me.redoStack.Pop
End Select
Me.DoUndo(undoRedoItem.Event, undoRedoItem.CurrentValue)
Me.isUndoingB = False
Me.isRedoingB = False
End Sub
Protected MustOverride Sub DoUndo(ByVal [event] As Integer, ByVal data As Object)
Protected Sub AddUndoRedoItem(ByVal command As UndoRedoCommand,
ByVal [event] As Integer,
ByVal currentData As Object,
ByVal lastData As Object)
Dim undoRedoItem As New UndoRedoItem
undoRedoItem.Event = [event]
Select Case command
Case UndoRedoCommand.Undo
If (Me.isUndoingB) Then
Exit Select
End If
If (Me.CanUndo) AndAlso (Me.CanRedo) AndAlso Not (Me.IsRedoing) Then
Me.redoStack.Clear()
End If
undoRedoItem.CurrentValue = lastData
undoRedoItem.LastValue = currentData
Me.undoStack.Push(undoRedoItem)
Case UndoRedoCommand.Redo
If (Me.isRedoingB) Then
Exit Select
End If
undoRedoItem.CurrentValue = currentData
undoRedoItem.LastValue = lastData
Me.redoStack.Push(undoRedoItem)
End Select
End Sub
#End Region
End Class
这是文本框上 undo/redo 的实现:
Public Enum UndoRedoTextBoxEvent As Integer
TextChanged
FontChanged
BackColorChanged
ForeColorChanged
End Enum
Public NotInheritable Class UndoRedoTextBox : Inherits UndoRedo(Of TextBox)
Private lastText As String
Private lastFont As Font
Private lastBackColor As Color
Private lastForeColor As Color
Public Sub New(ByVal tb As TextBox)
MyBase.New(tb)
End Sub
Protected Overrides Sub DoUndo([event] As Integer, data As Object)
Select Case DirectCast([event], UndoRedoTextBoxEvent)
Case UndoRedoTextBoxEvent.TextChanged
MyBase.controlB.Text = CStr(data)
Case UndoRedoTextBoxEvent.FontChanged
MyBase.controlB.Font = DirectCast(data, Font)
Case UndoRedoTextBoxEvent.BackColorChanged
MyBase.controlB.BackColor = DirectCast(data, Color)
Case UndoRedoTextBoxEvent.ForeColorChanged
MyBase.controlB.ForeColor = DirectCast(data, Color)
End Select
End Sub
Private Sub TextBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs) _
Handles controlB.TextChanged
Dim currentText As String = MyBase.controlB.Text
If Not String.Equals(Me.lastText, currentText, StringComparison.Ordinal) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.TextChanged, currentText, Me.lastText)
Me.lastText = currentText
End If
End Sub
Private Sub TextBox_FontChanged(sender As Object, e As EventArgs) _
Handles controlB.FontChanged
Dim currentFont As Font = MyBase.controlB.Font
If (Me.lastFont IsNot currentFont) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.FontChanged, currentFont, Me.lastFont)
Me.lastFont = currentFont
End If
End Sub
Private Sub TextBox_BackColorChanged(sender As Object, e As EventArgs) _
Handles controlB.BackColorChanged
Dim currentBackColor As Color = MyBase.controlB.BackColor
If (Me.lastBackColor <> currentBackColor) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.BackColorChanged, currentBackColor, Me.lastBackColor)
Me.lastBackColor = currentBackColor
End If
End Sub
Private Sub TextBox_ForeColorChanged(sender As Object, e As EventArgs) _
Handles controlB.ForeColorChanged
Dim currentForeColor As Color = MyBase.controlB.ForeColor
If (Me.lastForeColor <> currentForeColor) Then
MyBase.AddUndoRedoItem(UndoRedoCommand.Undo, UndoRedoTextBoxEvent.ForeColorChanged, currentForeColor, Me.lastForeColor)
Me.lastForeColor = currentForeColor
End If
End Sub
End Class