Поделиться через


EventInfo.AddEventHandler Method

Microsoft Silverlight will reach end of support after October 2021. Learn more.

Adds an event handler to an event source.

Namespace:  System.Reflection
Assembly:  mscorlib (in mscorlib.dll)

Syntax

'Declaration
<DebuggerHiddenAttribute> _
<DebuggerStepThroughAttribute> _
Public Overridable Sub AddEventHandler ( _
    target As Object, _
    handler As Delegate _
)
[DebuggerHiddenAttribute]
[DebuggerStepThroughAttribute]
public virtual void AddEventHandler(
    Object target,
    Delegate handler
)

Parameters

  • handler
    Type: System.Delegate
    Encapsulation of a method or methods to be invoked when the event is raised by the target.

Exceptions

Exception Condition
InvalidOperationException

The event does not have a public add accessor.

ArgumentException

The handler that was passed in cannot be used.

TargetException

The target parameter is nulla null reference (Nothing in Visual Basic) and the event is not static.

-or-

The EventInfo is not declared on the target.

MemberAccessException

This member is invoked late-bound through mechanisms such as Type.InvokeMember.

Remarks

Use the AddEventHandler method to hook up events in late-bound scenarios.

This method attempts to hook up an event handler for the current event on the event source specified by target, by calling the add accessor.

NoteNote:

In reflection, the add accessor can be obtained by calling the GetAddMethod method.

Version Notes

Silverlight for Windows Phone Silverlight for Windows Phone

 AddEventHandler throws ArgumentNullException instead of TargetException when target is nulla null reference (Nothing in Visual Basic) or the EventInfo is not declared on the target.

AddEventHandler throws ArgumentNullException instead of InvalidOperationException when the event does not have a public add accessor.

Examples

The example in this section shows how to do the following:

  • Determine the signature of an unknown event by using reflection, and emit an event handler by using a dynamic assembly.

  • Hook up the emitted event handler by using late binding.

  • Hook up a hard-coded event handler by using late binding, assuming that the event handler has the same signature as the event.

The example creates an instance of the System.Windows.Threading.DispatcherTimer class, stored in a variable of type Object. The example uses the Type.GetEvent method to get the DispatcherTimer.Tick event, and then uses the EventHandlerType property to get the delegate type for the event.

The example gets a MethodInfo for the Invoke method of the delegate type, and uses the MethodInfo to discover the signature of the delegate. The example then creates a dynamic assembly that contains a single type named Handler. Handler has a field to hold the TextBlock that is used for output, a constructor that takes a TextBlock and stores it in the field, and a method named DynamicHandler that handles the event.

After the dynamic type is created, the example gets a MethodInfo for the finished method and uses it to create a delegate instance. This instance is passed to the AddEventHandler method to hook up the event.

The example then uses the AddEventHandler method to hook up the hard-coded event handler, HardCodedHandler.

Finally, the example starts the timer and hooks up another event handler that lets you click the TextBlock to stop the example and unhook the events.

Imports System.Reflection
Imports System.Reflection.Emit

Public Class Example

   ' The type used for this code example is the DispatcherTimer 
   ' class. The Timer object is stored in a variable of type object,
   ' and all code that accesses the Timer does so late-bound. This
   ' is because the scenario in which you might use the AddEVentHander
   ' method is when you load the type after the program is already
   ' compiled, when it is not possible to use the Visual Basic
   ' AddHandler syntax to hook up the event. 
   '
   Private Shared timer As Object

   Private Shared outputBlock As System.Windows.Controls.TextBlock

   Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock)

      Example.outputBlock = outputBlock

      ' Get a Type object for the TextBlock.
      Dim obType As Type = outputBlock.GetType()

      ' Get a Type object representing the DispatcherTimer type.
      Dim t As Type = GetType(System.Windows.Threading.DispatcherTimer)

      ' Create an instance of the Timer type.
      timer = Activator.CreateInstance(t)

      ' Use reflection to get the Tick event.
      Dim eInfo As EventInfo = t.GetEvent("Tick")

      ' In order to create a method to handle the Tick event,
      ' it is necessary to know the signature of the delegate 
      ' used to raise the event. Reflection.Emit can then be
      ' used to construct a dynamic class with a static method
      ' that has the correct signature.
      '
      ' Get the event handler type of the Tick event. This is
      ' a delegate type, so it has an Invoke method that has
      ' the same signature as the delegate. The following code
      ' creates an array of Type objects that represent the 
      ' parameter types of the Invoke method.
      '
      Dim handlerType As Type = eInfo.EventHandlerType
      Dim invokeMethod As MethodInfo = handlerType.GetMethod("Invoke")
      Dim parms As ParameterInfo() = invokeMethod.GetParameters()
      '
      ' Note that in Visual Basic you must dimension the array one
      ' unit smaller than the source array in order to get an array
      ' of the same size. This is because Visual Basic adds an extra
      ' element to every array, for ease of use.
      '
      Dim parmTypes(parms.Length - 1) As Type
      Dim i As Integer
      For i = 0 To parms.Length - 1
         parmTypes(i) = parms(i).ParameterType
      Next i

      ' Use Reflection.Emit to create a dynamic assembly that
      ' will be run but not saved. An assembly must have at 
      ' least one module, which in this case contains a single
      ' type. The only purpose of this type is to contain the 
      ' event handler method and the TextBlock where output is
      ' displayed. 
      Dim aName As New AssemblyName()
      aName.Name = "DynamicTypes"
      Dim ab As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, _
         AssemblyBuilderAccess.Run)
      Dim mb As ModuleBuilder = ab.DefineDynamicModule(aName.Name)
      Dim tb As TypeBuilder = mb.DefineType("Handler", TypeAttributes.Public)

      ' Define a field to hold the TextBlock where output will
      ' be displayed.
      Dim output As FieldBuilder = tb.DefineField("outputBlock", obType, _
         FieldAttributes.Private) 

      ' Define a constructor that takes a TextBlock.
      Dim ctorParams() As Type = { obType }
      Dim ctor As ConstructorBuilder = _
         tb.DefineConstructor(MethodAttributes.Public, _
            CallingConventions.Standard, ctorParams)

      Dim il As ILGenerator = ctor.GetILGenerator()

      ' Base class constructor.
      Dim objCtor As ConstructorInfo = GetType(Object).GetConstructor(New Type() {})
      il.Emit(OpCodes.Ldarg_0)
      il.Emit(OpCodes.Call, objCtor)

      ' The constructor stores the TextBlock in the field. First, get 
      ' the new DynamicHandler instance, then get the parameter passed
      ' to the constructor, then store that value in the field.
      il.Emit(OpCodes.Ldarg_0)
      il.Emit(OpCodes.Ldarg_1)
      il.Emit(OpCodes.Stfld, output)
      ' The stack is now clear. Return.
      il.Emit(OpCodes.Ret)

      ' Create the method that will handle the event. The name
      ' is not important.
      '
      ' The parameter types and return type of the method are
      ' the same as those of the delegate's Invoke method, 
      ' captured earlier.
      Dim handler As MethodBuilder = tb.DefineMethod("DynamicHandler", _
         MethodAttributes.Public, invokeMethod.ReturnType, parmTypes)

      ' Generate code to handle the event. 
      il = handler.GetILGenerator()

      ' In this case, the handler concatenates a text
      ' string to the Text property of the TextBlock.
      ' To obtain the current text from the text block, and to 
      ' store the updated value, get the "get" and "set" accessors
      ' for the Text property:
      Dim textProp As PropertyInfo = obType.GetProperty("Text")
      Dim getter As MethodInfo = textProp.GetGetMethod()
      Dim setter As MethodInfo = textProp.GetSetMethod()

      ' To concatenate two strings, get the static Concat method of 
      ' the String class:
      Dim concat2 As MethodInfo = _
         GetType(String).GetMethod("Concat", _
                                   BindingFlags.Public Or BindingFlags.Static, _
                                   Type.DefaultBinder, _
                                   New Type() { GetType(String), GetType(String) }, _
                                   Nothing)

      ' At the very end of the method, the TextBlock instance needs 
      ' to be on the execution stack, to call the "set" accessor for
      ' the Text property. The TextBlock is stored in the private 
      ' field. Use the Handler instance to retrieve it.
      il.Emit(OpCodes.Ldarg_0)
      il.Emit(OpCodes.Ldfld, output)

      ' The existing value of the TextBlock.Text property must be on
      ' the stack, too, so load the TextBlock instance again. Then 
      ' call the "get" accessor for the Text property. The second
      ' TextBlock reference is popped off the stack, and the string 
      ' value is pushed onto the stack.
      il.Emit(OpCodes.Ldarg_0)
      il.Emit(OpCodes.Ldfld, output)
      il.Emit(OpCodes.Callvirt, getter)

      ' Load the output string, then call String.Concat to 
      ' Concatenate the two strings.
      il.Emit(OpCodes.Ldstr, "Timer's Tick event is handled by Handler.DynamicHandler." & vbLf)
      il.Emit(OpCodes.Call, concat2)

      ' The stack now has an instance of TextBlock and the concatenated
      ' string. Call the "set" accessor, which pops these items off the
      ' stack and stores the string in the property. 
      il.Emit(OpCodes.Callvirt, setter)

      ' There is nothing left on the stack, so it is safe to return 
      ' from the Sub.
      il.Emit(OpCodes.Ret)


      ' CreateType must be called before the Handler type can
      ' be used. Pass the TextBlock to the new instance of Handler.
      Dim finished As Type = tb.CreateType()
      Dim helper As Object = Activator.CreateInstance(finished, _
         New Object() { outputBlock })

      ' In order to create the delegate that will
      ' handle the event, a MethodInfo from the finished type
      ' is required.
      Dim eventHandler As MethodInfo = finished.GetMethod("DynamicHandler")

      ' Use the MethodInfo to create a delegate of the correct 
      ' type, and call the AddEventHandler method to hook up 
      ' the event.
      Dim d As [Delegate] = [Delegate].CreateDelegate(handlerType, helper, eventHandler)
      eInfo.AddEventHandler(timer, d)

      ' Save this delegate in a Shared field, so the StopExample
      ' method can unhook it.
      emittedHandler = d

      ' Now add a second handler, using a hard-coded method (that is,
      ' one that already exists in this class). In order to use this 
      ' technique, you must know the signature of the unknown event 
      ' at the time write your code. 
      d = [Delegate].CreateDelegate( _
         handlerType, _
         GetType(Example).GetMethod("HardCodedHandler", _
                                    BindingFlags.NonPublic Or BindingFlags.Static))
      eInfo.AddEventHandler(timer, d)

      ' Save this delegate in a Shared field, so the StopExample
      ' method can unhook it.
      hcHandler = d

      ' Late-bound calls to the Interval and Start property 
      ' are required to start the timer with a one-second
      ' interval.
      t.InvokeMember("Interval", BindingFlags.SetProperty, Nothing, timer, _
         New [Object]() { New TimeSpan(0, 0, 1) })
      t.InvokeMember("Start", BindingFlags.InvokeMethod, Nothing, timer, Nothing) 


      ' Add a handler for clicking the TextBlock, to stop the  
      ' timer and unhook the handlers.
      AddHandler outputBlock.MouseLeftButtonUp, AddressOf StopExample
      outputBlock.Text &= "Click here to stop the example." & vbCrLf

   End Sub

   ' An existing event handler that can be hooked up to an event
   ' by using reflection, if the signature of the event matches the
   ' signature of the handler.
   Private Shared Sub HardCodedHandler(ByVal sender As Object, _
                                       ByVal e As System.EventArgs)

      outputBlock.Text &= "Timer's Tick event is handled by Example.HardCodedHandler." & vbLf
   End Sub


   Private Shared emittedHandler As [Delegate]
   Private Shared hcHandler As [Delegate]

   Private Shared Sub StopExample(ByVal sender As Object, _
               ByVal e As System.Windows.Input.MouseButtonEventArgs)

      ' Stop the timer.
      timer.GetType().InvokeMember("Stop", BindingFlags.InvokeMethod, Nothing, timer, Nothing) 

      ' Unhook the handlers.
      Dim eInfo As EventInfo = timer.GetType.GetEvent("Tick")
      eInfo.RemoveEventHandler(timer, emittedHandler)
      eInfo.RemoveEventHandler(timer, hcHandler)

      ' Unhook the mouse event.
      RemoveHandler outputBlock.MouseLeftButtonUp, AddressOf StopExample
      outputBlock.Text &= "Refresh the page to run the example again." & vbCrLf
   End Sub
End Class

' This code example produces output similar to the following:
'
'Press the Enter key to end the program.
'Timer's Tick event is handled by Handler.DynamicHandler.
'Timer's Tick event is handled by Example.HardCodedHandler.
'Timer's Tick event is handled by Handler.DynamicHandler.
'Timer's Tick event is handled by Example.HardCodedHandler.
'Timer's Tick event is handled by Handler.DynamicHandler.
'Timer's Tick event is handled by Example.HardCodedHandler.
'Refresh the page to run the example again.
using System;
using System.Reflection;
using System.Reflection.Emit;

public class Example
{
   // The type used for this code example is the DispatcherTimer 
   // class. The Timer object is stored in a variable of type object,
   // and all code that accesses the Timer does so late-bound. This
   // is because the scenario in which you might use the AddEVentHander
   // method is when you load the type after the program is already
   // compiled, when it is not possible to use the Visual Basic
   // AddHandler syntax to hook up the event. 
   //
   private static object timer;

   private static System.Windows.Controls.TextBlock outputBlock;

   public static void Demo(System.Windows.Controls.TextBlock outputBlock)
   {
      Example.outputBlock = outputBlock;

      // Get a Type object for the TextBlock.
      Type obType = outputBlock.GetType();

      // Get a Type object representing the DispatcherTimer type.
      Type t = typeof(System.Windows.Threading.DispatcherTimer);

      // Create an instance of the Timer type.
      timer = Activator.CreateInstance(t);

      // Use reflection to get the Tick event.
      EventInfo eInfo = t.GetEvent("Tick");

      // In order to create a method to handle the Tick event,
      // it is necessary to know the signature of the delegate 
      // used to raise the event. Reflection.Emit can then be
      // used to construct a dynamic class with a static method
      // that has the correct signature.
      //
      // Get the event handler type of the Tick event. This is
      // a delegate type, so it has an Invoke method that has
      // the same signature as the delegate. The following code
      // creates an array of Type objects that represent the 
      // parameter types of the Invoke method.
      //
      Type handlerType = eInfo.EventHandlerType;
      MethodInfo invokeMethod = handlerType.GetMethod("Invoke");
      ParameterInfo[] parms = invokeMethod.GetParameters();
      Type[] parmTypes = new Type[parms.Length];
      for(int i = 0; i < parms.Length; i++)
      {
         parmTypes[i] = parms[i].ParameterType;
      }

      // Use Reflection.Emit to create a dynamic assembly that
      // will be run but not saved. An assembly must have at 
      // least one module, which in this case contains a single
      // type. The only purpose of this type is to contain the 
      // event handler method and the TextBlock where output is
      // displayed. 
      AssemblyName aName = new AssemblyName();
      aName.Name = "DynamicTypes";
      AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, 
                                                  AssemblyBuilderAccess.Run);
      ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
      TypeBuilder tb = mb.DefineType("Handler", TypeAttributes.Public);

      // Define a field to hold the TextBlock where output will
      // be displayed.
      FieldBuilder output = tb.DefineField("outputBlock", obType, FieldAttributes.Private);

      // Define a constructor that takes a TextBlock.
      Type[] ctorParams = {obType};
      ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public, 
                                            CallingConventions.Standard, ctorParams);

      ILGenerator il = ctor.GetILGenerator();

      // Base class constructor.
      ConstructorInfo objCtor = typeof(object).GetConstructor(new Type[] {});
      il.Emit(OpCodes.Ldarg_0);
      il.Emit(OpCodes.Call, objCtor);

      // The constructor stores the TextBlock in the field. First, get 
      // the new DynamicHandler instance, then get the parameter passed
      // to the constructor, then store that value in the field.
      il.Emit(OpCodes.Ldarg_0);
      il.Emit(OpCodes.Ldarg_1);
      il.Emit(OpCodes.Stfld, output);
      // The stack is now clear. Return.
      il.Emit(OpCodes.Ret);

      // Create the method that will handle the event. The name
      // is not important.
      //
      // The parameter types and return type of the method are
      // the same as those of the delegate's Invoke method, 
      // captured earlier.
      MethodBuilder handler = tb.DefineMethod("DynamicHandler", 
                         MethodAttributes.Public, invokeMethod.ReturnType, parmTypes);

      // Generate code to handle the event. 
      il = handler.GetILGenerator();

      // In this case, the handler concatenates a text
      // string to the Text property of the TextBlock.
      // To obtain the current text from the text block, and to 
      // store the updated value, get the "get" and "set" accessors
      // for the Text property:
      PropertyInfo textProp = obType.GetProperty("Text");
      MethodInfo getter = textProp.GetGetMethod();
      MethodInfo setter = textProp.GetSetMethod();

      // To concatenate two strings, get the static Concat method of 
      // the String class:
      MethodInfo concat2 = typeof(string).GetMethod("Concat", 
                 BindingFlags.Public | BindingFlags.Static,
                 Type.DefaultBinder, 
                 new Type[] { typeof(string), typeof(string) }, null);

      // At the very end of the method, the TextBlock instance needs 
      // to be on the execution stack, to call the "set" accessor for
      // the Text property. The TextBlock is stored in the private 
      // field. Use the Handler instance to retrieve it.
      il.Emit(OpCodes.Ldarg_0);
      il.Emit(OpCodes.Ldfld, output);

      // The existing value of the TextBlock.Text property must be on
      // the stack, too, so load the TextBlock instance again. Then 
      // call the "get" accessor for the Text property. The second
      // TextBlock reference is popped off the stack, and the string 
      // value is pushed onto the stack.
      il.Emit(OpCodes.Ldarg_0);
      il.Emit(OpCodes.Ldfld, output);
      il.Emit(OpCodes.Callvirt, getter);

      // Load the output string, then call String.Concat to 
      // Concatenate the two strings.
      il.Emit(OpCodes.Ldstr, "Timer's Tick event is handled by Handler.DynamicHandler.\n");
      il.Emit(OpCodes.Call, concat2);

      // The stack now has an instance of TextBlock and the concatenated
      // string. Call the "set" accessor, which pops these items off the
      // stack and stores the string in the property. 
      il.Emit(OpCodes.Callvirt, setter);

      // There is nothing left on the stack, so it is safe to return 
      // from the void method.
      il.Emit(OpCodes.Ret);


      // CreateType must be called before the Handler type can
      // be used. Pass the TextBlock to the new instance of Handler.
      Type finished = tb.CreateType();
      object helper = Activator.CreateInstance(finished, new object[] { outputBlock });

      // In order to create the delegate that will
      // handle the event, a MethodInfo from the finished type
      // is required.
      MethodInfo eventHandler = finished.GetMethod("DynamicHandler");

      // Use the MethodInfo to create a delegate of the correct 
      // type, and call the AddEventHandler method to hook up 
      // the event.
      Delegate d = Delegate.CreateDelegate(handlerType, helper, eventHandler);
      eInfo.AddEventHandler(timer, d);

      // Save this delegate in a Shared field, so the StopExample
      // method can unhook it.
      emittedHandler = d;


      // Now add a second handler, using a hard-coded method (that is,
      // one that already exists in this class). In order to use this 
      // technique, you must know the signature of the unknown event 
      // at the time write your code. 
      d = Delegate.CreateDelegate(handlerType, 
                   typeof(Example).GetMethod("HardCodedHandler", 
                   BindingFlags.NonPublic | BindingFlags.Static));
      eInfo.AddEventHandler(timer, d);

      // Save this delegate in a Shared field, so the StopExample
      // method can unhook it.
      hcHandler = d;

      // Late-bound calls to the Interval and Start property 
      // are required to start the timer with a one-second
      // interval.
      t.InvokeMember("Interval", BindingFlags.SetProperty, null, timer, 
                                  new object[] { new TimeSpan(0, 0, 1) });
      t.InvokeMember("Start", BindingFlags.InvokeMethod, null, timer, null);


      // Add a handler for clicking the TextBlock, to stop the  
      // timer and unhook the handlers.
      outputBlock.MouseLeftButtonUp += 
         new System.Windows.Input.MouseButtonEventHandler(StopExample);
      outputBlock.Text += "Click here to stop the example.\r\n";

   }

   // An existing event handler that can be hooked up to an event
   // by using reflection, if the signature of the event matches the
   // signature of the handler.
   private static void HardCodedHandler(object sender, System.EventArgs e)
   {

      outputBlock.Text += "Timer's Tick event is handled by Example.HardCodedHandler.\n";
   }


   private static Delegate emittedHandler;
   private static Delegate hcHandler;

   private static void StopExample(object sender, System.Windows.Input.MouseButtonEventArgs e)
   {

      // Stop the timer.
      timer.GetType().InvokeMember("Stop", BindingFlags.InvokeMethod, null, timer, null);

      // Unhook the handlers.
      EventInfo eInfo = timer.GetType().GetEvent("Tick");
      eInfo.RemoveEventHandler(timer, emittedHandler);
      eInfo.RemoveEventHandler(timer, hcHandler);

      // Unhook the mouse event.
      outputBlock.MouseLeftButtonUp -= 
                new System.Windows.Input.MouseButtonEventHandler(StopExample);
      outputBlock.Text += "Refresh the page to run the example again.\r\n";
   }
}

/* This code example produces output similar to the following:

Press the Enter key to end the program.
Timer's Tick event is handled by Handler.DynamicHandler.
Timer's Tick event is handled by Example.HardCodedHandler.
Timer's Tick event is handled by Handler.DynamicHandler.
Timer's Tick event is handled by Example.HardCodedHandler.
Timer's Tick event is handled by Handler.DynamicHandler.
Timer's Tick event is handled by Example.HardCodedHandler.
Refresh the page to run the example again.
 */

Version Information

Silverlight

Supported in: 5, 4, 3

Silverlight for Windows Phone

Supported in: Windows Phone OS 7.1, Windows Phone OS 7.0

XNA Framework

Supported in: Xbox 360, Windows Phone OS 7.0

Platforms

For a list of the operating systems and browsers that are supported by Silverlight, see Supported Operating Systems and Browsers.