问题是这个,我有一个ListView,有自己的事件和两个Stacks
需要启用这样的属性来处理这些事件:
Public property Enable_Something as boolean
好吧,我将我的用户控件添加到UI中,然后启用该属性,但如果我再次在UI中添加相同的控件,则会为两个控件处理事件!并且两个控件都推动/加载堆栈......
因此该属性存在冲突,并且事件和堆栈也存在冲突,因为第二个控件会在第一个控件堆栈中添加一些内容。
我想为每个控件分离操作/事件/堆栈。
这是form1类(阅读评论):
Public Class Form1
Friend WithEvents Undom As ListView_Elektro.UndoRedoManager
Private lvi As ListViewItem
Private Sub Test(sender As Object, e As EventArgs) Handles MyBase.Shown
' Enable undo manager in listview1 but not in listview2
' But no way, the undomanager is handled by both controls...
ListView_Elektro1.Enable_UndoRedo_Manager = True
ListView_Elektro2.Enable_UndoRedo_Manager = False
Undom = New ListView_Elektro.UndoRedoManager(ListView_Elektro1)
lvi = New ListViewItem("hello1")
ListView_Elektro1.AddItem(lvi)
lvi = New ListViewItem("hello2")
ListView_Elektro2.AddItem(lvi)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
Undom.UndoLastAction()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) _
Handles Button2.Click
Undom.RedoLastAction()
End Sub
Private Sub OnItemAdded() Handles ListView_Elektro1.ItemAdded
MsgBox("Item added")
' ( No way, both ListView_Elektro1.ItemAdded and ListView_Elektro2.ItemAdded are handled here... )
End Sub
End Class
这是用户控件的重要部分:
Public Class ListView_Elektro : Inherits ListView
''' <summary>
''' Enable or disble the Undo/Redo monitoring.
''' </summary>
Public Property Enable_UndoRedo_Manager As Boolean = False
Public Shared Event ItemAdded As EventHandler(Of ItemAddedEventArgs)
Public Class ItemAddedEventArgs : Inherits EventArgs
Public Property Item As ListViewItem
End Class
Public Function AddItem(ByVal Item As ListViewItem) As ListViewItem
MyBase.Items.Add(Item)
RaiseEvent ItemAdded(Me, New ItemAddedEventArgs With {.Item = Item})
Return Item
End Function
''' <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 UndoRedoManager.Method
''' <summary>
''' Data Array for the method to excecute.
''' </summary>
Property Data As ListViewItem
End Class
Public Class UndoRedoManager
Private WithEvents LV As ListView_Elektro
' Delegate to Add an Item for Undo/Redo operations.
Delegate Sub AddDelegate(item As ListViewItem)
' Delegate to Remove an Item for Undo/Redo operations.
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>
''' This event is raised after an Undo/Redo action is performed.
''' </summary>
Event UndoRedo_IsPerformed As EventHandler(Of UndoneRedoneEventArgs)
Class UndoneRedoneEventArgs : Inherits EventArgs
Public Property Operation As Operation
Public Property Method As Method
Public Property Item As ListViewItem
Public Property UndoStack As Stack(Of ListView_Action)
Public Property RedoStack As Stack(Of ListView_Action)
End Class
''' <summary>
''' This event is raised when Undo/Redo Stack size changed.
''' </summary>
Event UndoRedo_StackSizeChanged As EventHandler(Of StackSizeChangedEventArgs)
Class StackSizeChangedEventArgs : Inherits EventArgs
Public Property UndoStack As Stack(Of ListView_Action)
Public Property RedoStack As Stack(Of ListView_Action)
Public Property UndoStackIsEmpty As Boolean
Public Property RedoStackIsEmpty As Boolean
End Class
Public Sub New(ByVal ListView As ListView_Elektro)
LV = ListView
' MsgBox(LV.ToString)
End Sub
''' <summary>
''' Undo the last action.
''' </summary>
Sub UndoLastAction()
If LV.Undostack.Count = 0 Then Exit Sub ' Nothing to Undo.
LV.IsDoingUndo = True
action = LV.Undostack.Pop ' Get the Action from the Stack and remove it.
action.Operation.DynamicInvoke(action.Data) ' Invoke the undo Action.
LV.IsDoingUndo = False
Raise_UndoRedo_IsPerformed(Operation.Undo, action.Method, action.Data)
End Sub
''' <summary>
''' Redo the last action.
''' </summary>
Sub RedoLastAction()
If LV.Redostack.Count = 0 Then Exit Sub ' Nothing to Redo.
LV.IsDoingRedo = True
action = LV.Redostack.Pop() ' Get the Action from the Stack and remove it.
action.Operation.DynamicInvoke(action.Data) ' Invoke the redo Action.
LV.IsDoingRedo = False
Raise_UndoRedo_IsPerformed(Operation.Redo, action.Method, action.Data)
End Sub
' 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 = LV.Undostack, _
.RedoStack = LV.Redostack})
Raise_UndoRedo_StackSizeChanged()
End Sub
' Raises the "UndoRedo_IsPerformed" Event
Private Sub Raise_UndoRedo_StackSizeChanged()
RaiseEvent UndoRedo_StackSizeChanged(Me, New StackSizeChangedEventArgs _
With {.UndoStack = LV.Undostack, _
.RedoStack = LV.Redostack, _
.UndoStackIsEmpty = LV.Undostack.Count = 0, _
.RedoStackIsEmpty = LV.Redostack.Count = 0})
End Sub
' Reverses an Undo/Redo action
Private Function GetReverseAction(ByVal e As UndoneRedoneEventArgs) As ListView_Action
action = New ListView_Action
action.Name = e.Item.Text
action.Data = e.Item
action.Operation = If(e.Method = Method.Add, _
New RemoveDelegate(AddressOf LV.RemoveItem), _
New AddDelegate(AddressOf LV.AddItem))
action.Method = If(e.Method = Method.Add, _
Method.Remove, _
Method.Add)
Return action
End Function
' 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.
LV.Redostack.Push(GetReverseAction(e))
Case Operation.Redo
' Create a Undo Action for the redone action.
LV.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 LV.ItemAdded
If LV.Enable_UndoRedo_Manager _
AndAlso (Not LV.IsDoingUndo And Not LV.IsDoingRedo) Then
LV.Redostack.Clear()
' // Crate an Undo Action
action = New ListView_Action
action.Name = e.Item.Text
action.Operation = New RemoveDelegate(AddressOf LV.RemoveItem)
action.Data = e.Item
action.Method = Method.Remove
LV.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 LV.ItemRemoved
If LV.Enable_UndoRedo_Manager _
AndAlso (Not LV.IsDoingUndo And Not LV.IsDoingRedo) Then
LV.Redostack.Clear()
' // Crate an Undo Action
action = New ListView_Action
action.Name = e.Item.Text
action.Operation = New AddDelegate(AddressOf LV.AddItem)
action.Data = e.Item
action.Method = Method.Add
LV.Undostack.Push(action)
Raise_UndoRedo_StackSizeChanged()
End If
End Sub
End Class
End Class
答案 0 :(得分:1)
你开始采用完全不同的方式,所以原谅我们/我没有得到更新的任务声明。这可能会消除当前的问题:
Public Property Enable_UndoRedo_Manager As Boolean = False
...
Public Function AddItem(ByVal Item As ListViewItem) As BOOLEAN
MyBase.Items.Add(Item)
' TEST to see if this LV instance should signal a change:
If Enable_UndoRedo_Manager THen
RaiseEvent ItemAdded(Me, New ItemAddedEventArgs With {.Item = Item})
Return True
end if
Return False
End Function
你还有其他问题,因为你有一个表单级UM,所以你真的不希望它创建单独的堆栈。
修改
因为对于LV改变其Enable_UM状态是非常不利的,我会使它成为一个无法改变的ctor参数(只要UM不是LV的完全内部辅助类)。
编辑编辑
内部UM的概念:
Private _undoStack As Stack(Of ListViewEUndo)
Public Function AddItem(ByVal Item As ListViewItem) As Boolean
' need to not stack Undos
_IgnoreChange = True
' NOT NEEDED for an internal UM
'RaiseEvent ItemAdded(Me, Item)
MyBase.Items.Add(Item)
AddUnDoAction(Item) ' create an undo action, push it
_IgnoreChange = False
End Function
如果需要,UM代码可以位于您实例化的UM类中,并添加操作:
MyBase.Items.Add(Item)
myUndoMgr.AddUnDoAction(Item) ' _undoStack would be inside this class'
' in this case
此LV不需要事件告诉此UM添加内容。您可能仍然使用观察者和事件,但这似乎是您正在避免的事情。架构问题是您无法在没有UM的情况下使用LVE,而UM将无法在LVE之外使用
我应该补充说,1 UM的仅额外障碍要做多个控制,你必须在UndoAction类中存储对控件的引用到UnDo / ReDo。这当然可以使用各种事件中的sender
轻松捕获。
编辑* 3
如果要使用事件,首先要改变的是:
Public Shared Event ItemAdded As EventHandler(Of ItemAddedEventArgs)
为:
' a good UnDo Mgr will need to know WHO changed
' and might as well tell WHAT changed to make things easy
Public Event ItemAdded(ByVal sender As Object, ByVal item As ListViewItem)
Public Event ItemRemoved(ByVal sender As Object, ByVal item As ListViewItem)
将发件人作为事件参数之一,您可以执行以下操作:a)设置ListUndoAction.Ctl = sender
,以便通用UM知道此操作适用于哪个控件,或者b)评估sender.UndoManagerEnabled = True