我在这里谈谈这个问题的第二部分Implement Undo/Redo operations for Adding/Deleting ListView Items和另一部分Extend this Class to Undo/Redo in a Listview。
我正在尝试实现添加/删除ListView项目的撤消/重做操作。
我已经使用这个LV UndoManager代码编写了一些进展,但是当我尝试前进时,我总是很难。
目前我可以添加单个项目,然后我可以完全撤消/重做添加的项目,不再需要。
我遇到的问题是:
·当我从Listview中删除单个项目时,我无法执行“撤消”以再次将该项目添加到LV中。
·当我添加范围时,如果我无法撤消的项目,当我致电UndoLastAction
时,会抛出System.Reflection.TargetParameterCountException
例外
·当我删除一系列项目时,我无法撤消/重做操作并启动相同的异常。
在简历中,如果我添加一个项目,我可以完全撤消/重做,如果删除单个项目我无法正确撤销,我也无法撤消/重做一系列ListViewItems。
我需要一个可以帮助我解决这些问题的人......或至少其中一个,耐心等待。
代码有点大,所以我认为可能花费更少的时间来理解并找到错误,打开并测试我上传的源项目。
以下是full
来源:
http://elektrostudios.tk/UndoManager%20Test%20Application.zip
只是一张图片:
这是UndoManager类:
Class ListView_UndoManager
Private action As ListView_Action = Nothing
Public Property Undostack As New Stack(Of ListView_Action)
Public Property Redostack As New Stack(Of ListView_Action)
' Public Property IsDoingUndo As Boolean = False
' Public Property IsDoingRedo As Boolean = False
''' <summary>
''' Undo the last action.
''' </summary>
''' <remarks></remarks>
Sub UndoLastAction()
If Undostack.Count = 0 Then Exit Sub ' Nothing to Undo.
action = Undostack.Pop ' Get the Action from Stack and remove it.
action.Operation.DynamicInvoke(action.data) ' Invoke the undo Action.
End Sub
''' <summary>
''' Redo the last action.
''' </summary>
''' <remarks></remarks>
Sub RedoLastAction()
If Redostack.Count = 0 Then Exit Sub ' Nothing to Redo.
action = Redostack.Pop() ' Get the Action from Stack and remove it.
action.Operation.DynamicInvoke(action.data) ' Invoke the redo Action.
End Sub
End Class
Class ListView_Action
''' <summary>
''' Name the Undo / Redo Action
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Property name As String
''' <summary>
''' Points to a method to excecute
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Property Operation As [Delegate]
''' <summary>
''' Data Array for the method to excecute
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Property data As ListViewItem()
End Class
这是我正在使用的ListView用户控件,我发布这个因为重要的是我触发的事件:ItemAdded
,ItemRemoved
,RangeItemAdded
和{{1 }}
RangeItemRemoved
最后这里是Test应用程序的Form1代码,这里是我用来添加/删除项目和调用undo / redo但我调用自定义ListView用户控件的方法的东西,所以你需要注意到...
Public Class LV : Inherits ListView
Public Shared Event ItemAdded As EventHandler(Of ItemAddedEventArgs)
Public Class ItemAddedEventArgs : Inherits EventArgs
Public Property Item As ListViewItem
End Class
Public Shared Event ItemRemoved As EventHandler(Of ItemRemovedEventArgs)
Public Class ItemRemovedEventArgs : Inherits EventArgs
Public Property Item As ListViewItem
End Class
Public Shared Event RangeItemAdded As EventHandler(Of RangeItemAddedEventArgs)
Public Class RangeItemAddedEventArgs : Inherits EventArgs
Public Property Items As ListViewItem()
End Class
Public Shared Event RangeItemRemoved As EventHandler(Of RangeItemRemovedEventArgs)
Public Class RangeItemRemovedEventArgs : Inherits EventArgs
Public Property Items As ListViewItem()
End Class
Public Sub New()
Me.Name = "ListView_Elektro"
Me.GridLines = True
Me.FullRowSelect = True
Me.MultiSelect = True
Me.View = View.Details
End Sub
''' <summary>
''' Adds an Item to the ListView,
''' to monitor when an Item is added to the ListView.
''' </summary>
Public Function AddItem(ByVal Item As ListViewItem) As ListViewItem
RaiseEvent ItemAdded(Me, New ItemAddedEventArgs With { _
.Item = Item
})
Return MyBase.Items.Add(Item)
End Function
''' <summary>
''' Adds a range of Items to the ListView,
''' to monitor when an Item is added to the ListView.
''' </summary>
Public Sub AddItem_Range(ByVal Items As ListViewItem())
RaiseEvent RangeItemAdded(Me, New RangeItemAddedEventArgs With { _
.Items = Items
})
MyBase.Items.AddRange(Items)
End Sub
''' <summary>
''' Removes an Item from the ListView
''' to monitor when an Item is removed from the ListView.
''' </summary>
Public Sub RemoveItem(ByVal Item As ListViewItem)
RaiseEvent ItemRemoved(Me, New ItemRemovedEventArgs With { _
.Item = Item
})
MyBase.Items.Remove(Item)
End Sub
''' <summary>
''' Removes a range of Items from the ListView
''' to monitor when an Item is removed from the ListView.
''' </summary>
Public Sub RemoveItem_Range(ByVal Items As ListViewItem())
RaiseEvent RangeItemRemoved(Me, New RangeItemRemovedEventArgs With { _
.Items = Items
})
For Each Item As ListViewItem In Items
MyBase.Items.Remove(Item)
Next
End Sub
End Class
PS:就像我说的那样,下载源并测试它真的会非常友好。
答案 0 :(得分:2)
When I remove a single Item from the Listview
- 很简单。
RemoveItem从列表中删除一个项目并将其添加到 ReDo 堆栈,但它仍然驻留在UnDo堆栈上!如果你添加5,删除1然后撤消,你将在重做上获得2个项目5的副本!
首先,您应该将AddItem机制更改为直接计数器以使调试更容易
nLVItemIndex += 1
Dim index As String = (nLVItemIndex).ToString
newItem = New ListViewItem
newItem.Text = "Item " & index
newItem.SubItems.Add("Hello " & index)
newItem.SubItems.Add("World " & index)
AddItem(newItem)
在ListView项目计数上使用CStr
会创建UnDo / Redo堆栈上已存在的名称,并使调试更加困难。
我认为GUI级别,像RemoveItem这样的用户调用动作会落入UnDo堆栈。您将AddItem与UnDO和RemoveItem等同于Redo,这是错误的。 GUI表单级别的所有内容都应该属于撤消堆栈,并且应该通过UM.Undo方法进入ReDo的 方式。
将它移到UnDo堆栈会发现另一个问题:你的UnDo Manager自身做的很少,并且使用表单级别的AddItem / RemoveItem而不是它自己的内部程序(他甚至无法创建自己的UnDo / Redo动作。)结果是所有Additem操作将Remove Action推送到UnDo堆栈;和所有RemoveItems推送一个无效的ReDo操作,因为你确实想要取消删除!
最终结果是来自UnDo(好)的UM.UndoLastAction
弹出然后DynamicInvoke
触发发出UnDo Push的Form.AddItem(非常糟糕,因为刚刚弹出一个 - 事实上这就是我们仍然做 - 这就是为什么原来有IsRedoing标志)。 UnDo Manager需要进行大脑手术才能完成自己的工作,因为GUi级别的添加/删除操作与UnDo / ReDo不同。
然后,这表明UnDoManager没有引用他正在“管理”的控件,更不用说监控多个LV的能力了。我认为AddRange方法只会加剧上述问题(无法找到代码墙中的基本要素)。
最后,是否真的有必要在文本墙中发布所有prop XML注释标题?撤消的所有Draw覆盖是否与撤消密切相关?否。
编辑
这里大致是UnDoManager.UnDo
需要做的事情(从我开始的那个过分夸张的事情中重新开始):
Friend Function UnDo() As Boolean
If _undoStack.Count = 0 Then Exit Function
Dim ur As UndoAction ' ie Command
_IgnoreChange = True ' ie IsUnDoing so you dont Push while Popping
ur = _undoStack.Pop ' get the Undo, such as LV.RemoveItem
ur.Undo() ' Undo whatever it is (could be a checkbox etc)
_IgnoreChange = False ' open for business
_redoStack.Push(ur) ' push the same Action onto the ReDo
' I dont bother changing a code (yet) because
' if it is in Undostack it is an UnDo
return True
End Function
我的UnDoAction
只是撤消控件而Data As Object
。由于MOST Controls只有一件事让用户感到困惑,没问题。 LV有2个合法的用户操作(Checked和Label Edit),因此要做到这一点,就需要进行扩展。
我和另一个依赖于多态,其中undoStack(2)可能是一个checkedlistbox撤消动作,undoStack(9)可能是一个combox动作 - 观察者(监视器)知道要创建哪种类型以及如何撤消/重新执行行动。文本撤消(TextBox,Combo,MaskedEdit和DateTimePicker)只是:
Friend Overrides Function Undo() As Boolean
_Ctl.Text = _UndoData
Return True
End Function
我想知道的是现在你正在做LastItem - 那么RemoveSelectedItem呢?你怎么把它恢复原状?如果您保留任何类型的订单参考,它可能无效,因为该参考可能不再存在。