0

问题是这样的,我有一个 ListView 有自己的事件和两个堆栈

并且需要启用这样的属性来处理这些事件:

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
4

1 回答 1

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

您仍然会遇到其他问题,因为您拥有 ONE 表单级 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)

将 sender 作为事件参数之一,您的代码可以:a) 设置ListUndoAction.Ctl = sender以便通用 UM 知道此操作适用于哪个控件或 b) 评估是否sender.UndoManagerEnabled = True

于 2013-11-07T13:55:55.763 回答