2010-11-10 30 views
8

Necesito pasar un evento como parámetro a una función. ¿Hay alguna forma de hacer esto?VB.NET - Pasar un evento como un parámetro

La razón es que tengo una secuencia de dos líneas de código dispersas por todo mi programa, donde elimino dinámicamente el controlador de un evento y luego establezco el controlador nuevamente. Estoy haciendo esto para varios eventos diferentes y manejadores de eventos, así que he decidido escribir una función que lo haga.

Como ejemplo, digamos que tengo un cuadro combinado en mi código llamado combobox1, y tengo el controlador llamado indexChangedHandler. En varios lugares de mi código, tienen las siguientes dos líneas:

RemoveHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler 
AddHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler 

Ahora, yo no quiero seguir repitiendo las dos líneas anteriores de código (o similar) por todo el programa, por lo que' m en busca de una manera de hacer esto:

Private Sub setHandler(evt As Event, hndler As eventhandler) 
    RemoveHandler evt, hndler 
    AddHandler evt, hndler 
End Sub 

por lo que en todas partes donde las dos líneas de código (o similar) se producen en mi programa, sólo puede sustituirlos por:

setHandler(combobox1.SelectedIndexChanged, AddressOf indexChangedHandler) 

hasta ahora , la parte "evt como evento" del El argumento de la función setHandler está dando un error.

P.S: He hecho esta pregunta en otros foros y me siguen preguntando por qué querría configurar el controlador inmediatamente después de eliminarlo. La razón es porque dinámicamente agregar un controlador de eventos n veces hace que el controlador se ejecute n veces cuando se produce el evento. Para evitar esto, es decir, para garantizar que el controlador se ejecute solo una vez cuando se produce el evento, primero elimino el controlador cada vez que deseo agregar el controlador dinámicamente.

Es posible que se pregunte por qué el controlador se agregará varias veces en primer lugar ... La razón es porque agrego el controlador solo después de un evento en particular, digamos E1, en mi forma se ha producido (agrego el controlador dentro del controlador del evento E1). Y el evento E1 puede ocurrir varias veces dentro de mi formulario. Si no elimino el controlador cada vez que lo vuelvo a agregar, el controlador se agrega y, por lo tanto, se ejecuta varias veces.

En cualquier caso, el procesamiento que se produce dentro de la función no es de importancia fundamental para mí en este momento, sino más bien el único medio para pasar un evento como parámetro.

+1

¿Puede decirnos cuál es el error? –

+0

No hay una manera real de hacerlo programáticamente, ya que no puede pasar referencias a un miembro de evento. Tampoco estoy exactamente seguro de qué se supone que haga SetHandler ... ¿puedes aclarar por qué necesitarías eliminar un controlador de eventos y luego volver a agregarlo? – cdhowie

+0

@cdhowie Estaba en el proceso de editar mi publicación para explicar por qué tendría que hacer esto, me interrumpieron, volví y completé la edición y la envié, antes de darme cuenta de que ya había hecho la pregunta. En resumen, la razón ahora está incluida en la publicación original :) – Tracer

Respuesta

10

Por supuesto puede pasar eventos alrededor ... Bueno, puede pasar Action(Of EventHandler) que puede hacer lo que quiera.

Si usted tiene una clase que define un evento de este modo:

Public Class ComboBox 
    Public Event SelectedIndexChanged As EventHandler 
End Class 

Dada una instancia de ComboBox a continuación, puede crear añadir & acciones del manejador de eliminación de este modo:

Dim combobox1 = New ComboBox() 

Dim ah As Action(Of EventHandler) 
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h 
Dim rh As Action(Of EventHandler) 
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h 

(Ahora, esto es VB.NET 4.0 de código, pero se puede hacer esto en 3,5 usando AddressOf y un poco más curioseaba.)

Así que si yo HÅ ve un controlador Foo:

Public Sub Foo(ByVal sender as Object, ByVal e As EventArgs) 
    Console.WriteLine("Over here with Foo!") 
End Sub 

y un método Raise en ComboBox ahora puedo hacer esto: "! Por aquí con Foo"

ah(AddressOf Foo) 
combobox1.Raise() 
rh(AddressOf Foo) 

Esto escribe el mensaje como se esperaba.

también puedo crear este método:

Public Sub PassActionOfEventHandler(ByVal h As Action(Of EventHandler)) 
    h(AddressOf Foo) 
End Sub 

me puede pasar en torno a las acciones de control de eventos, así:

PassActionOfEventHandler(ah) 
combobox1.Raise() 
PassActionOfEventHandler(rh) 

que de nuevo escribe el mensaje "Por aquí con Foo!".

Ahora, un problema que puede ser un problema es que accidentalmente puede cambiar el agregar y quitar delegados del controlador de eventos en el código, después de todo, son del mismo tipo. Por lo que es fácil de definir simplemente delegados inflexible de tipos para el complemento y retirar las acciones de este modo:

Public Delegate Sub AddHandlerDelegate(Of T)(ByVal eh as T) 
Public Delegate Sub RemoveHandlerDelegate(Of T)(ByVal eh as T) 

El código para definir los casos de delegado no cambia a excepción de los tipos de delegado:

Dim ah As AddHandlerDelegate(Of EventHandler) 
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h 
Dim rh As RemoveHandlerDelegate(Of EventHandler) 
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h 

Así que esto ahora te permite ser muy creativo. Puede definir una función que tomará el delegado del controlador de agregar, el delegado del controlador de eliminación y un manejador de eventos, que conectará un manejador de eventos, y luego le devolverá un IDisposable que puede usar más adelante para eliminar el controlador sin necesidad de retener una referencia al controlador de eventos. Esto es útil para usar las declaraciones Using.

Aquí está la firma:

Function SubscribeToEventHandler(
    ByVal h as EventHandler, 
    ByVal ah As AddHandlerDelegate(Of EventHandler), 
    ByVal rh As RemoveHandlerDelegate(Of EventHandler)) As IDisposable 

Así da esta función ahora puedo hacer esto:

combobox1.Raise() 
Using subscription = SubscribeToEventHandler(AddressOf Foo, ah, rh) 
    combobox1.Raise() 
    combobox1.Raise() 
End Using 
combobox1.Raise() 

Y esto escribe el mensaje "Por aquí con Foo!" solo dos veces. La primera y la última llamada a Raise están fuera de la suscripción al controlador de eventos.

¡Disfrútalo!

+1

Si entiendo esto bien, estás pasando 'Delegados' alrededor, no los 'Eventos' en sí, como se le preguntó en la pregunta. Una muy buena respuesta, no obstante. – Bobby

+0

Enigmatividad, gracias por su respuesta detallada. Sin embargo, fue un poco difícil de entender para mí, ya que vi algunos constructos de aspecto extraño en tu publicación (la primera vez que veo esa sintaxis, así que por favor, desnúdate conmigo). Pero por lo que he entendido, siempre debo definir explícitamente el objeto y el evento en "ah" y "rh" ¿no? lo que me gustaría saber es si de todos modos no se limita la definición de "ah" a "combobox1.selectedIndexChanged", así que puedo usar la misma función para configurar el controlador en un objeto diferente (cbx2), por ejemplo, " cbx2.selectedIndexChanged ". – Tracer

+0

@ 'Tracer' - Sí. Simplemente cambie las definiciones de delegado a 'Public Delegate Sub AddHandlerDelegate (Of T) (ByVal eh como T, comboBox como ComboBox)' y puede hacer que funcione para cualquier 'ComboBox'. si tiene que hacer otros eventos, le sugiero que llame al delegado 'AddSelectedIndexChangedHandler', etc. Agregue y elimine delegados para cada evento y control. – Enigmativity

2

No se puede pasar eventos. Event no es un tipo, es una palabra clave que define un par de métodos, agregar y eliminar, utilizado para cambiar el estado del miembro del evento de la clase. Se parecen mucho a las propiedades a este respecto (que tienen métodos get y set).

Al igual que las propiedades, los métodos para agregar y quitar pueden hacer lo que usted desee. Normalmente, estos no harán nada más que mantener una instancia de delegado, que a su vez es MulticastDelegate o en otras palabras, una lista de delegados a los que se les llama uno por uno si se produce el evento.

Puede ver claramente esta estructura en C#, donde no está enmascarada con AddHandler/RemoveHandler, pero edita directamente la lista de controladores asociados: myObject.Event += new Delegate(...);.

Y, de nuevo como propiedades, ser miembro de una clase que realmente abstrae dos métodos diferentes, no es posible pasar literalmente un evento como un objeto.

+0

Gracias por esa explicación, Bobby. – Tracer

2

Existen varios significados diferentes para el término "evento", según el contexto. En el contexto que está buscando, un Evento es un par de métodos combinados que efectivamente agregará o eliminará un delegado de una lista de suscripción. Desafortunadamente, no hay clases en VB.NET para representar una dirección de método sin un objeto adjunto.Uno probablemente podría usar reflection para buscar los métodos apropiados, y luego pasar esos métodos a una rutina que los llamaría a ambos, pero en su situación particular eso probablemente sería una tontería.

La única situación en la que veo que podría ser útil pasar un evento en el sentido descrito es algo así como un objeto IDisposable que se suscribe a eventos de objetos de vida más larga y necesita cancelar la suscripción de todos sus eventos cuando está dispuesto. En ese caso, puede ser útil utilizar la reflexión para buscar el método de quitar delegado para cada evento, y luego llamar a todos los métodos de eliminación de delegados cuando se elimina un objeto. Desafortunadamente, no conozco ninguna forma de representar los eventos que se recuperarán, excepto como literales de cadena, cuya validez no pudo verificarse hasta el tiempo de ejecución.

Cuestiones relacionadas