Share via


Eventos personalizados y descriptores de acceso de eventos en componentes de Windows en tiempo de ejecución

Con la compatibilidad de .NET Framework para Windows en tiempo de ejecución resulta más sencillo declarar eventos en los componentes de Windows en tiempo de ejecución. Para hacer esto, se ocultan las diferencias entre el patrón de evento de Windows en tiempo de ejecución y el patrón de evento de .NET Framework. Sin embargo, al declarar descriptores de acceso de eventos personalizados en un componente de Windows en tiempo de ejecución, debes seguir el patrón de Windows en tiempo de ejecución.

Cuando te registras para controlar un evento en Windows en tiempo de ejecución, el descriptor de acceso add devuelve un token. Pasa este token al descriptor de acceso de remove para anular el registro. Esto indica que los descriptores de acceso de add y remove para los eventos de Windows en tiempo de ejecución tienen firmas diferentes a los descriptores de acceso con los que ya estás familiarizado.

Afortunadamente, los compiladores de C# y Visual Basic simplifica este proceso: cuando se declara un evento con los descriptores de acceso personalizados en un componente de Windows en tiempo de ejecución, los compiladores usan automáticamente el patrón de Windows en tiempo de ejecución. Por ejemplo, se obtiene un error de compilación si su descriptor de acceso add no devuelve un token. .NET Framework proporciona dos tipos para prestar compatibilidad a la implementación:

  • La estructura de EventRegistrationToken representa el token.

  • La clase EventRegistrationTokenTable<T> crea los tokens y mantiene una asignación entre los tokens y controladores de eventos. El argumento de tipo genérico es el tipo de argumento del evento. Se crea una instancia de esta clase para cada evento, la primera vez que se registra un controlador de eventos para ese evento.

El código siguiente para el evento NumberChanged muestra el patrón básico para los eventos de Windows en tiempo de ejecución. En este ejemplo, el constructor del objeto del argumento de evento, NumberChangedEventArgs, se sirve de un único parámetro entero que representa el valor numérico cambiado.

NotaNota

Este es el mismo patrón que usan los compiladores para eventos normales que puedes declarar en un componente de Windows en tiempo de ejecución.

    private EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>> 
        m_NumberChangedTokenTable = null;

    public event EventHandler<NumberChangedEventArgs> NumberChanged
    {
        add
        {
            return EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
                .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
                .AddEventHandler(value);
        }
        remove
        {
            EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
                .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
                .RemoveEventHandler(value);
        }
    }

    internal void OnNumberChanged(int newValue)
    {
        EventHandler<NumberChangedEventArgs> temp = 
            EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
            .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
            .InvocationList;
        if (temp != null)
        {
            temp(this, new NumberChangedEventArgs(newValue));
        }
    }
Private m_NumberChangedTokenTable As  _
    EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs))

Public Custom Event NumberChanged As EventHandler(Of NumberChangedEventArgs)

    AddHandler(ByVal handler As EventHandler(Of NumberChangedEventArgs))
        Return EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            AddEventHandler(handler)
    End AddHandler

    RemoveHandler(ByVal token As EventRegistrationToken)
        EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            RemoveEventHandler(token)
    End RemoveHandler

    RaiseEvent(ByVal sender As Class1, ByVal args As NumberChangedEventArgs)
        Dim temp As EventHandler(Of NumberChangedEventArgs) = _
            EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            InvocationList
        If temp IsNot Nothing Then
            temp(sender, args)
        End If
    End RaiseEvent
End Event

El método static (Shared en Visual Basic) GetOrCreateEventRegistrationTokenTable crea la instancia del evento del objeto EventRegistrationTokenTable<T> de forma diferida. Pasa el campo de nivel de clase que contendrá la instancia de tabla de tokens para este método. Si el campo está vacío, el método crea la tabla, almacena una referencia en la tabla en el campo y devuelve una referencia a la tabla. Si el campo ya contiene una referencia de tabla de tokens, el método solo devuelve esa referencia.

Nota importanteImportante

Para garantizar la seguridad de los subprocesos, el campo que contiene la instancia del evento EventRegistrationTokenTable<T> debe ser un campo de nivel de clase. Si es un campo de nivel de clase, el método GetOrCreateEventRegistrationTokenTable garantiza que cuando varios subprocesos intenten crear la tabla de tokens, todos van obtener la misma instancia de la tabla. Para un evento determinado, todas las llamadas al método GetOrCreateEventRegistrationTokenTable deben usar el mismo campo de nivel de clase.

Cuando se llama al método GetOrCreateEventRegistrationTokenTable en el descriptor de acceso remove y en el método RaiseEvent (método OnRaiseEvent en C#), se garantiza que no se produzcan excepciones si se llama a estos métodos antes de agregar delegados del controlador de eventos.

Los miembros restantes de la clase EventRegistrationTokenTable<T> que se usan en el patrón de eventos de Windows en tiempo de ejecución incluyen lo siguiente:

  • El método AddEventHandler genera un token para el delegado del controlador de eventos, almacena el delegado en la tabla, lo agrega a la lista de invocación y devuelve el token.

  • La sobrecarga del método RemoveEventHandler(EventRegistrationToken) quita el delegado de la tabla y de la lista de invocación.

    NotaNota

    Los métodos AddEventHandler y RemoveEventHandler(EventRegistrationToken) bloquean la tabla para ayudar a garantizar la seguridad de los subprocesos.

  • La propiedad InvocationList devuelve un delegado que incluye todos los controladores de eventos registrados actualmente para controlar el evento. Usa este delegado para generar el evento o usa los métodos de la clase de Delegate para invocar controladores individualmente.

    NotaNota

    Te recomendamos que sigas el patrón que se muestra en el ejemplo proporcionado anteriormente en este artículo y que copies el delegado en una variable temporal antes de invocarlo. De esta forma se evita una condición de carrera en la que un subproceso pueda quitar el último controlador, asignando al delegado un valor null justo antes de que otro subproceso intente invocar al delegado. Los delegados son inmutables, por lo que la copia es aún válida.

Coloca tu propio código en los descriptores de acceso adecuados. Si la seguridad de los subprocesos representa un problema, debes aportar tu propio bloqueo para el código.

Usuarios de C#: cuando escribas descriptores de acceso de eventos personalizados en el patrón de evento de Windows en tiempo de ejecución, el compilador no proporcionará los accesos directos sintácticos habituales. Genera errores si se usa el nombre del evento en el código.

Usuarios de Visual Basic: en .NET Framework, un evento es simplemente un delegado de multidifusión que representa todos los controladores de eventos registrados. Si se genera el evento solo se estaría invocando el delegado. La sintaxis de Visual Basic oculta normalmente las interacciones con el delegado y el compilador copia el delegado antes de invocarlo, como se describe en la nota sobre la seguridad de los subprocesos. Cuando se crea un evento personalizado en un componente de Windows en tiempo de ejecución, debes tratar con el delegado directamente. Esto también quiere decir que puedes, por ejemplo, usar el método MulticastDelegate.GetInvocationList para obtener una matriz que contenga un delegado independiente para cada controlador de eventos, siempre que quieras invocar controladores por separado.

Vea también

Tareas

Cómo: Implementar descriptores de acceso de eventos personalizados (Guía de programación de C#)

Referencia

Eventos (Guía de programación de C#)

Otros recursos

Eventos (Visual Basic)

.NET Framework Support for Windows Store Apps and Windows Runtime