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