Well, I have to talk about the second part of this question. Introduce Undo / Redo operations to add / remove ListView elements , and another. Extend this class to undo / redo in Listview .
I am trying to perform Undo / Redo operations to add / remove ListView items.
I have made some progress with the encoding of this LV UndoManager code, but it is very difficult for me whenever I try to advance.
At the moment, I can add individual elements, and then I can completely undo / redo that ADDED elements are no more.
I have the following problems:
When I remove one item from the list, I canβt βcancelβ to add this item to LV again.
When I add a range, if the elements cannot cancel, When I call UndoLastAction , it throws a System.Reflection.TargetParameterCountException exception
. When I delete a series of items, I cannot undo / redo the operation and throw the same exception.
In summary, if I add one item, I can quickly undo / redo it, if I delete one item that I cannot undo correctly, also I cannot undo / change the range of ListViewItems.
I need someone who can help me fix these problems ... or at least one of them with patience.
The code is a bit big, so I think it may take less time to understand and find errors when opening and testing this source project, which I downloaded.
Here is the full Source:
http://elektrostudios.tk/UndoManager%20Test%20Application.zip
Simple image:

here is the UndoManager class:
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
Here is the ListView control that I use, I submit it, because the Events I fire are ItemAdded : ItemAdded , ItemRemoved , RangeItemAdded and RangeItemRemoved .
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
And finally, here is the Form1 code of the test application, this is what I use to add / remove items and to call undo / redo, but I am referring to the methods of my custom ListView control, so you need to notice that ...
Public Class Form1 Dim _undoManager As New ListView_UndoManager Dim LVItem As ListViewItem Delegate Sub AddDelegate(item As ListViewItem) Delegate Sub RemoveDelegate(item As ListViewItem) Delegate Sub AddRangeDelegate(item As ListViewItem()) Delegate Sub RemoveRangeDelegate(item As ListViewItem()) ' Adds a single item Private Sub Button_AddItem_Click(sender As Object, e As EventArgs) _ Handles Button_AddItem.Click Dim index As String = CStr(LV1.Items.Count + 1) LVItem = New ListViewItem With {.Text = index} LVItem.SubItems.AddRange({"Hello " & index, "World " & index}) LV1.AddItem(LVItem) End Sub ' Adds a range of 2 items to the ListView Private Sub Button_AddRange_Of_Items_Click(sender As Object, e As EventArgs) Handles Button_AddRange_Of_Items.Click Dim index As String = CStr(LV1.Items.Count + 1) Dim lvitem As New ListViewItem With {.Text = index} lvitem.SubItems.AddRange({"Hello " & index, "World " & index}) Dim lvitem2 As New ListViewItem With {.Text = index + 1} lvitem2.SubItems.AddRange({"Hello " & index + 1, "World " & index + 1}) LV1.AddItem_Range({lvitem, lvitem2}) End Sub ' Removes the last item Private Sub Button_RemoveLastItem_Click(sender As Object, e As EventArgs) _ Handles Button_RemoveLastItem.Click If LV1.Items.Count <> 0 Then LV1.RemoveItem(LV1.Items.Cast(Of ListViewItem).Last) End If End Sub ' Clear all items Private Sub Button_Clear_Items_Click(sender As Object, e As EventArgs) _ Handles Button_Clear_Items.Click LV1.Items.Clear() End Sub ' Clear the Undo/Redo Stacks Private Sub Button_Clear_Stacks_Click(sender As Object, e As EventArgs) _ Handles Button_Clear_Stacks.Click _undoManager.Undostack = New Stack(Of ListView_Action) _undoManager.Redostack = New Stack(Of ListView_Action) Label_UndoCount_Value.Text = CStr(0) Label_RedoCount_Value.Text = CStr(0) End Sub ' Refreshes the Stacks Count Private Sub Refresh_StackCount() Label_UndoCount_Value.Text = CStr(_undoManager.Undostack.Count) Label_RedoCount_Value.Text = CStr(_undoManager.Redostack.Count) End Sub ' Monitors when an Item is added Private Sub ListView_ItemAdded(sender As Object, e As LV.ItemAddedEventArgs) _ Handles LV1.ItemAdded ' // Crate an Undo Action Dim u As New ListView_Action() With u .name = "Remove Item" .Operation = New RemoveDelegate(AddressOf LV1.RemoveItem) .data = {e.Item} End With _undoManager.Undostack.Push(u) Refresh_StackCount() End Sub ' Monitors when a range of Items are added Private Sub ListView_RangeItemAdded(sender As Object, e As LV.RangeItemAddedEventArgs) _ Handles LV1.RangeItemAdded ' // Crate an Undo Action Dim u As New ListView_Action() With u .name = "Remove Item Range" .Operation = New RemoveRangeDelegate(AddressOf LV1.RemoveItem_Range) .data = e.Items End With _undoManager.Undostack.Push(u) Refresh_StackCount() End Sub ' Monitors when an Item is removed Private Sub ListView_ItemRemoved(sender As Object, e As LV.ItemRemovedEventArgs) _ Handles LV1.ItemRemoved ' // Create a Redo Action Dim r As New ListView_Action() With r .name = "Add Item" .Operation = New AddDelegate(AddressOf LV1.AddItem) .data = {e.Item} End With _undoManager.Redostack.Push(r) Refresh_StackCount() End Sub ' Monitors when a range of Items are removed Private Sub ListView_RangeItemRemoved(sender As Object, e As LV.RangeItemRemovedEventArgs) _ Handles LV1.RangeItemRemoved ' // Create a Redo Action Dim r As New ListView_Action() With r .name = "Add Item" .Operation = New AddRangeDelegate(AddressOf LV1.AddItem_Range) .data = e.Items End With _undoManager.Redostack.Push(r) Refresh_StackCount() End Sub ' Undo Private Sub Button_Undo_Click(sender As Object, e As EventArgs) _ Handles Button_Undo.Click _undoManager.UndoLastAction() End Sub ' Redo Private Sub Button_Redo_Click(sender As Object, e As EventArgs) _ Handles Button_Redo.Click _undoManager.RedoLastAction() End Sub Private Sub Button_Remove_Range_Of_Items_Click(sender As Object, e As EventArgs) Handles Button_Remove_Range_Of_Items.Click If LV1.Items.Count > 1 Then Dim lvi1 As ListViewItem = LV1.Items(LV1.Items.Count - 1) Dim lvi2 As ListViewItem = LV1.Items(LV1.Items.Count - 2) LV1.RemoveItem_Range({lvi1, lvi2}) End If End Sub End Class
PS: As I said, it would really be very down-source friendly and test it.