【问题标题】:Implement Undo/Redo operations for Adding/Deleting ListView Items为添加/删除 ListView 项实现撤消/重做操作
【发布时间】:2013-11-14 10:42:20
【问题描述】:

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

我在此之前意识到 Extend this Class to Undo/Redo in a Listview 的一个相对问题,在那里我开始获得 50、100、200 和 300 点的多个赏金,总共 650 点......但没有人能真正帮助我在几周和几个月。

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

问题是简单的“撤消”工作正常,但是当我尝试重做 1 次以上时,它会引发异常,因为它无法在列表视图中再次添加相同的项目,而且代码还有更多问题,例如在当我无法重做撤消操作或撤消重做操作时。

只是我需要帮助来为 Listview 项添加/删除创建一个有效的撤消/重做管理器,仅此而已,我已经编写了一半的代码,我需要帮助来完成它我对此一团糟.

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

http://elektrostudios.tk/UndoManager.zip

这是一个视频,向您展示我尝试撤消/重做的错误: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

这是我试图撤消/重做添加/删除列表视图项的其余代码:

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

【问题讨论】:

  • 请缩短您的代码以表示相关部分。删除不必要的 cmets,请指出你的异常是在哪一行抛出的!
  • @Pilgerstorfer Franz 感谢您的评论,抱歉,代码和我的话不能少,因为我需要提供所有必要的信息,在这一行抛出异常:_undoManager.RedoLastAction()you可以在我上传的视频中看到它,但这个异常并不是问题的全部,我会从我的问题中引用这个,请阅读:The problem is when I try to redo more than 1 time it throws an exception about it cant add the same item again in the listview, also the code has more problems for example ATM I'm not able to redo a undo operation, or undo a redo operation

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


【解决方案1】:

“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 中的项目。

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

此外,添加项目会落入 UnDo 存储桶,但 RemoveItem 不会落入 RemoveItem,反之亦然(无法撤消 RemoveItem)。在原始 Undo 中自动将命令添加到 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 版本的语法):

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 操作。看起来只是因为人工测试数据。

【讨论】:

  • 谢谢你的回答,"El URL requerido no fue encontrado en este servidor."我已经修复了网址,如果你想尝试修复代码,现在你可以下载解决方案来查看它。关于 peek/pop 我错误地写了“peek”,但在发布问题之前我已经尝试了“all”(peek/pop/push/reversing/clear)。只是为了注释我的 LV 有一个 LVItemAdded 和 LVItemRemoved 事件。
  • 关于你回答的最后一段我知道代码有这些问题,我的意思是当我做“撤消”时我不添加重做,当我做“重做”时我不'不加撤消,但是重做代码错了,但是我不能继续尝试在撤消的时候构建重做,在重做的时候撤消,因为我必须先更正重做代码才能更正其他两个问题,目前的重做是错了,我无法在没有帮助的情况下修复它们
  • 我知道这是不对的,但我想说我也尝试过使用“pop”但没有得到解决。 PS:对不起我的英语。
  • 如果不是太麻烦问你你认为你可以检查项目以帮助通过示例(修复)修复代码吗?你知道我的旧要求我真的很需要和绝望,无论如何真的感谢你的信息!但我需要的不仅仅是这个该死的 UndoManager 的信息 :(
  • 对不起老兄,我把“Peek”改成了“Pop”,效果很好。那是您使用的实际测试台吗?我不得不注释掉你的怪异代码负载以使其绘制并且按钮可见,但之后它工作正常(嗯,它都是手动的,这可能是为什么堆栈是公共的并且 UndoManager 没有做任何工作??)。它还将 SubItems 与 Items 混为一谈,而不是单独的 Undo 候选者?我还将它链接到一个普通的 LV,它工作正常(在将 AddItem 更改为 items.Add 等之后)
【解决方案2】:

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

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

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

  • 文本框
  • 组合框
  • 日期时间选择器
  • NumericUpDown
  • MaskedTextBox
  • ListBox(单选和多选)
  • 复选框
  • 单选按钮
  • 月历
  • ListView(标签文本更改)

【讨论】:

    猜你喜欢
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2012-07-16
    • 2010-09-30
    • 2014-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多