Click here to Skip to main content
15,885,953 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
My Situation is that I want to have a Delegate to a method from a Control.

For that I use the code as follows :
VB
Private RepeatDelayCounter As Integer = 0
Private RepeatControl As Control = Nothing
Private Delegate Sub delegate_OnClick(instance As Control, e As System.EventArgs)
Private RepeatDelegate As delegate_OnClick = Nothing


Private Sub Button_Down(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    RepeatControl = sender
    RepeatDelegate = Nothing

    Dim myType As Type = RepeatControl.GetType
    Dim myInfo As System.Reflection.MethodInfo = myType.GetMethod("OnClick", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
    If myInfo IsNot Nothing Then RepeatDelegate = [Delegate].CreateDelegate(GetType(delegate_OnClick), myInfo, False)


    RepeatDelayCounter = 0
End Sub

You should know that this is only a part (but the relevant one) of a complex class.

This code works properly (I get a Delegate) if the Control which starts the Button_Down-method uses a overrided Version of the OnClick-method.

If I don't use a customized Control but a "Standard"-Control (for example a Button) I get the right MethodInfo, because this Control has a OnClick-Method inside, but I can't get a Delegate.
The Error-Message I get points to the Binding-Flags, but these are completely the same as those from my custom Control. So I think it comes from the Protected-attribute of the OnClick-method from the "Standard"-Control.

So ... what I need is a useful advice how to solve this - because I want to understand it ... ;-)

What I have tried:

see above ...
Posted
Updated 23-Mar-17 5:29am
Comments
Tomas Takac 23-Mar-17 10:11am    
You are creating a delegate to a instance method but not providing the instace. Found here: http://stackoverflow.com/questions/11120401/creating-delegate-from-methodinfo
Richard Deeming 23-Mar-17 10:48am    
It's an open instance delegate[^] - the instance is passed as the first parameter.

1 solution

Based on some crude testing, I suspect the problem is actually the other way round: you can create the delegate when the method hasn't been overridden, and you can't create the delegate when the method has been overridden.
VB.NET
Class Foo
    Protected Overridable Sub Click()
        Console.WriteLine("Foo.Click")
    End Sub
End Class

Class Bar : Inherits Foo
End Class

Class Baz : Inherits Foo
    Protected Overrides Sub Click()
        Console.WriteLine("Baz.Click")
        MyBase.Click()
    End Sub
End Class

Delegate Sub MyDelegate(ByVal instance As Foo)

Function CreateDelegate(ByVal instance As Foo) As MyDelegate
    Dim myType As Type = instance.GetType()
    Dim myInfo As MethodInfo = myType.GetMethod("Click", BindingFlags.NonPublic Or BindingFlags.Instance)
    Return DirectCast([Delegate].CreateDelegate(GetType(MyDelegate), myInfo, True), MyDelegate)
End Function

Sub Main
    Dim f As New Foo()
    CreateDelegate(f).Invoke(f) 
    ' OK: Prints "Foo.Click"
    
    f = New Bar()
    CreateDelegate(f).Invoke(f) 
    ' OK: Prints "Foo.Click"
    
    f = New Baz()
    CreateDelegate(f).Invoke(f) 
    ' ArgumentException: 
    ' Cannot bind to the target method because its signature or security 
    ' transparency is not compatible with that of the delegate type.
End Sub


The reason is fairly simple. When the method has been overridden, the GetMethod returns a MethodInfo representing the overridden version of the method. That overridden version can only be called on an instance of the class where the override is declared. But you are trying to bind it to a delegate that can be called on any instance of the base class.

So, for example, you could create a delegate to call Baz.Click, but pass in an instance of Bar instead.

The simple solution is to retrieve the MethodInfo for the base method:
VB.NET
Function CreateDelegate(ByVal instance As Foo) As MyDelegate
    Dim myType As Type = GetType(Foo)
    Dim myInfo As MethodInfo = myType.GetMethod("Click", BindingFlags.NonPublic Or BindingFlags.Instance)
    Return DirectCast([Delegate].CreateDelegate(GetType(MyDelegate), myInfo, True), MyDelegate)
End Function

Invoking the delegate will still call the overridden version:
VB.NET
f = New Baz()
CreateDelegate(f).Invoke(f) 
' OK: Prints "Baz.Click", then "Foo.Click"


So your code would become:
VB.NET
Private Sub Button_Down(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    RepeatControl = sender
    RepeatDelegate = Nothing
    
    Dim myType As Type = GetType(Control)
    Dim myInfo As System.Reflection.MethodInfo = myType.GetMethod("OnClick", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
    If myInfo IsNot Nothing Then RepeatDelegate = [Delegate].CreateDelegate(GetType(delegate_OnClick), myInfo, False)
    
    RepeatDelayCounter = 0
End Sub


Alternatively, you could create a closed instance delegate, and you wouldn't need the RepeatControl member at all:
VB.NET
Private RepeatDelayCounter As Integer = 0
Private Delegate Sub delegate_OnClick(ByVal e As System.EventArgs)
Private RepeatDelegate As delegate_OnClick = Nothing

Private Sub Button_Down(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
    RepeatDelegate = Nothing
    
    Dim myType As Type = GetType(Control)
    Dim myInfo As System.Reflection.MethodInfo = myType.GetMethod("OnClick", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
    If myInfo IsNot Nothing Then RepeatDelegate = [Delegate].CreateDelegate(GetType(delegate_OnClick), sender, myInfo, False)

    RepeatDelayCounter = 0
End Sub
 
Share this answer
 
Comments
Ralf Meier 24-Mar-17 6:10am    
Hi Richard,
thanks again for your (very great) work and the time you spend to my Problem.
Also your alternativ Suggestion is very good - thanks for this too.
Ralf Meier 24-Mar-17 17:52pm    
Hi Richard,
according to your first sentence of your solution :
What I have written was not correct - the difference for my provided code was the basetype. The controls I've developed derive from Control. So the Code worked. The Button I've used later derived from Buttonbase. I think, that was the important difference - but this is solved with your change (Dim myType As Type = GetType(Control)).
So again - thanks a lot ...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900