Is The Sky Really The Limit? (Site pessoal de Miguel Prego Gaspar)
 
 
Syndication

Categorias
Do site ...

Locations of visitors to this page





Artigos

Artigos actuais

Tratamento de eventos e "Delegates"
sexta-feira, 20 de Junho de 2008 :: 747 Visualizações :: 0 Comentários :: :: Básicos

Objectivo

Este artigo apresenta os "delegates" e as técnicas para expor e consumir eventos. No final será apresentado um caso prático, que consiste num simulador mais realista do processo de extração de bolas do "Euromilhões" (este exercício é uma evolução do apresentado no artigo sobre OOP disponível aqui)

"Delegates" 

Um "Delegate" é um objecto que permite invocar um método indirectamente.   
Os "Delegates" podem ser utilizados para invocar quer métodos estáticos quer métodos de instância.   
Os "Delegates" são o motor de implementação interna dos eventos.   
Exemplo de declaração de um "Delegate":


Delegate Sub DisplayMessage (ByVal msg as String) 

Exemplo de instanciação de um objecto to tipo "Delegate":

Dim MyDelegate as DisplayMessage 
MyDelegate= new DisplayMessage (AdressOf WriteToDebugWindow)

O procedimento utilizado neste exemplo "WriteToDebugWindow", tem de ter a mesma assinatura que o "delegate" utilizado ("DisplayMessage"):

' Exibe uma mensagem na janela de debug 
Sub WriteToDebugWindow(ByVal msgText As String) 
Debug.WriteLine(msgText) 
End Sub

Exemplo de invocação de um objecto to tipo "Delegate":

Dim MyDelegate as DisplayMessage 
MyDelegate= new DisplayMessage (AdressOf WriteToDebugWindow)
MyDelegate.Invoke ("teste") 


A flexibilidade oferecida por um "delegate" permite alterar o comportamento de uma aplicação com alteração minima de código:

Dim MyDelegate as DisplayMessage 
MyDelegate= new DisplayMessage (AdressOf WriteToPopUpWindow)
MyDelegate.Invoke ("teste")


Exemplo de implementação do método "WriteToPopUpWindow":

' Exibe uma mensagem numa janela pop up 
Sub WriteToPopUpWindow(ByVal msgText As String) 
MsgBox(msgText) 
End Sub

Os eventos são processados internamente através de "Delegates". Uma classe que expõe um evento, define internamente um "delegate" privado que aponta para todos os clientes que subscreveram o evento. Quando o evento dispara, a infra-estrutura do .Net chama o método "invoke" do "delegate" levando a que todos os clientes sejam notificados do evento.

Tratamento de Eventos (Design Time)

Um "Handler" para um evento é criado automaticamente, por exemplo quando se clica no objecto do tipo Button:

Private Sub ButtonOk_Click (ByVal sender as object, ByVal e as eventArgs) Handles ButtonOk.Click 

O "Handler" de um evento aceita sempre dois argumentos:   

1 - Uma referência para o objecto que despoletou o evento.

2 - Um objecto derivado de "System.EventArgs" que possui informação sobre o próprio evento.

Exemplo: O "Handler" do Evento KeyPress recebe um objecto do tipo "KeyPressEventArgs", que por sua vez expõe as propriedades "KeyChar" e "Handled":

Private Textbox1_KeyPress(ByVal sender as object, ByVal e as KeyPressEventArgs) Handles TextBox1.KeyPress 
If e.KeyChar=" " then e.Handled=true

É possível definir um único "Handler" para tratar eventos provenientes de vários controlos.

Exemplo: Um único "Handler" para alterar a cor do controlo que ganha o focus:

Private Sub Control_Enter (ByVal sender as object, ByVal e as eventArgs) Handles TxtFirstName.Enter, TxtLastName.Enter 
    Dim ctrl as control=Directcast(Sender, Control) 
    ctrl.BackColor=color.red 
End Sub

  

Private Sub Control_Leave (ByVal sender as object, ByVal e as eventArgs) Handles TxtFirstName.Leave, TxtLastName.Leave 
    Dim ctrl as control=Directcast(Sender, Control) 
    ctrl.BackColor=SystemColors.Window 
End Sub 

Para que um evento de um objecto possa ser processado, utilizando a expressão "Handles", esse objecto teve de ser declarado com a expressão "WithEvents":

Public WithEvents FirstName as System.Windows.Forms.TextBox 

Tratamento de Eventos (Run Time)

Para obter o máximo de flexibilidade no tratamento de eventos, deve utilizar-se o operador "AddHandler". Nomeadamente para que seja possível:   

  • Tratar eventos de objectos gerados em "Run Time"
  • Tratar eventos estáticos (Shared)
  • Tratar eventos de colecções e "Arrays" de objectos

AddHandler Object.Event, Delegate 

Exemplo:

AddHandler TextBox1.Enter, New EventHandler (AddressOf Control_Enter) 

O VB consegue determinar o "Delegate EventHandler" automaticamente, por isso pode ser utilizada a versão simplificada:

AddHandler TextBox1.Enter, AddressOf Control_Enter 

Neste exemplo o método "Control_Enter" tem de ser implementado, respeitando a assinatura do "Delegate EventHandler"

Inversamente, para deixar de receber as notificações de um evento, utiliza-se o operador "RemoveHandler":

RemoveHandler TextBox1.Enter, AddressOf Control_Enter 

O operador "removeHandler" deve ser utilizado sempre que tenha sido utilizado previamente o operador "AddHandler", de forma a impedir que os objectos "Delegate" fiquem a consumir recursos após a destruição do objecto.

Exemplo:

Public Class Form1 
    Dim MyButton As New Button 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        MyButton.Text = "Teste" 
        MyButton.Height = 100 
        MyButton.Width = 100 
        Me.Controls.Add(MyButton) 
        AddHandler MyButton.Click, AddressOf ShowMessage 
    End Sub 
    Private Sub ShowMessage(ByVal sender As Object, ByVal e As EventArgs) 
        MsgBox("O botão foi Clicado ") 
        RemoveHandler MyButton.Click, AddressOf ShowMessage 
    End Sub 
End Class 

Exemplo de utilização do operador "AddHandler" para tratar eventos estáticos:

Public Class Form1 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        AddHandler Application.Idle, AddressOf Application_Idle 
    End Sub 
    Private Sub Application_Idle(ByVal sender As Object, ByVal e As EventArgs) 
        Label1.Text = "Caracteres: " & TextBox1.TextLength.ToString 
    End Sub 
End Class

O operador "AddHandler" pode ainda ser utilizado para tratar eventos de colecções ou "arrays" objectos.

Exemplo:

Public Class Form1 
    Dim MyButton As Button 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        For x As Integer = 1 To 10 
            MyButton = New Button 
            MyButton.Name = x.ToString 
            MyButton.Text = x.ToString 
            MyButton.Height = 50 
            MyButton.Width = 50 
            MyButton.Left = x * 50 + 10 
            Me.Controls.Add(MyButton) 
            AddHandler MyButton.Click, AddressOf ShowClickedButton 
        Next 
    End Sub 
    Private Sub ShowClickedButton(ByVal sender As Object, ByVal e As EventArgs) 
        Label1.Text = "Clicou no botão:" & CType(sender, Button).Text 
    End Sub 
End Class

Despoletar eventos

Os eventos, tal como os campos, propriedades e métodos, fazem parte do interface que um tipo expõe aos seus clientes.

Os eventos são especiais, pois são activados pelo tipo e não pelo cliente.

Exemplo:

Public Event NameChanged (ByVal sender as object, ByVal e as EventArgs) 

Exemplo de uma classe que expõe um evento:

Public Class User 
    Public Event NameChanged(ByVal sender As Object, ByVal e As EventArgs) 
    Private _Name As String 
    Public Property Name() As String 
        Get 
            Return _Name 
        End Get 
        Set(ByVal Value As String) 
            If _Name <> Value Then 
                _Name = Value 
                RaiseEvent NameChanged(Me, EventArgs.Empty) 
            End If 
        End Set 
    End Property 
End Class

Para tornar o código mais claro e aumentar a performance, deve ser utilizada a forma alternativa, que permite definir um evento em função de um "Delegate" especifico:

Public Event NameChanged As EventHandler 

Event Args

Todos os eventos trabalham com dois argumentos: Uma referência para o objecto que lançou o evento e um objecto do tipo "System.EventArgs" (ou derivado, o nome termina sempre em "EventArgs") que expõe argumentos do evento através de campos ou propriedades.

Imports System.ComponentModel 
Public Class User 
    Public Event BeforeLogin As CancelEventHandler 
    Public Event AfterLogin As EventHandler 
    Public Sub Login() 
        Dim e As New CancelEventArgs 
        RaiseEvent BeforeLogin(Me, e) 
        If e.Cancel Then Exit Sub 
        'Código para implementar o Login 
        RaiseEvent AfterLogin(Me, EventArgs.Empty) 
    End Sub 
End Class

Derivando de EventArgs

Quando não existe nenhuma classe derivada de "System.EventArgs" que exponha as propriedades desejadas é necessário criar uma classe personalizada:

Imports System.componentmodel 
    Public Class NameChangingEventArgs 
        Inherits CancelEventArgs 
        Private _ProposedValue As String 
        Public Sub New(ByVal ProposedValue As String) 
            _ProposedValue = ProposedValue 
        End Sub 
        Public ReadOnly Property ProposedValue() As String 
            Get 
                Return _ProposedValue 
            End Get 
    End Property 
End Class

Caso prático

O objectivo é criar um modelo realista do funcionamento de um mecanismo de extracção de bolas para o jogo "EuroMilhões", utilizando as técnicas estudadas para manipulação de objectos e eventos.

A lógica da aplicação é a seguinte:   

  • Cada bola possui um relógio interno que simula o tempo em segundos que a bola demora a passar junto a porta de saída.
  • Cada vez que a bola passar junto a porta, lança um evento indicando que esta pronta a sair.
  • A tômbola possui um mecanismo de abertura da porta, também dependente de um relógio interno.
  • Ao ser iniciado um jogo, a tômbola procede a criação do número de bolas necessário, subscrevendo os seus eventos.
  • Quando for recebido um evento de que uma bola esta pronta a sair, esta deve ser retirada, apenas se a porta estiver aberta.
  • A porta da tômbola deve ser mantida fechada enquanto esta a ser processada uma bola.
  • Quando a tômbola terminar de extrair todas as bolas necessárias, deve emitir um evento indicando que o jogo terminou.

Regras para desenvolvimento

  • Criar um novo projecto com o template "Windows Application"
  • Criar uma classe "bola" com as seguintes propriedades
    • Valor numérico da bola
    • Cor (respeitando as regras do LAB 2)
    • Ciclo (propriedade só de leitura, indicando o ciclo da bola em segundos, ou seja, de quanto em quanto tempo passa pela porta da tômbola)
  • O construtor da classe bola aceita como parâmetro o valor e procede aos seguintes passos:
    • Atribui uma cor (pelas regras já definidas)
    • Define aleatoriamente o ciclo da bola
  • A classe bola deve expor o evento "QueroSair" sempre que atingir o seu ciclo.
  • Criar uma classe "Tômbola" com as seguintes propriedades:
    • Chave (Propriedade só de leitura que contem a chave encontrada, através de um "array" de inteiros. A propriedade não é indexada.)
  • O construtor da classe "Tômbola" deve aceitar como argumentos:
    • A quantidade de bolas necessárias para o jogo
    • A quantidade de bolas a extrair
  • O construtor da classe tômbola deverá ainda realizar os passos:
    • Criar as bolas necessárias, subscrevendo os seus eventos
    • Iniciar o relógio que controla a abertura da porta
  • Sempre que a classe "Tômbola" receber um pedido de saída de uma bola, deverá:
    • Aceitar a saída caso a porta esteja aberta
    • Guardar o valor da bola
    • Destruir a bola (e deixar de receber os seus eventos)
    • Determinar se foram retiradas todas as bolas necessárias, e se sim, destruir as restantes bolas e fazer o "Raise" de um evento "Fimdejogo"
  • No form, criar duas instâncias do objecto "tômbola"
  • Ao receber a notificação de ambas as tômbolas de que o jogo terminou, apresentar a chave no ecrã.

    Solução

       

powered by metaPost

Rating

Comentários
Ainda não existem comentários. Seja o primeiro a colocar um!
Clique aqui para colocar um comentário.
 
 
   
ankaa.net@2009