Rozszerz tę klasę na cofnij / ponów w widoku listy

Używam kodu strony do zarządzania operacjami cofania / ponawiania w projekcie WindowsForm.

Muszę rozszerzyć klasę, aby zarządzać operacjami cofania / ponawiania w widoku listy, co oznacza:

· Cofnij / Ponów Dodaj / Usuń elementy i podelementy

· Cofnij / Ponów zaznacz / odznacz wiersze

· Cofnij / Ponów kilka innych ważnych rzeczy, które być może przegapiłem

Nie wiem, jak zacząć to robić, kod jest dla mnie zbyt skomplikowany, każda pomoc / wskazówki / przykłady na ten temat byłyby dla mnie bardzo satysfakcjonujące, ale za 3 miesiące nie byłem w stanie przeprowadzić tej zmiany, ja myślę, że będę potrzebował dobrych wyjaśnień lub pełnych przykładów, oto kod:

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

http://pastebin.com/Gmh5HS4x

(Nie opublikowałem tego kodu tutaj, ponieważ przekracza on limit 30 000 znaków StackOverflow)

AKTUALIZACJA:

To jest kilka przydatnych informacji od autora wyjaśniających rzeczy, które muszę zrobić, aby dodać obsługę Listview, ale tak naprawdę nie mogę sam:

Dodanie funkcjonalności do listview nie powinno być tak trudne, a także świetny sposób na to, jak to działa. Musisz utworzyć nowy monitor, który przechwyci zdarzenia zmiany listy i zachowa bieżącą wartość przed jej zmianą. Polecenie jest tworzone, jeśli wykryjesz, że dokonano zmiany przy użyciu wszystkich informacji potrzebnych do cofnięcia lub ponownego wykonania akcji. To jest to. Dopóki monitor i polecenie dziedziczą z klas podstawowych, zostaną wykryte i użyte automatycznie.

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

AKTUALIZACJA:

Właściciel klasy zaktualizował kod dodając jedną z rzeczy, których potrzebowałem, element etykiety cofał / ponawiał operacje, o które prosiłem.

· Cofnij / Ponów zmiany tekstu wewnątrz Listview (tryb normalny lub tryb szczegółów)

Niestety ta aktualizacja nie jest wystarczająca, aby móc dodać inne operacje cofania / ponawiania, których potrzebuję. Przeczytaj uwagi dotyczące wyjaśniania komentarzy @Plutonix

Oto część zaktualizowanej Klasy dla kogoś, kto może wziąć pomysły i pomóc je rozszerzyć:

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

AKTUALIZACJA 2:

Oto, co autor powiedział o tym, jak dodać funkcje, których potrzebuję do operacji cofania / ponawiania listview:

Myślę, że nie musisz przepisywać pełnej klasy. Najtrudniejszą częścią tego jest znalezienie sposobu na wykrycie, kiedy element może zostać usunięty i kiedy jest rzeczywiście usunięty. W ListViewMonitor będziesz musiał dodać niezbędne procedury obsługi zdarzeń (w źródle, gdzie znajdziesz AddHandler dla BeforeLabelEdit i AfterLabelEdit). Dla klasy Command musisz mieć aktualny ListViewItem i pozycję elementu w ListView, zanim zostanie usunięty. Możesz po prostu utworzyć swoją strukturę za pomocą tej informacji, na przykład ListViewItemRemoveUndoRedoData. Po cofnięciu usunięcia wystarczy dodać zapisaną ListViewItem do ListView w miejscu, w którym została zapisana. Sugerowałbym dodanie dodatkowej liczby do struktury ListViewItemRemoveUndoRedoData, która zawiera liczbę elementów w widoku listy. Ponadto myślę, że jedynym potrzebnym wydarzeniem jest SelectedIndexChanged. Gdy zdarzenie to ma miejsce, występują 2 sytuacje.

1- Liczba przedmiotów jest taka sama jak liczba zapisana wcześniej (ustaw na -1 lub coś na tworzenie monitora): Przechowujesz przedmiot, pozycję i całkowitą liczbę przedmiotów.

2- Liczba przedmiotów jest mniejsza niż liczba zapisana wcześniej: Element jest usuwany i konfigurujesz jego UndoRedoCommand, aby można go było cofnąć.

Istnieje oczywiście trzecia opcja, która oznaczałaby dodanie elementu

Potrzebuje kreatywności, aby znaleźć odpowiednie zdarzenia i co należy zapisać, aby wykonać cofnięcie / ponowienie. Może to nawet oznaczać, że musisz znaleźć alternatywny podgląd listy z lepszymi wydarzeniami i wsparciem (które znajdziesz tutaj na codeproject)

AKTUALIZACJA 3:

Próbując podążać za rozwiązaniem @ThorstenC, napotykam problemy z RedoLastAction, ponawiam je, nawet jeśli najpierw niczego nie cofnę.

Mogę także przerobić nieskończone czasy, a to tylko zmienia ostatnią akcję, to znaczy Jeśli cofnę 3 różne przedmioty LV, mogę tylko powtórzyć ostatni dodany przedmiot.

· Klasa 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

· Główny kod formularza:

' 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

questionAnswers(3)

yourAnswerToTheQuestion