我需要扩展Class来管理Listview中的撤消/重做操作,这意味着:
·撤消/重做检查/取消选中行
·撤消/重做其他一些我可能错过的重要事情
我不知道如何开始这样做,代码对我来说太复杂了,关于这个的任何帮助/提示/示例对我来说都会非常满意,但在3个月内我无法进行此更改,我我想我需要很好的解释或完整的例子,这里是代码:
******************************************************** Undo/Redo framework (c) Copyright 2009 Etienne Nijboer ********************************************************
(我没有在这里发布代码,因为它超过了StackOverflow的30.000字符限制)
UPDATE:
这是来自作者的一些有用的信息,解释了我需要做的事情来添加Listview支持,但实际上我不能自己:
Adding functionality for a listview shouldn’t be that hard by the way
and also a great way of getting into how it works. You would need to
create a new monitor that will capture the listview change events and
store the current value before it is changed. A command is created if
you detect that a change was made with all the info needed to undo or
redo the action. Thats it. As long as your monitor and command inherit
from the base classes it will be detected and used automatically.
http://www.codeproject.com/Articles/43436/Undo-Redo-Framework
更新:
该类的所有者更新了代码,添加了我需要的东西之一,我要求的标签项撤销/重做操作.
·Listview中的撤消/重做文本更改(正常模式或详细信息模式)
Unafortunatelly这个更新不足以让我能够添加我需要的其他撤消/重做操作,请阅读@Plutonix评论解释事物
以下是更新类的一部分,适用于可以提出想法并帮助扩展它的人:
'**************************************************************************************************************** ' ListView Undo/Redo Example,(c) Copyright 2013 Etienne Nijboer '**************************************************************************************************************** ' This is an example implementation of the Monitor and Command to add support for listviewitem labeltext changes ' Only the two classes arre needed to add support for an additional control. There were no extra changes needed ' in other code because the UndoRedoManager class uses reflection to discover the new Monitor and if you check ' the message Box on startup you'll notice the new addition of the ListViewMonitor to the list. ' ' Hopefully this example makes it easier for others to understand the mechanism behind this and how to add ' undo/redo functionality for other actions and controls. ' ' Note: Beware that this example doesn't work if items in the listview can be sorted,moved and/or deleted. You ' would need to expand the Monitor for these actions and add Command classes as well. Hopefully this ' addition to will make it easier for you to do just that ;-) ' ' Good luck! ' '**************************************************************************************************************** ' Because we want to perform undo on a specific item at a certain index within the listview it is important this ' index is also stored. Otherwise we know that a label is changed but not to which item it belongs Structure ListViewUndoRedoData Public ItemIndex As Integer Public LabelText As String End Structure '**************************************************************************************************************** ' ListViewMonitor '**************************************************************************************************************** Public Class ListViewMonitor : Inherits BaseUndoRedoMonitor Private Data As ListViewUndoRedoData Public Sub New(ByVal AUndoRedoManager As UndoRedoManager) MyBase.New(AUndoRedoManager) End Sub Public Overrides Function Monitor(ByVal AControl As System.Windows.Forms.Control) As Boolean If TypeOf AControl Is ListView Then AddHandler CType(AControl,ListView).BeforeLabelEdit,AddressOf ListView_BeforeLabelEdit AddHandler CType(AControl,ListView).AfterLabelEdit,AddressOf ListView_AfterLabelEdit Return True End If Return False End Function Private Sub ListView_BeforeLabelEdit(sender As System.Object,e As System.Windows.Forms.LabelEditEventArgs) ' Before change,make sure to save the data of what it is you want to be able to undo later. Data.ItemIndex = e.Item Data.LabelText = CType(sender,ListView).Items(e.Item).Text End Sub Private Sub ListView_AfterLabelEdit(sender As System.Object,e As System.Windows.Forms.LabelEditEventArgs) ' Events that are also fired when the undo/redo value is changed by code,like change events,' it is important to make sure that no undo/redo command is added when performing a undo/redo action. If Not isPerformingUndoRedo Then If Not (Data.ItemIndex = e.Item And String.Equals(Data.LabelText,e.Label)) Then AddCommand(UndoRedoCommandType.ctUndo,New ListViewUndoRedoCommand(Me,sender,Data)) ListView_BeforeLabelEdit(sender,e) End If End If End Sub End Class '**************************************************************************************************************** ' ListViewUndoRedoCommand '**************************************************************************************************************** Public Class ListViewUndoRedoCommand : Inherits BaseUndoRedoCommand Public Sub New(ByVal AUndoMonitor As BaseUndoRedoMonitor,ByVal AMonitorControl As Control) MyBase.New(AUndoMonitor,AMonitorControl) Debug.Assert(False,"This constructor cannot be used because creating the current state of the control should be done at the actual undo or redo action!") End Sub Public Sub New(ByVal AUndoMonitor As BaseUndoRedoMonitor,ByVal AMonitorControl As Control,ByVal AUndoRedoData As Object) MyBase.New(AUndoMonitor,AMonitorControl,AUndoRedoData) End Sub Public ReadOnly Property Control() As ListView Get Return CType(UndoRedoControl,ListView) End Get End Property Private ReadOnly Property Data() As ListViewUndoRedoData Get Return CType(UndoRedoData,ListViewUndoRedoData) End Get End Property Private Function GetCurrentStateData() As ListViewUndoRedoData GetCurrentStateData.ItemIndex = Data.ItemIndex GetCurrentStateData.LabelText = Control.Items(Data.ItemIndex).Text End Function Public Overrides Sub Undo() MyBase.Undo(GetCurrentStateData()) Control.Items(Data.ItemIndex).Text = Data.LabelText End Sub Public Overrides Sub Redo() MyBase.Redo(GetCurrentStateData()) Control.Items(Data.ItemIndex).Text = Data.LabelText End Sub Public Overrides Function CommandAsText() As String Return String.Format("Item {0}: {1}",Data.ItemIndex,Data.LabelText) End Function End Class
UPDATE 2:
这就是作者所说的如何添加listview撤消/重做操作所需的功能:
I don’t think you need to rewrite the full class. The hardest part of
this,is finding a way of detecting when an item might be up for
removal and when it is actually deleted. In the ListViewMonitor you
will need to add the necessary event handlers (In the source where you
find the AddHandler for BeforeLabelEdit and AfterLabelEdit). For the
Command class you need to have the actual ListViewItem and the
position of the item in the ListView before it was removed. You can
simply create your structure with this information,something like
ListViewItemRemoveUndoRedoData. When you undo the removal,you simply
add the stored ListViewItem to the ListView at the position you stored
with that. I would suggest adding an extra Count to the
ListViewItemRemoveUndoRedoData structure that holds the number of
items in the listview. Furthermore I think the only event you need is
SelectedIndexChanged. When this event happens there are 2 situations.1- The number of items is the same as the count stored earlier (set it to -1 or something on creation of the monitor): You store the item,
position and the total item count.2- The number of items is less than the count you stored earlier: An item is removed and you setup its UndoRedoCommand so it can be undone.
- There is a 3th option of course,which would mean that an item is added
It needs some creativity to find the right events and what needs to be
stored to perform the undo/redo. It might even mean that you need to
find an alternative listview with better events and support (which you
can find right here on codeproject)
UPDATE 3:
试图遵循@ThorstenC解决方案,我遇到了RedoLastAction的问题,即使我没有撤消任何内容,它也会重做.
我也可以重做无限次,它只重做最后一个动作,我的意思是如果我撤消3个不同的LV项目,那么我只能重做最后添加的项目.
·UndoManager类:
Class ListView_UndoManager Public Property Undostack As New Stack(Of ListView_Action) Public Property Redostack As New Stack(Of ListView_Action) Private action As ListView_Action = Nothing ''' <summary> ''' Undo the top of the stack ''' </summary> ''' <remarks></remarks> Sub UndoLastAction() If Undostack.Count = 0 Then Exit Sub ' Nothing to Undo. action = Undostack.Pop ' Get the Action from Stack. action.Operation.DynamicInvoke(action.data) ' Invoke the reverse Action . End Sub ''' <summary> ''' Redo the top of the stack ''' </summary> ''' <remarks></remarks> Sub RedoLastAction() If Redostack.Count = 0 Then Exit Sub ' Nothing to Redo. action = Redostack.Peek ' Get the Action from Stack,but don't remove it. action.Operation.DynamicInvoke(action.data) ' Invoke the reverse 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 Object() End Class
·主要表格代码:
' Undo/Redo Dim _undoManager As New ListView_UndoManager Delegate Sub RemoveDelegate(item As Object) Delegate Sub AddDelegate(text As String,subtext1 As String,subtext2 As String) ' Button Add Song [Click] Private Sub Button_Add_Song_Click(sender As Object,e As EventArgs) _ Handles Button_Add_Song.Click AddItem(ListView_Monitor.Items.Count + 1,WinampFile,ComboBox_Sendto.Text) End Sub Sub AddItem(ByVal name As String,ByVal subitem1 As String,ByVal subitem2 As String) Dim newItem = ListView_Monitor.Items.Add(name) newItem.SubItems.Add(subitem1) newItem.SubItems.Add(subitem2) 'Crate an Undo Operation Dim u As New ListView_Action() With {.name = "Remove Item",.Operation = New RemoveDelegate(AddressOf RemoveItem),.data = New Object() {newItem}} _undoManager.Undostack.Push(u) ' Create a Redo Dim r As New ListView_Action() With {.name = "Add Item",.Operation = New AddDelegate(AddressOf AddItem),.data = New Object() {name,subitem1,subitem2}} _undoManager.Redostack.Push(r) End Sub Sub RemoveItem(item As Object) ListView_Monitor.Items.Remove(item) End Sub
忘记当前的实现,开始实现自己的Undo / Redo类.
每个操纵某些东西的方法都需要创建自己的撤销方法.
存储代理并在需要时调用它.我已经通过简单的添加/删除listview项目做了一个例子.
Public Class Form1 Dim _undoManager As New UndoManager ''' <summary> ''' Delegates to Remove an item ''' </summary> ''' <param name="rowNumber"></param> ''' <remarks></remarks> Delegate Sub RemoveDelegate(item As Object) ''' <summary> ''' Delegates to Add an Item ''' </summary> ''' <param name="text"></param> ''' <remarks></remarks> Delegate Sub AddDelegate(text As String) Sub AddItem(name As String) Dim newItem = ListView1.Items.Add(name) 'Crate an Undo Operation Dim a As New action() With {.name = "Remove Item",.data = New Object() {newItem}} _undoManager.Undostack.Push(a) ' Create a Redo Dim a As New action() With {.name = "Add Item",.data = New Object() {name}} _undoManager.Redostack.Push(a) End Sub Sub RemoveItem(item As Object) ListView1.Items.Remove(item) End Sub ''' <summary> ''' Changes the Text of the Item ''' </summary> ''' <param name="item"></param> ''' <param name="text"></param> ''' <remarks></remarks> Sub changetext(item As Object,text As String) Dim oldtext As String = item.text End Sub Private Sub Button1_Click(sender As System.Object,e As System.EventArgs) Handles Button1.Click Me.AddItem("new Item") End Sub Private Sub Button2_Click(sender As System.Object,e As System.EventArgs) Handles Button2.Click _undoManager.UndoLastAction() End Sub End Class Class UndoManager Public Property Undostack As New Stack(Of action) Public Property Redostack As New Stack(Of action) ''' <summary> ''' Undos the top of the stack ''' </summary> ''' <remarks></remarks> Sub UndoLastAction() Dim action As action = Undostack.Pop ' Get the Action from Stack action.Operation.DynamicInvoke(action.data) ' Invoke the reverse Action End Sub Sub RedoLastAction() Dim action As action = Redostack.Peek' Get the Action from Stack,but dont remove action.Operation.DynamicInvoke(action.data) ' Invoke the reverse Action End Sub End Class Class 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 Object() End Class