对所有相同的控件处理UserControl事件

时间:2013-11-07 13:01:14

标签: .net vb.net winforms events user-controls

问题是这个,我有一个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

1 个答案:

答案 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