扩展此类以在列表视图中撤消/重做

2023-11-26

我正在使用第三方代码来管理 Windows 窗体项目中的撤消/重做操作。

我需要扩展该类来管理列表视图中的撤消/重做操作,这意味着:

· 撤消/重做添加/删除项目和子项目

· 撤消/重做检查/取消检查行

· 撤消/重做一些我可能错过的其他重要事情

我不知道如何开始这样做,代码对我来说太复杂了,任何关于这方面的帮助/提示/示例对我来说都会非常满意,但是在 3 个月内我一直无法进行这个更改,我我想我需要很好的解释或完整的例子,这里是代码:

********************************************************
 Undo/Redo framework (c) Copyright 2009 Etienne Nijboer
********************************************************

http://pastebin.com/Gmh5HS4x

(我没有在这里发布代码,因为它超出了 StackOverflow 的 30.000 个字符限制)

UPDATE:

这是作者提供的一些有用信息,解释了我需要做的事情来添加列表视图支持,但实际上我自己做不到:

顺便说一句,为列表视图添加功能应该不那么难 也是了解其工作原理的好方法。你需要 创建一个新的监视器来捕获列表视图更改事件并 在更改之前存储当前值。如果满足以下条件,则创建命令 您检测到已使用撤消所需的所有信息进行了更改或 重做该动作。就是这样。只要你的监视器和命令继承 从基类中,它将被自动检测和使用。

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

UPDATE:

该类的所有者更新了代码,添加了我需要的内容之一,即我要求的标签项撤消/重做操作。

· 撤消/重做列表视图内的文本更改(普通模式或详细模式)

不幸的是,这个更新不足以让我添加我需要的其他撤消/重做操作,请阅读 @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

更新2:

这是作者所说的如何添加列表视图撤消/重做操作所需的功能:

我认为你不需要重写整个课程。最难的部分 这是寻找一种方法来检测某个物品何时可能会被出售 删除以及实际删除的时间。在ListViewMonitor中你 将需要添加必要的事件处理程序(在您所在的源代码中) 找到 BeforeLabelEdit 和 AfterLabelEdit 的 AddHandler)。为了 命令类您需要有实际的 ListViewItem 和 项目在被删除之前在 ListView 中的位置。你可以 只需使用这些信息创建您的结构,例如 ListViewItemRemoveUndoRedoData。当您撤消删除操作时,您只需 将存储的ListViewItem添加到ListView中您存储的位置 接着就,随即。我建议添加一个额外的计数 ListViewItemRemoveUndoRedo保存数量的数据结构 列表视图中的项目。此外,我认为你唯一需要的事件是 选定索引已更改。当这个事件发生时,有两种情况。

1- 项目数与之前存储的计数相同(将其设置为 -1 或创建监视器时的其他值):您存储项目, 位置和总项目数。

2- 项目数量小于您之前存储的数量:项目被删除,您设置其 UndoRedoCommand 以便可以撤消它。

  • 当然还有第三个选项,这意味着添加一个项目

需要一些创造力来找到正确的事件和需要做的事情 存储以执行撤消/重做。这甚至可能意味着您需要 找到一个具有更好事件和支持的替代列表视图(您可以 可以在 codeproject 上找到)


更新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

如果仔细观察第 328 行,它已经处理了一个 ListView。是不是在某些方面有所欠缺?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

扩展此类以在列表视图中撤消/重做 的相关文章

  • 如何在模型更改时停止ListView“跳跃”

    我需要做什么 我需要创建一个聊天窗口用一个ListView在 QML 中存储聊天消息 我设置listView positionViewAtEnd 以便跟踪最后的消息 我禁用positionViewAtEnd当我向上滚动时 我可以阅读过去的消
  • 使用 AesManaged“填充无效且无法删除”

    我正在尝试使用 AesManaged 进行简单的加密 解密 但在尝试关闭解密流时不断出现异常 这里的字符串被正确加密和解 密 然后在 Console WriteLine 打印正确的字符串后 我收到 CryptographicExceptio
  • 从 datagridview 选定的行更新 mysql 数据库

    我有一个 datagridview 它在表单加载事件上加载 mysql 数据库表 t pi clients 并且我有另一个选项卡 其中包含 t pi client 相应列的文本框 它能够从 fullrowselect 模式获取数据到这些文本
  • 无法更新 .mdf 数据库,因为该数据库是只读的(Windows 应用程序)

    我使用 C 创建了一个数据库 Windows 应用程序 我的应用程序在 Windows XP 上成功运行 但在 Vista 或 Windows 7 系统上无法正确执行 我的应用程序显示类似以下内容的消息 无法更新 mdf 数据库 因为该数据
  • 使用32位应用程序获取syswow64目录

    我正在尝试在系统目录中查找文件 问题是当使用 Environment SystemDirectory 在 x64 计算机上 我仍然获得 System32 目录 而不是 Systemwow64 目录 我需要在 x86 机器上获取 System
  • 专家 C#/.Net/WPF 开发人员应该了解哪些知识? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 在 C# 中将 ulong 映射到 long ?

    我正在尝试将 ulong 映射到 long 反之亦然 将 uint 映射到 int 反之亦然 如下所示 为了将值保存在具有签名类型的 MS SQL 数据库中仅限整数和大整数 我这样做是因为我必须检查 在数据库中 一个数字 uint ulon
  • 父窗体中的居中消息框[重复]

    这个问题在这里已经有答案了 有没有一种简单的方法可以在 net 2 0中将MessageBox居中于父窗体中 我在 C 中确实需要这个并发现中心消息框 C http bytes com topic c sharp answers 26712
  • 中继命令和无参数执行方法

    我目前正在学习 WPF 和 MVVM 我想我已经了解了大部分内容及其工作原理 但我在使用 RelayCommand 或 DelegateCommand 时遇到了一些我不明白的问题 我认为这与代表的工作方式有关 请注意 下面的代码目前仅处于测
  • 使用 wmi 获取活动会话(Win32_LogonSession 还返回非活动/旧会话)

    有没有办法只显示 wmi 的活动会话 问题是 Win32 LogonSession 还显示不活动 断开连接的会话 ManagementScope scope new ManagementScope ManagementPath Defaul
  • 如何暂时暂停实时数据图表的绘制更新

    我想要 暂停 图表的系列更新来完成一些工作 就像我有一个按钮 当我单击它时 它将暂停图表更新 然后当我单击恢复按钮时 它将更新系列中的所有暂停点 我知道关于 chart1 Series SuspendUpdates 但它似乎不适合我 我使用
  • 依赖注入的惰性解析

    我有 net 课程 我使用 Unity 作为 IOC 来解决我们的依赖关系 它尝试在开始时加载所有依赖项 Unity中有没有一种方法 设置 允许在运行时加载依赖项 还有更好的解决方案 Unity 2 0 中对 Lazy 和 IEnumera
  • Sencha Touch 和远程服务器上的身份验证

    我希望收到您关于使用 PhoneGap 和 Sencha Touch 的应用程序以及带有 Active Directory 的 NET 服务器的身份验证机制的反馈 因此 我们需要在移动设备上存储用户凭据 以便用户每次希望使用该应用程序时都不
  • 学习实体框架[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 查询 dns 别名

    我找到了一些code http msdn microsoft com en us library system net dns gethostbyaddress VS 71 aspx来自 msdn 站点 下面包含代码 看起来它将返回给定服务
  • 使用 SqlDataReader.IsDBNull 时使用列名

    我已经得到了从 SQL DB 读取数据的代码 我不知道应该如何编辑它 以便我可以使用原始列名称而不是列索引 string query SELECT FROM zajezd WHERE event thisrow AND year klien
  • 使用 iTextSharp CSSResolver 类的命名空间

    我正在使用 iTextSharp CSSResolver 类在 vb net 中使用外部 css 请让我知道使用的命名空间XMLWorkerFontProvider CssFilesImpl StyleAttrCSSResolver类 下面
  • If 语句中 Bool 计算错误

    只是为了好奇 我的代码有这个问题 e被评估为false 我知道通过查看数据库中的数据会得到错误 但 if 语句并不关心这一点 并假设这是真的 并试图抛出异常 有什么想法吗 edit 没有 在第 16 行末尾 价值false是正确的 我已经检
  • .NET 迭代器包装抛出 API

    我有一个带有 API 的类 它允许我请求对象 直到它抛出一个IndexOutOfBoundsException 我想将它包装到一个迭代器中 以便能够编写更清晰的代码 但是 我需要捕获异常以停止迭代 static IEnumerable It
  • 从嵌入 C# 的浏览器控件获取 HTML 源代码

    我有一个嵌入在 C Windows 应用程序中的浏览器控件 我想获取渲染的 HTML 可能已被 javascript 修改 而不是原始 HTML 有什么建议么 您可以获取 HTML 并确实设置它 使用WebBrowser DocumentT

随机推荐

  • Android Studio 中的 NDK 集成错误

    我正在使用 Android studio 版本 1 3 1 并尝试通过转到 local proprties 并编写 ndk dir 来集成 NDK 但出现此错误 错误 任务 tesstwo compileReleaseNdk 执行失败 错误
  • Android 连续语音识别返回 ERROR_NO_MATCH 太快

    我尝试实现连续语音识别机制 当我开始语音识别时 我在 logcat 中收到以下消息 06 05 12 22 32 892 11753 11753 com aaa bbb D SpeechManager startSpeechRecognit
  • 如何在 Java 中比较字符串?

    这个问题的答案是社区努力 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我一直在使用 我的程序中的运算符来比较到目前为止我的所有字符串 然而 我遇到了一个错误 将其中一个更改为 equals 相反 它修复了该错误 Is 坏的 什么时
  • 如何通过环境在 aurelia 中设置常量

    我希望将我的 API 基本 URL 更改为dev to prod 在 Angular I 用户中使用config json文件 后来使用注入到应用程序中grunt env 如果您使用奥里利亚 CLI 它将生成一个environments你的
  • 如何在代码编辑器中禁用自动完成代码语句?

    每次我打字if然后按空格键 Delphi 完成它if True then和上面一个新的空行 有没有办法去除this 自动完成 功能或至少编辑它以不创建新行 来自工具 选项 编辑器选项 代码洞察菜单中 取消选择代码模板完成下的自动完成复选框
  • 如何以精确的时间(即 10 毫秒)定期运行 Erlang 进程

    我想每 10 毫秒运行一次定期的 erlang 进程 基于挂钟时间 10 毫秒应该尽可能准确 正确的实施方法应该是什么 如果您想要真正可靠和准确的定期过程 您应该依靠实际的挂钟时间使用erlang monotonic time 0 1 如果
  • 如何运行 test/a.py 形式的单元测试?

    是否可以实现一个具有如下文件结构的Python项目 myproj a py b py c py test a py b py c py Note in particular that the test scripts under test
  • Mongodb - 使用 addToSet 的项目计数

    我分组依据organization并使用 addToSet来展现与众不同的machineIds与此相关的organization 我想计算machineIds对于每个organization 然而 下面的代码返回所有的计数machineId
  • flutter查询firestore中的多个集合

    我正在玩 flutter 但我遇到了 firestore 的问题 我无法弄清楚 假设我想检索客户的购买者历史记录 并且我有一个如下所述的 Firestore 因此我有一个 用户 集合 其中包含以下文档user id然后在其中 我有一个 产品
  • Identity 2.1 - 未找到 UserId 但之前可以使用

    该代码之前多次工作 但在 Identity 2 1 中为用户添加几个新属性后 它突然停止工作 尽管在调试器中可以看到 UserId 的值 但我收到了 UserId not found 错误 任何人都知道为什么会突然发生这种情况 至少看到这一
  • 窗口卸载事件的本地存储

    我使用本地存储来存储一些数据 用户发出 ajax 请求来获取信息 我将结果存储在存储中 以便下次他请求相同的信息时 我首先在存储中查看它是否存在 现在我意识到 将数据保存在内存中的对象中 并在需要时循环该数据 而不是循环本地存储 实际上更有
  • Delphi 的 WebSocket 客户端实现

    Delphi 有免费的 WebSocket 客户端实现吗 我只找到了这个 WebSockets Delphi 组件 但它不是免费的 这是我的开源库 https github com andremussche DelphiWebsockets
  • Django 2.1 - 'functools.partial' 对象没有属性 '__name__'

    我最近将 Django 从 2 0 7 升级到 2 1 1 出现了一个新错误 其中出现此错误 functools partial object has no attribute name 我想了解我的修复是否正确以及是什么导致了这个新错误的
  • 对核心数据实体进行排序的最佳方法是什么?

    我有一个完全正常工作的核心数据模型 但是当我使用获取请求返回数据时 它的顺序看似随机 对这些数据进行排序的最佳方法是什么 是使用核心数据模型中的另一个表 然后 查询 第一个表吗 或者是将数据拉入数组中 然后以这种方式排序 我不太确定如何做其
  • ggplot 函数在图例下方添加文本

    在 R 中 我想创建一个函数 它接受 ggplot 对象和一些文本并返回 ggplot 对象 方法是在图例下方添加文本 在图的右侧 同时将图例保留在右侧 myplot ggplot iris aes x Sepal Length y Sep
  • 单一来源项目结构有哪些缺点?

    我是目前公司的新人 正在从事由我的直接团队领导编写的项目 该公司通常不使用 C 但我的同事用 C C 编写了高效的代码 只有我们知道如何用 C 编码 我和我的领导 所以没有第三种意见可以涉及 在我对这个项目有了足够的了解之后 我意识到整个结
  • 什么是无界数组?

    什么是无界数组 无界数组和动态分配数组有什么区别 与无界数组相关的常见操作有哪些 就像我们有堆栈数据结构的弹出和推送 无界数组可以 并且通常是 静态分配 实现无界数组时的主要关注点是提供类似动态数组的自由来在运行时决定数组大小 而不会因运行
  • 与序列化相比,使用 MarshalByRefObject 的成本有多高?

    在我的 Azure Web 角色代码中 我有一个CustomIdentity类派生自System Security Principal IIdentity 在某些时候 NET 运行时尝试序列化该类 and 序列化不起作用 试图解决我搜索了很
  • 为什么 range-for 找不到 std::istream_iterator 的 begin 和 end 重载?

    我有这样的代码 std ifstream file filename std ios base in if file good file imbue std locale std locale new delimeter tokens fo
  • 扩展此类以在列表视图中撤消/重做

    我正在使用第三方代码来管理 Windows 窗体项目中的撤消 重做操作 我需要扩展该类来管理列表视图中的撤消 重做操作 这意味着 撤消 重做添加 删除项目和子项目 撤消 重做检查 取消检查行 撤消 重做一些我可能错过的其他重要事情 我不知道