2

在一些人的帮助下,我使用UndoRedo Manager完成了这个用户控件。

目前它只能在您添加或删除项目时撤消/重做,仅此而已,但我想实现 LabelEdit 撤消/重做和复选框撤消/重做。

PS:请注意,Listview_Action期望ListviewItem

这是撤消/重做管理器类,不是完整的代码,只是必要的。

我需要在这个类中执行哪些更改来添加我需要的改进?

如果有人可以向我展示完整的代码示例,我将不胜感激。

Public Class myListView : Inherits ListView

    Public Event ItemAdded As EventHandler(Of ItemAddedEventArgs)
    Public Class ItemAddedEventArgs : Inherits EventArgs
        Property Item As ListViewItem
    End Class

    Public Event ItemRemoved As EventHandler(Of ItemRemovedEventArgs)
    Public Class ItemRemovedEventArgs : Inherits EventArgs
        Property Item As ListViewItem
    End Class

#Region " Undo/Redo Manager "

    ''' <summary>
    ''' Enable or disble the Undo/Redo monitoring.
    ''' </summary>
    Public Property Enable_UndoRedo_Manager As Boolean = False

    ' Stacks to store Undo/Redo actions.
    Private Property Undostack As New Stack(Of ListView_Action)
    Private Property Redostack As New Stack(Of ListView_Action)

    ' Flags to check if it is doing a Undo/Redo operation.
    Private IsDoingUndo As Boolean = False
    Private IsDoingRedo As Boolean = False

    ' Delegate to Add an Item for Undo/Redo operations.
    Private Delegate Sub AddDelegate(item As ListViewItem)

    ' Delegate to Remove an Item for Undo/Redo operations.
    Private Delegate Sub RemoveDelegate(item As ListViewItem)

    ' The Undo/Redo action.
    Private action As ListView_Action = Nothing

    ' The operation.
    Public Enum Operation As Short
        Undo = 0
        Redo = 1
    End Enum

    ' The method for the Undo/Redo operation.
    Public Enum Method As Short
        Add = 0
        Remove = 1
    End Enum

    ''' <summary>
    ''' Creates a Undo/Redo Action.
    ''' </summary>
    Class ListView_Action

        ''' <summary>
        ''' Names the Undo/Redo Action.
        ''' </summary>
        Property Name As String

        ''' <summary>
        ''' Points to a method to excecute.
        ''' </summary>
        Property Operation As [Delegate]

        ''' <summary>
        ''' Method of the Undo/Redo operation.
        ''' </summary>
        Property Method As Method

        ''' <summary>
        ''' Data Array for the method to excecute.
        ''' </summary>
        Property Data As ListViewItem

    End Class

    ''' <summary>
    ''' This event is raised after an Undo/Redo action is performed.
    ''' </summary>
    Public Event UndoRedo_IsPerformed As EventHandler(Of UndoneRedoneEventArgs)
    Public Class UndoneRedoneEventArgs : Inherits EventArgs
        Property Operation As Operation
        Property Method As Method
        Property Item As ListViewItem
        Property UndoStack As Stack(Of ListView_Action)
        Property RedoStack As Stack(Of ListView_Action)
    End Class

    ''' <summary>
    ''' This event is raised when Undo/Redo Stack size changed.
    ''' </summary>
    Public Event UndoRedo_StackSizeChanged As EventHandler(Of StackSizeChangedEventArgs)
    Public Class StackSizeChangedEventArgs : Inherits EventArgs
        Property UndoStack As Stack(Of ListView_Action)
        Property RedoStack As Stack(Of ListView_Action)
        Property UndoStackIsEmpty As Boolean
        Property RedoStackIsEmpty As Boolean
    End Class

    ''' <summary>
    ''' Undo the last action.
    ''' </summary>
    Public Sub Undo()

        If Me.Undostack.Count = 0 Then Exit Sub ' Nothing to Undo.

        Me.IsDoingUndo = True
        Me.action = Me.Undostack.Pop ' Get the Action from the Stack and remove it.
        Me.action.Operation.DynamicInvoke(Me.action.Data) ' Invoke the undo Action.
        Me.IsDoingUndo = False

        Raise_UndoRedo_IsPerformed(Operation.Undo, Me.action.Method, Me.action.Data)

    End Sub

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

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

        Me.IsDoingRedo = True
        Me.action = Me.Redostack.Pop() ' Get the Action from the Stack and remove it.
        Me.action.Operation.DynamicInvoke(Me.action.Data) ' Invoke the redo Action.
        Me.IsDoingRedo = False

        Raise_UndoRedo_IsPerformed(Operation.Redo, Me.action.Method, Me.action.Data)

    End Sub

    ' Reverses an Undo/Redo action
    Private Function GetReverseAction(ByVal e As UndoneRedoneEventArgs) As ListView_Action

        Me.action = New ListView_Action

        Me.action.Name = e.Item.Text
        Me.action.Data = e.Item

        Me.action.Operation = If(e.Method = Method.Add, _
                        New RemoveDelegate(AddressOf Me.RemoveItem), _
                        New AddDelegate(AddressOf Me.AddItem))

        Me.action.Method = If(e.Method = Method.Add, _
                     Method.Remove, _
                     Method.Add)

        Return Me.action

    End Function

    ' Raises the "UndoRedo_IsPerformed" Event
    Private Sub Raise_UndoRedo_IsPerformed(ByVal Operation As Operation, _
                                           ByVal Method As Method, _
                                           ByVal Item As ListViewItem)

        RaiseEvent UndoRedo_IsPerformed(Me, New UndoneRedoneEventArgs _
                   With {.Item = Item, _
                         .Method = Method, _
                         .Operation = Operation, _
                         .UndoStack = Me.Undostack, _
                         .RedoStack = Me.Redostack})

        Raise_UndoRedo_StackSizeChanged()

    End Sub

    ' Raises the "UndoRedo_StackSizeChanged" Event
    Private Sub Raise_UndoRedo_StackSizeChanged()

        RaiseEvent UndoRedo_StackSizeChanged(Me, New StackSizeChangedEventArgs _
                   With {.UndoStack = Me.Undostack, _
                         .RedoStack = Me.Redostack, _
                         .UndoStackIsEmpty = Me.Undostack.Count = 0, _
                         .RedoStackIsEmpty = Me.Redostack.Count = 0})

    End Sub

    ' This handles when an Undo or Redo operation is performed.
    Private Sub UndoneRedone(ByVal sender As Object, ByVal e As UndoneRedoneEventArgs) _
    Handles Me.UndoRedo_IsPerformed

        Select Case e.Operation

            Case Operation.Undo
                ' Create a Redo Action for the undone action.
                Me.Redostack.Push(GetReverseAction(e))

            Case Operation.Redo
                ' Create a Undo Action for the redone action.
                Me.Undostack.Push(GetReverseAction(e))

        End Select

    End Sub

    ' Monitors when an Item is added to create an Undo Operation.
    Private Sub OnItemAdded(sender As Object, e As ItemAddedEventArgs) _
    Handles Me.ItemAdded

        If Me.Enable_UndoRedo_Manager _
            AndAlso (Not Me.IsDoingUndo And Not Me.IsDoingRedo) Then

            Me.Redostack.Clear()

            ' // Crate an Undo Action
            Me.action = New ListView_Action
            Me.action.Name = e.Item.Text
            Me.action.Operation = New RemoveDelegate(AddressOf Me.RemoveItem)
            Me.action.Data = e.Item
            Me.action.Method = Method.Remove

            Me.Undostack.Push(action)

            Raise_UndoRedo_StackSizeChanged()

        End If

    End Sub

    ' Monitors when an Item is removed to create an Undo Operation.
    Private Sub OnItemRemoved(sender As Object, e As ItemRemovedEventArgs) _
    Handles Me.ItemRemoved

        If Me.Enable_UndoRedo_Manager _
            AndAlso (Not Me.IsDoingUndo And Not Me.IsDoingRedo) Then

            Me.Redostack.Clear()

            ' // Crate an Undo Action
            Me.action = New ListView_Action
            Me.action.Name = e.Item.Text
            Me.action.Operation = New AddDelegate(AddressOf Me.AddItem)
            Me.action.Data = e.Item
            Me.action.Method = Method.Add

            Me.Undostack.Push(action)

            Raise_UndoRedo_StackSizeChanged()

        End If

    End Sub

#End Region

End Class
4

1 回答 1

1

首先,由于您是从派生类开始的,因此请将这些事件添加到其中:

' (your Item events still lack a Sender and Item to save a lot of work)
Public Event CheckChanged(ByVal sender As Object, _
           ByVal lice As ListItemCheckChangedArgs)
Public Shadows Event BeforeLabelEdit(ByVal sender As Object, _
           ByVal oldText As String)

BeforeLabelEdit 是必需的,因为基本 LVNothing在开始时存储了旧的标签文本,您需要将其更改为 .Text 属性以实际检测更改。接下来,监视 Text 更改将要求您执行与 ItemAdded 不同的操作(无论它是通用的还是内部的)。

触发时Before...,存储一个副本...Watcher 会将 _BeforeText 与 _AfterText 进行比较(通过观看正常的 AfterLabelEdit 事件获得),如果它们不同,则将 _BEforeText 推入堆栈。

CheckChanged有点骗人。您可以直接单击 LVItem Check 而不会触发任何类型的 Enter/GotFocus 等事件。这意味着观察者无法获得_BeforeChecked价值。原生 LV ItemChecked 确实触发了,我只是从中重组 args 以将我需要的内容传递给 Watcher(通过上述事件)。虱子参数:

Public Class ListItemCheckChangedArgs
    Public Index As Integer                ' index of item checked
    Public OldValue As CheckState          
    Public NewValue As CheckState

    Public Sub New(ByVal ndx As Integer, ByVal ov As CheckState, _
            ByVal nv As CheckState)
        Index = ndx
        OldValue = ov
        NewValue = nv

    End Sub
End Class

观察者然后用 OldValue 作为数据创建一个 UnDoAction 对象。即使没有观察者,这也将有助于捕获您需要的内容。

重要的部分是 Undo 类(内部 UM 会有所不同):

 Public Class ListViewEUndo
    '... WHO this action applies to
    Friend Ctl As ListViewEX

    ' a listview item, or string (Text) or bool (Check)
    Friend UnDoData As Object

    Friend LVActionType As LVEActionType = LVEActionType.None

    ' original index of items removed / index of item checked
    Friend Index As Integer = -1

  Public Sub New(ByVal _ctl As ListViewEX, ByVal _LvAType As LVEActionType, _
            ByVal _data As Object)
        Ctl = _ctl
        LVActionType = _LvAType
        UnDoData = _data

  End Sub
    ...

LVEActionType只是 AddItem、RemoveItem 等(此操作类型是什么)

您可以设计一个基类,然后为 TextUndo、CheckUndo 和 ItemUndo 继承它。这是一个折腾是否更好或由此产生的简短 SELECT CASE 语句。最后,没有 DELEGATES,因为 UndoManager 类/助手将自己应用更改,而不是将工作导出到 from 或控件(这也有助于避免由 Undo/Redo 操作引起的 Pushing 操作!)。

我不确定它是不是full code example,但可能有助于解决您的问题,具体取决于它是否是内部的(内部几乎不需要任何事件 - 主要用于触发 Watcher 操作 - 在值之前捕获,在值之后捕获/比较,响应添加/删除项目等)。

于 2013-11-07T16:15:02.277 回答