WindowsForm プロジェクトで元に戻す/やり直し操作を管理するために、サード パーティのコードを使用しています。
リストビューで元に戻す/やり直し操作を管理するには、クラスを拡張する必要があります。これは、次のことを意味します。
・元に戻す/やり直し アイテムとサブアイテムの追加/削除
· 行のチェックを元に戻す/チェックをやり直す/チェックを外す
· 見逃した可能性のある他の重要なことを元に戻す/やり直す
これをどのように開始すればよいかわかりません。コードは私には複雑すぎます。これに関するヘルプ/ヒント/例は非常に喜ばしいものですが、3 か月間、この変更を実行できませんでした。適切な説明または完全な例が必要になると思います。コードは次のとおりです。
********************************************************
Undo/Redo framework (c) Copyright 2009 Etienne Nijboer
********************************************************
(StackOverflow の 30.000 文字制限を超えるため、ここにコードを投稿しませんでした)
アップデート:
これは、リストビューのサポートを追加するために必要なことを説明する著者からの有用な情報ですが、実際には自分ではできません。
ちなみに、リストビューに機能を追加することはそれほど難しいことではなく、それがどのように機能するかを理解するための優れた方法でもあります。リストビューの変更イベントをキャプチャし、変更前の現在の値を保存する新しいモニターを作成する必要があります。アクションを元に戻すまたはやり直すために必要なすべての情報を使用して変更が行われたことを検出すると、コマンドが作成されます。それでおしまい。モニターとコマンドが基本クラスから継承されている限り、自動的に検出されて使用されます。
http://www.codeproject.com/Articles/43436/Undo-Redo-Framework
アップデート:
クラスの所有者がコードを更新して、私が必要としていたものの 1 つである、私が要求したラベル項目の取り消し/やり直し操作を追加しました。
· リストビュー内のテキストの変更を元に戻す/やり直す (通常モードまたは詳細モード)
残念ながら、この更新は、必要な他の元に戻す/やり直し操作を追加するには不十分です。@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 を見つけるソース内)。Command クラスの場合、実際の ListViewItem と、削除される前の ListView 内の項目の位置が必要です。この情報を使用して、ListViewItemRemoveUndoRedoData のような構造を簡単に作成できます。削除を元に戻すときは、保存した ListViewItem を、保存した位置の ListView に追加するだけです。リストビュー内の項目数を保持する ListViewItemRemoveUndoRedoData 構造に追加の Count を追加することをお勧めします。さらに、必要なイベントは SelectedIndexChanged だけだと思います。このイベントが発生すると、2 つの状況が発生します。
1-アイテムの数は、以前に保存されたカウントと同じです(モニターの作成時に-1または何かに設定します):アイテム、位置、および合計アイテム数を保存します。
2- アイテムの数が、以前に保存した数よりも少ない: アイテムが削除され、その UndoRedoCommand をセットアップして元に戻せるようにします。
- もちろん、アイテムが追加されることを意味する 3 番目のオプションがあります。
適切なイベントと、元に戻す/やり直しを実行するために保存する必要があるものを見つけるには、ある程度の創造性が必要です。より良いイベントとサポートを備えた代替のリストビューを見つける必要があることを意味するかもしれません (これは 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