Trying to perform a dynamic Action on multiple controls at once, it is giving a cross-thread exception

StackOverflow https://stackoverflow.com/questions/20533563

Question

I've did a class named "ControlIterator" that has methods to iterate multiple controls to enable or disable them, check or uncheck them, and now I would like to add a method to perform an Action on the specified controls.

Keep in mind that this method need to be for generic use, couldn't be hardcoded!

The problem I have is at the PerformActionOnControls method, exactly at this line:

If [Control].InvokeRequired Then
    [Control].Invoke(ControlAction.DynamicInvoke([Control]))
End IF

...because I don't manage very well the Delegates/Actions so when a Control requires invocation the method throws a typically cross-threading exception.

I need help to fix the problem.

Here are the "Perform Action" methods:

( The reason why I'm using an Object instead of a Control type is just for flexibility, like I've said it's for generic usage so if I pass a checkbox then the control type hasn't a "checked" property and I need to convert the type, I preffer do not do it. )

''' <summary>
''' Perform an Action on a specific Control.
''' </summary>
''' <param name="Control">Indicates the Control to perform the Action.</param>
''' <param name="ControlAction">Indicates the Action to perform on the control.</param>
Public Shared Sub PerformAction(ByVal Control As Object, ByVal ControlAction As [Delegate])
    PerformActionOnControls({Control}, ControlAction)
End Sub

''' <summary>
''' Perform an Action on multiple Controls at once.
''' </summary>
''' <param name="Controls">Indicates the Controls to perform the Action.</param>
''' <param name="ControlAction">Indicates the Action to perform on the controls.</param>
Public Shared Sub PerformAction(ByVal Controls As IEnumerable(Of Object), ByVal ControlAction As [Delegate])
    PerformActionOnControls(Controls, ControlAction)
End Sub

''' <summary>
''' Perform an Action on all the Controls of the specified Type on the specified Control Container.
''' </summary>
''' <param name="ControlContainer">Indicates the control container where to find the controls.</param>
''' <param name="ControlAction">Indicates the Action to perform on the controls.</param>
Public Shared Sub PerformAction(Of T)(ByVal ControlContainer As Control, ByVal ControlAction As [Delegate])
    PerformActionOnControls(ControlContainer.Controls.OfType(Of T), ControlAction)
End Sub

''' <summary>
''' Perform an Action on all the Controls of the specified Type on the specified Control Collection.
''' </summary>
''' <param name="ControlCollection">Indicates the control collection where to find the controls.</param>
''' <param name="ControlAction">Indicates the Action to perform on the controls.</param>
Public Shared Sub PerformAction(Of T)(ByVal ControlCollection As Control.ControlCollection, ByVal ControlAction As [Delegate])
    PerformActionOnControls(ControlCollection.OfType(Of T), ControlAction)
End Sub

''' <summary>
''' Perform an Action on Controls.
''' </summary>
Public Shared Sub PerformActionOnControls(ByVal Controls As IEnumerable(Of Object),
                                          ByVal ControlAction As [Delegate])

    For Each [Control] As Object In Controls

        If [Control].InvokeRequired Then
            MsgBox("invoke required: " & [Control].name)
            [Control].Invoke(ControlAction.DynamicInvoke([Control]))
            ' This else don't worked too:
            ' [Control].Invoke(ControlAction.Method.Invoke(ControlAction, {[Control]}))
        Else
            MsgBox("no invoke: " & [Control].name)
            ControlAction.DynamicInvoke([Control])
        End If

    Next [Control]

End Sub

I'm calling the method this way from a secondary thread:

Sub ExampleThread()
    ControlIterator.PerformAction(Of CheckBox)(Me.GroupBox1, Sub(chk As CheckBox) chk.Checked = False)
    ControlIterator.PerformAction(RichTextBox1, Sub(rb As RichTextBox) rb.Text = "Hello World!")
End Sub

My first attempt for write the method above I was trying to follow the steps of this wonderfull method but I couldn't reproduce the usage of the Action because I need to use Objects (no control type) and also notice that my method expects an Ienumerable(of Object) and not a single control/object, so I've decided to use a Delegate at the method above instead of an Action, but really no matter for me if using an Action like in this example below could be the solution... well just I share this 'cause maybe could help to give ideas to resolve my problem above:

#Region " Invoke Control "

    ' Examples :
    '
    ' InvokeControl(TextBox1, Sub(x) x.AppendText("Hello"))
    ' InvokeControl(CheckBox1, Sub(x) x.Checked = True)

    ''' <summary>
    ''' Invokes an Action on the specified control.
    ''' This method avoids cross-threading exceptions.
    ''' </summary>
    Public Sub InvokeControl(Of T As Control)(ByVal Control As T, ByVal Action As Action(Of T))

        If Control.InvokeRequired Then
            Control.Invoke(New Action(Of T, Action(Of T))(AddressOf InvokeControl), New Object() {Control, Action})
        Else
            Action(Control)
        End If

    End Sub

#End Region
Était-ce utile?

La solution

I were very close, This is the solution:

If [Control].InvokeRequired Then
    [Control].Invoke(ControlAction, [Control])
Else
    ' ControlAction.DynamicInvoke([Control])
    ControlAction.Method.Invoke(ControlAction, {[Control]})
End If
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top