Skip to content

6. Gerenciamento de eventos

No capítulo anterior, abordamos o conceito de eventos vinculados a componentes. Veremos agora como criar eventos em nossas próprias classes. Trata-se de um conceito complexo que não interessará aos desenvolvedores iniciantes, aos quais recomendamos passar diretamente para o próximo capítulo.

6.1. Objetos delegate

A instrução

    Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer

define um tipo chamado opération, que é, na verdade, um protótipo de função que aceita dois inteiros e retorna um inteiro. É a palavra-chave delegate que faz de opération uma definição de protótipo de função. O tipo [opération] pode ser atribuído a uma variável da seguinte maneira:


        Dim op As New opération(AddressOf [fonction])

Uma variável do tipo [délégué] é criada como um objeto. O parâmetro do construtor é a função associada à instância do delegado. Em VB, essa função é fornecida na forma AddressOf função, onde [fonction] é o nome da função. Uma vez criada a instância de um delegado, ela pode ser usada como uma função:


        ' executa-se a função delegada
        Dim n As Integer = op(4, 7)

O delegado [opération] foi definido para receber dois parâmetros do tipo [integer] e retornar um resultado do mesmo tipo. Portanto, sua instância [op], apresentada acima, é utilizada como tal função. Consideremos o seguinte exemplo:


' funções delegadas
Imports System

Public Class delegate1
    ' definição de um protótipo de função
    ' aceita 2 inteiros como parâmetros e retorna um inteiro
    Delegate Function opération(ByVal n1 As Integer, ByVal n2 As Integer) As Integer

    ' dois métodos de instância correspondentes ao protótipo
    ' adicionar
    Public Function ajouter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
        Console.Out.WriteLine(("ajouter(" & n1 & "," & n2 & ")"))
        Return n1 + n2
    End Function

    ' subtrair
    Public Function soustraire(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
        Console.Out.WriteLine(("soustraire(" & n1 & "," & n2 & ")"))
        Return n1 - n2
    End Function

    ' um método estático correspondente ao protótipo
    Public Shared Function augmenter(ByVal n1 As Integer, ByVal n2 As Integer) As Integer
        Console.Out.WriteLine(("augmenter(" & n1 & "," & n2 & ")"))
        Return n1 + 2 * n2
    End Function

    ' programa de teste
    Public Shared Sub Main()
        ' define-se um objeto do tipo operação para registrar funções nele
        ' registra-se a função estática aumentar
        Dim op As New opération(AddressOf delegate1.augmenter)
        ' executa-se o delegado
        Dim n As Integer = op(4, 7)
        Console.Out.WriteLine(("n=" & n))
        ' criação de um objeto c1 do tipo class1
        Dim c1 As New delegate1
        ' registra-se no delegado o método “adicionar” de c1
        op = New delegate1.opération(AddressOf c1.ajouter)
        ' execução do objeto delegado
        n = op(2, 3)
        Console.Out.WriteLine(("n=" & n))
        ' registra-se no delegado o método “subtrair” de c1
        op = New delegate1.opération(AddressOf c1.soustraire)
        n = op(2, 3)
        Console.Out.WriteLine(("n=" & n))
    End Sub
End Class

Os resultados da execução são os seguintes:

augmenter(4,7)
n=18
ajouter(2,3)
n=5
soustraire(2,3)
n=-1

6.2. Gerenciamento de eventos

Para que serve um objeto do tipo delegate?

Como veremos no exemplo a seguir, ele serve principalmente para o gerenciamento de eventos. Uma classe C1 pode gerar eventos evti. Durante um evento evti, um objeto do tipo C1 iniciará a execução de um objeto evtiDéclenché do tipo delegate. Todas as funções registradas no objeto delegado evtiDéclenché serão então executadas. Se um objeto C2 que utiliza um objeto C1 quiser ser notificado sobre a ocorrência do evento evti no objeto C1, ele registrará um de seus métodos C2.f no objeto delegado C1.evtiDéclenché do objeto C1, para que o método C2.f seja executado sempre que oevento evti ocorrer no objeto C1. Como o objeto delegado C1.evtiDéclenché pode registrar várias funções, diferentes objetos Ci poderão se registrar junto ao delegado C1.evtiDéclenché para serem notificados sobre o evento evti no C1.

6.2.1. Declaração de um evento

Um evento é declarado da seguinte maneira (1):


Delegate Sub proc(liste d'arguments)
Public Event événement as proc

ou (2)


Public Event événement(liste d'arguments)

A palavra-chave [Event] define um dado como um evento. Associamos ao evento a assinatura que os gerenciadores do mesmo deverão ter. É possível definir essa assinatura por meio de um delegado (método 1) ou diretamente (método 2).

6.2.2. Definir os manipuladores de um evento

Quando um evento é acionado pela instrução [RaiseEvent], ele deve ser processado. Os procedimentos encarregados de processar os eventos são chamados de manipuladores de eventos. Ao criar um objeto [Event], apenas a assinatura dos manipuladores do evento é declarada. Essa assinatura é a do objeto [delegate] associado ao evento. Feito isso, será possível associar manipuladores ao evento. Esses manipuladores são procedimentos que devem ter a mesma assinatura que o [delegate] associado ao evento. Para associar um manipulador de evento a um evento, utiliza-se a instrução [AddHandler] com a seguinte sintaxe:

AddHandler event, AddressOf eventhandler
  • event: variável de evento à qual se associa um novo manipulador de eventos
  • eventhandler: nome do manipulador de eventos — deve ter a assinatura do [délégué] associado ao evento

É possível associar quantos manipuladores de eventos forem desejados a um evento. Todos eles serão executados quando o evento ao qual estão associados for acionado.

6.2.3. Disparar um evento

Para acionar um evento por meio de programa, utiliza-se a instrução [RaiseEvent]:

RaiseEvent eventname[( argumentlist )]
  • eventname: variável de evento (declarada com a palavra-chave Event)
  • argumentlist: argumentos do delegado associado ao evento.

Todos os manipuladores que foram associados ao evento pela instrução AddHandler serão chamados. Eles receberão como parâmetros os parâmetros definidos na assinatura do evento.

6.2.4. Um exemplo

Consideremos o seguinte exemplo:


'gerenciamento de eventos
Imports System

Public Class myEventArgs
    Inherits EventArgs
    ' a classe de um evento
    ' atributo
    Private _saisie As String

    ' construtor
    Public Sub New(ByVal saisie As String)
        _saisie = saisie
    End Sub

    ' propriedade inserida como somente leitura
    Public Overrides Function ToString() As String
        Return _saisie
    End Function
End Class

Public Class émetteur
    ' a classe emissora de um evento
    ' atributo
    Private _nom As String    ' nom de l'émetteur

    ' construtor
    Public Sub New(ByVal nom As String)
        _nom = nom
    End Sub

    ' ToString
    Public Overrides Function ToString() As String
        Return _nom
    End Function

    ' o protótipo das funções responsáveis pelo tratamento do evento
    Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)

    ' o conjunto de gerenciadores de eventos
    Public Event evtHandler As _evtHandler

    ' método para solicitar a emissão de um evento
    Public Sub envoyerEvt(ByVal evt As myEventArgs)
        ' notificamos todos os assinantes
        ' age-se como se o evento tivesse origem aqui
        RaiseEvent evtHandler(Me, evt)
    End Sub
End Class

' uma classe de processamento do evento
Public Class souscripteur
    ' atributo
    Private nom As String    ' nom du souscripteur
    Private sender As émetteur    ' l'émetteur des évts

    ' construtor
    Public Sub New(ByVal nom As String, ByVal e As émetteur)
        ' registramos o nome do assinante
        Me.nom = nom
        ' e o emissor dos eventos
        Me.sender = e
        ' inscreve-se para receber os eventos do emissor e
        AddHandler e.evtHandler, AddressOf traitementEvt
    End Sub

    ' gerenciador de eventos
    Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
        ' exibição de eventos
        Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
    End Sub
End Class

' um programa de teste
Public Class test
    Public Shared Sub Main()
        ' criação de um emissor de eventos
        Dim émetteur1 As New émetteur("émetteur1")
        ' criação de uma tabela de assinantes
        ' para os eventos emitidos pelo emissor1
        Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
        ' lê-se uma sequência de números inteiros do teclado
        ' assim que um deles estiver incorreto, emite-se um evento
        Console.Out.Write("Nombre entier (rien pour arrêter) : ")
        Dim saisie As String = Console.In.ReadLine().Trim()
        ' enquanto a linha inserida não estiver vazia
        While saisie <> ""
            ' a entrada é um número inteiro?
            Try
                Dim n As Integer = Integer.Parse(saisie)
            Catch
                ' não é um número inteiro
                ' avisa-se a todos
                émetteur1.envoyerEvt(New myEventArgs(saisie))
            End Try
            ' avisa-se a todos
            ' nova entrada
            Console.Out.Write("Nombre entier (rien pour arrêter) : ")
            saisie = Console.In.ReadLine().Trim()
        End While
        ' fim
        Environment.Exit(0)
    End Sub
End Class

O código anterior é bastante complexo. Vamos detalhá-lo. Em um gerenciamento de eventos, há um emissor de eventos (sender) que envia os detalhes dos eventos (EventArgs) aos assinantes que se declararam interessados nos eventos em questão. A classe emissora dos eventos é, neste caso, a seguinte:


Public Class émetteur
    ' a classe emissora de um evento
    ' atributo
    Private _nom As String    ' nom de l'émetteur

    ' construtor
    Public Sub New(ByVal nom As String)
        _nom = nom
    End Sub

    ' ToString
    Public Overrides Function ToString() As String
        Return _nom
    End Function

    ' o protótipo das funções responsáveis pelo tratamento do evento
    Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)

    ' o conjunto de gerenciadores de eventos
    Public Event evtHandler As _evtHandler

    ' método para solicitar a emissão de um evento
    Public Sub envoyerEvt(ByVal evt As myEventArgs)
        ' notificamos todos os assinantes
        ' age-se como se o evento tivesse origem aqui
        RaiseEvent evtHandler(Me, evt)
    End Sub
End Class

Cada objeto émetteur possui um nom definido por padrão. O método ToString foi redefinido de forma que, ao transformar um objeto émetteur em string,, seja recuperado o nome deste último. A classe define um evento:


        ' o protótipo das funções responsáveis pelo tratamento do evento
        Delegate Sub _evtHandler(ByVal sender As Object, ByVal evt As myEventArgs)

        ' o evento
        Public Event evtHandler As _evtHandler

O primeiro argumento de uma função de tratamento de um evento será o objeto que emite o evento. O segundo argumento será do tipo myEventArgs, um objeto que fornecerá detalhes sobre o evento e sobre o qual voltaremos a falar. Para poder acionar um evento de fora de um objeto émetteur, adicionamos à classe o método envoyerEvt:


    ' método para solicitar a emissão de um evento
    Public Sub envoyerEvt(ByVal evt As myEventArgs)
        ' avisamos todos os assinantes
        ' trata-se como se o evento tivesse origem aqui
        RaiseEvent evtHandler(Me, evt)
    End Sub

Precisamos definir quais serão os tipos de eventos acionados pela classe émetteur. Para isso, definimos a classe myEventArgs:


Public Class myEventArgs
    ' a classe de um evento
    ' atributo
    Private _saisie As String

    ' construtor
    Public Sub New(ByVal saisie As String)
        _saisie = saisie
    End Sub

    ' propriedade inserida como somente leitura
    Public Overrides Function ToString() As String
        Return _saisie
    End Function
End Class

Os eventos que nos interessam são entradas incorretas no teclado. Solicitaremos que um usuário digite números inteiros no teclado e, assim que ele digitar uma sequência que não represente um número inteiro, um evento será acionado. Como detalhe do evento, nos limitaremos a fornecer a entrada incorreta. Esse é o significado do atributo _saisie da classe. Um objeto myEventArgs é, portanto, criado com a entrada incorreta como parâmetro. Além disso, redefinimos o método ToString para que, ao converter um objeto myEventArgs em string, obtenhamos o atributo _saisie.

Definimos o emissor dos eventos e o tipo de eventos que ele emite. Em seguida, definimos o tipo de assinantes interessados nesses eventos.


    ' uma classe de tratamento do evento
    Public Class souscripteur
        ' atributo
        Private nom As String     ' nom du souscripteur
        Private sender As émetteur     ' l'émetteur des évts

        ' construtor
        Public Sub New(ByVal nom As String, ByVal e As émetteur)
            ' registra-se o nome do assinante
            Me.nom = nom
            ' e o emissor dos eventos
            Me.sender = e
            ' inscreve-se para receber os eventos do emissor e
            AddHandler e.evtHandler, AddressOf traitementEvt
        End Sub

        ' gerenciador de eventos
        Public Sub traitementEvt(ByVal sender As Object, ByVal evt As myEventArgs)
            ' exibição de eventos
            Console.Out.WriteLine(("L'objet [" + sender.ToString + "] a signalé la saisie erronée [" + evt.ToString + "] au souscripteur [" + nom + "]"))
        End Sub
    End Class

Um assinante será definido por dois parâmetros: seu nome (atributo nom) e o objeto émetteur cujos eventos ele deseja processar (atributo sender). Esses dois parâmetros serão passados ao construtor do objeto. Durante essa mesma criação, o assinante se inscreve para receber os eventos do emissor:


            ' inscreve-se para receber os eventos do emissor e
            AddHandler e.evtHandler, AddressOf traitementEvt

A função registrada junto ao emissor é traitementEvt. Este método da classe souscripteur exibe os dois argumentos que recebeu (sender, evt), bem como o nome do receptor (nom). Foram definidos o tipo dos eventos gerados, o tipo do emissor desses eventos e o tipo dos assinantes. Agora, basta implementá-los:


    ' um programa de teste
    Public Class test
        Public Shared Sub Main()
            ' criação de um emissor de eventos
            Dim émetteur1 As New émetteur("émetteur1")
            ' criação de uma tabela de assinantes
            ' para os eventos emitidos pelo emissor1
            Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}
            ' lê-se uma sequência de números inteiros do teclado
            ' assim que um deles estiver incorreto, emite-se um evento
            Console.Out.Write("Nombre entier (rien pour arrêter) : ")
            Dim saisie As String = Console.In.ReadLine().Trim()
            ' enquanto a linha inserida não estiver vazia
            While saisie <> ""
                ' a entrada é um número inteiro?
                Try
                    Dim n As Integer = Integer.Parse(saisie)
                Catch
                    ' não é um número inteiro
                    ' avisa-se a todos
                    émetteur1.envoyerEvt(New myEventArgs(saisie))
                End Try
                ' avisa-se a todos
                ' nova entrada
                Console.Out.Write("Nombre entier (rien pour arrêter) : ")
                saisie = Console.In.ReadLine().Trim()
            End While
            ' fim
            Environment.Exit(0)
        End Sub

Criamos um objeto émetteur:


         ' criação de um emissor de eventos
        Dim émetteur1 As New émetteur("émetteur1")

Criamos uma tabela com dois assinantes para os eventos emitidos pelo objeto émetteur1:


         ' criação de uma tabela de assinantes
         ' para os eventos emitidos pelo emissor1
        Dim souscripteurs() As souscripteur = {New souscripteur("s1", émetteur1), New souscripteur("s2", émetteur1)}

Solicitamos ao usuário que digite números inteiros no teclado. Assim que ocorrer uma entrada incorreta, solicitamos que o émetteur1 envie um evento aos seus assinantes:


                 ' avisamos a todos
                émetteur1.envoyerEvt(New myEventArgs(saisie))

O evento enviado é do tipo myEventArgs e contém a entrada incorreta. Os dois assinantes devem receber esse evento e sinalizá-lo. É o que mostra a execução a seguir.

dos>evt1
Nombre entier (rien pour arrêter) : 4
Nombre entier (rien pour arrêter) : a
L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s1]
L'objet [émetteur1] a signalé la saisie erronée [a] au souscripteur [s2]
Nombre entier (rien pour arrêter) : 1.6
L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s1]
L'objet [émetteur1] a signalé la saisie erronée [1.6] au souscripteur [s2]
Nombre entier (rien pour arrêter) :