实现添加/删除ListView项的撤消/重做操作

时间:2013-11-03 19:34:27

标签: .net vb.net winforms listview undo-redo

尝试在ListView控件中实现撤消/重做操作时遇到太多问题,只是为了添加/删除项目。

我很久以前就意识到这里有一个相对的问题Extend this Class to Undo/Redo in a Listview我开始了50分,100分,200分和300分的多个分数,共计650分......但是没有任何身体可以真正帮助我完成这个几周和几个月的问题。

但是经过一段时间后,一个用户(@ThorstenC)向我展示了一个可能的解决方案和一个好主意,他的代码是不完整的,所以他的代码就是我想要实现/完成的。

问题是简单的“撤销”工作正常,但是当我尝试重做超过1次时它抛出一个异常,它不能在listview中再次添加相同的项目,代码也有更多的问题,例如在我无法重做撤消操作或撤消重做操作的那一刻。

我需要帮助才能为Listview项目添加/删除创建一个有效的Undo / Redo管理器,就是这样,我已经编写了代码的一半部分,我需要帮助才能完成它我脑子里有一个乱七八糟的东西

这是VS2012中的一个简单的WinForms源项目,我上传以测试撤消管理器失败:

http://elektrostudios.tk/UndoManager.zip

enter image description here

这是一段视频,向您展示我尝试撤消/重做的错误:http://www.youtube.com/watch?v=MAzChURATpM

这是@ThorstenC的UndoManager类,有一点点修饰:

Class ListView_UndoManager

    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

    Private action As ListView_Action = Nothing

    ''' <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.

        'Redostack = New Stack(Of ListView_Action)(Redostack)
        'Redostack.Pop()
        'Redostack = New Stack(Of ListView_Action)(Redostack)

    End Sub

    ''' <summary>
    ''' Redo the last action.
    ''' </summary>
    ''' <remarks></remarks>
    Sub RedoLastAction()

        ' If Redostack.Count = Undostack.Count Then Exit Sub

        If Redostack.Count = 0 Then Exit Sub ' Nothing to Redo.

        'Redostack = New Stack(Of ListView_Action)(Redostack) ' Reverse the Stack contents.

        action = Redostack.Pop() ' Get the Action from Stack and remove it.
        ' action = Redostack.Peek()

         action.Operation.DynamicInvoke(action.data) ' Invoke the redo Action.

        'Redostack = New Stack(Of ListView_Action)(Redostack) ' Re-Reverse the Stack contents.

    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

以下是我正在尝试撤消/重做添加/删除listview项目的其余代码:

Public Class Form1


    Dim _undoManager As New ListView_UndoManager
    Delegate Sub RemoveDelegate(item As ListViewItem)
    Delegate Sub AddDelegate(item As ListViewItem)

    Dim newItem As ListViewItem = Nothing



    Sub AddItem(ByVal item As ListViewItem)

        ' // Crate an Undo Action
        Dim u As New ListView_Action() With {.name = "Remove Item",
                            .Operation = New RemoveDelegate(AddressOf RemoveItem),
                                    .data = New Object() {newItem}}

        _undoManager.Undostack.Push(u)

        ListView_Elektro1.AddItem(item)

    End Sub

    Sub RemoveItem(item As ListViewItem)

        ' // Create a Redo Action
        Dim r As New ListView_Action() With {.name = "Add Item",
                    .Operation = New AddDelegate(AddressOf AddItem),
                            .data = New Object() {item}}

        _undoManager.Redostack.Push(r)

        ' Remove the ListViewItem from ListView
        ListView_Elektro1.RemoveItem(item)

    End Sub

    Private Sub Button_AddItem_Click(sender As Object, e As EventArgs) _
    Handles Button_AddItem.Click

        Dim index As String = CStr(ListView_Elektro1.Items.Count + 1)

        newItem = New ListViewItem _
                  With {.Text = index}
        newItem.SubItems.AddRange({"Hello " & index, "World " & index})

        AddItem(newItem)

    End Sub

    Private Sub Button_RemoveItem_Click(sender As Object, e As EventArgs) _
    Handles Button_RemoveItem.Click

        newItem = ListView_Elektro1.Items.Cast(Of ListViewItem).Last

        RemoveItem(newItem)

    End Sub

    Private Sub Button_Undo_Click(sender As Object, e As EventArgs) _
    Handles Button_Undo.Click

        ' _undoManager.IsDoingUndo = True
        _undoManager.UndoLastAction()
        ' _undoManager.IsDoingUndo = False

    End Sub

    Private Sub Button_Redo_Click(sender As Object, e As EventArgs) _
    Handles Button_Redo.Click

        '_undoManager.IsDoingRedo = True
        _undoManager.RedoLastAction()
        '_undoManager.IsDoingRedo = False

    End Sub

    Private Sub ListView_Elektro1_ItemAdded() _
    Handles ListView_Elektro1.ItemAdded, _
            ListView_Elektro1.ItemRemoved

        Label_UndoCount_Value.Text = CStr(_undoManager.Undostack.Count)
        Label_RedoCount_Value.Text = CStr(_undoManager.Redostack.Count)

    End Sub

End Class

2 个答案:

答案 0 :(得分:2)

“El URL requerido no fue encontrado en este servidor。”所以我很确定这就是它:

action = Redostack.Peek() ' Get the Action from Stack and remove it.

不,你正在看没有从堆栈中获取它。原作和快速返工都是我用过的:

action = Redostack.Pop() 

由于您正在堆叠中存储实际的LV项目以回发给LV,第二次按下它时,您正在查看并尝试恢复已经在LV中的一个。

由于大多数原始“命令”将Undo / ReDo数据保存为对象,为什么你只是在UnDoReDoManager上公开AddLVUndoItem(item)以使用现有代码将LV动作与其他控件集成?它的问题是没有LVItemAdded事件来自动抓取这些东西。将此作为用户控制功能与另一个一起使用的一个问题是,您现在有2个堆栈,一个跳过LV,另一个只执行LV。用户可以清空另一个堆栈,试图进行LV撤消操作。

此外,添加项目属于UnDo存储桶,但不属于RemoveItem,反之亦然,适用于RemoveItem(无法撤消RemoveItem)。在原始撤消中自动将命令添加到ReDo堆栈。它在标题和旧请求中,但不在代码中。

修改 这是错的:

Sub RemoveItem(item As ListViewItem)
    ' // Create a Redo Action
    Dim r As New ListView_Action() With {.name = "Add Item",
                .Operation = New AddDelegate(AddressOf AddItem),
                        .data = New Object() {item}}   ' wrong!

    _undoManager.Redostack.Push(r)

    ' Remove the ListViewItem from ListView
    ListView_Elektro1.RemoveItem(item)
End Sub

你没有为undoStack创建一个新的LVI,使用传递的那个删除的那个(回想一下我必须改变我的VS ver的语法):

Sub RemoveItem(ByVal item As ListViewItem)

    ' // Create a Redo Action
    Dim r As New ListView_Action()
    With r
        .name = "Add Item"
        .Operation = New AddDelegate(AddressOf AddItem)
        .data = item           ' use the one passed!!!
    End With

    _undoManager.Redostack.Push(r)

    ' Remove the ListViewItem from ListView
    LVE.RemoveItem(item)
    _undoManager.ShowStacks()

End Sub

因此,您的ReDo没有缓存任何UnDo操作。它看起来只是由于人工测试数据。

答案 1 :(得分:1)

您可能还想查看用VB.NET编写的这个撤销/重做框架

http://www.codeproject.com/Articles/43436/Undo-Redo-Framework

它专为以下类型的控件而设计(但大多数情况下也应该使用自定义控件)

  • TextBox
  • ComboBox
  • DateTimePicker
  • NumericUpDown
  • MaskedTextBox
  • ListBox(单选和多选)
  • CheckBox
  • RadioButton
  • MonthCalendar
  • ListView(标签文字更改)