C# - Como fazer uma classe definir e disparar eventos


Neste artigo veremos como fazer uma classe definir e disparar eventos na linguagem C#.

Vamos começar definindo o que são eventos e como eles se comportam. Os eventos na plataforma .NET são baseados no modelo de delegate.

Um evento é uma mensagem enviada por um objeto para sinalizar a ocorrência de uma ação.

A ação pode ser causada pela interação do usuário, como o clique em um botão, ou pode ser acionada por alguma outra lógica de programa, como a alteração do valor de uma propriedade.

O objeto que aciona o evento é chamado de remetente(sender) do evento. O remetente do evento não sabe qual objeto ou método receberá (handle) os eventos que ele aciona. O evento normalmente é membro do remetente(sender) do evento.

Para definir um evento usamos a palavra-chave event, e, para acionar um evento adicionamos um método que é marcado como protected e virtual; damos a esse método o nome On+NomeEvento. Ex: OnPrinting().

O método deve usar um parâmetro que especifique um objeto de dados de evento. Você fornece esse método para permitir que as classes derivadas substituam a lógica para acionar o evento. Uma classe derivada sempre deve chamar o método OnNomeEvento da classe base a fim de garantir que os delegates registrados recebam o evento.

Definindo e disparando eventos em uma classe

Para mostrar como definir e disparar eventos em uma classe vamos usar como exemplo uma classe Conta que gerencia uma conta bancária.

Se o programa tentar remover um valor maior do que o saldo atual da conta, o objeto Conta vai gerar um evento OnSaldoInsuficiente que incluir dois parâmetros:

  1. Um objeto chamado sender que contém uma referência ao objeto Conta que esta disparando o evento;
  2. Um objeto SaldoInsuficienteArgs;

O objeto SaldoInsuficienteArgs inclui duas propriedades úteis :

  1. A propriedade ValorDebito que indica o valor que esta sendo debitado da conta;
  2. A propriedade Libera que é um valor booleano usado para informar ao objeto Conta para processar o débito mesmo que o saldo fique negativo;

Para poder fazer a implementação do evento e seu tratamento na classe vamos realizar as seguints tarefas:

  1. Criar o formulário usado no projeto;
  2. Definir a classe EventArgs que será usada para transmitir valores ao manipulador de eventos do programa principal;
  3. Definir o evento;
  4. Disparar o evento quando apropriado;
  5. Inscrever-se no evento no programa principal;
  6. Manipular o evento no programa principal;

Com o roteiro definido vamos ver isso funcionando na prática.

Criando o projeto Windows Forms

Vamos criar um vou criar um projeto Windows Desktop usando o template Windows Forms App(.NET Framework)  usando o VS 2017 Community com o nome WF_ClasseEventos;

A partir da ToolBox inclua no formulário Form1.cs os controles:

Este´será o formulário usado no projeto. Bem simples.

Definir a classe SaldoInsuficienteArgs

A classe SaldoInsuficienteArgs deve conter dois campos :  ValorDebito e Libera, e,  deve herdar da classe EventArgs que representa a classe base das classes que contêm dados de evento e fornece um valor a ser usado para eventos que não incluem dados de evento.

using System;
namespace WF_ClasseEventos
{
    public class SaldoInsuficienteArgs : EventArgs
    {
        // O valor a ser debitado
        public decimal ValorDebito;
 
        //Define se libera ou não o debito do valor
        // O valor padrão é false para não permitir saldo negativo
        public bool Libera = false;
    }
}

Esta classe fornece o campo ValorDebito que representa o valor do débito que será sacado da conta. O campo Boolean Libera permite que o programa principal diga ao objeto se ele deve permitir o débito, mesmo que isso faça com que a conta fique com um saldo negativo.

Observe que a classe
SaldoInsuficienteArgs não inclui o saldo atual da conta. O programa principal pode obter isso do próprio objeto Conta.

Agora vamos criar a classe Conta no projeto via menu Project-> Add Class e nesta classe vamos definir:

using System;
namespace WF_ClasseEventos
{
    public class Conta
    {
        public event EventHandler<SaldoInsuficienteArgs> SaldoInsuficiente;
        // O saldo da conta
        public decimal Saldo { get; set; }
        // Retirar uma quantidade da conta
        public void Debito(decimal valor)
        {
            if (valor < 0)
            {
                throw new ArgumentOutOfRangeException("O valor do débito deve ser um valor positivo.");
            }
            // Verifica se existe saldo
            if (Saldo >= valor)
            {
                // Há saldo
                Saldo -= valor;
            }
            else
            {
                // Não tem dinheiro suficiente
                // Dispera o evento SaldoInsuficiente
                if (SaldoInsuficiente != null)
                {
                    // Cria um objeto do tipo SaldoInsuficienteArgs
                    SaldoInsuficienteArgs args = new SaldoInsuficienteArgs();
                    args.ValorDebito = valor;
                    // Dispara o evento somente se houver inscritos
                    SaldoInsuficiente?.Invoke(this, args);
                    // Se for liberado 
                    // debita o valor valor e a conta fica negativa
                    if (args.Libera)
                    {
                        Saldo -= valor;
                    }
                }
            }
        }
        // Deposita valor na conta
        public void Credito(decimal valor)
        {
            if (valor < 0)
            {
                throw new ArgumentOutOfRangeException("O valor do crédito deve ser um valor positivo.");
            }
            Saldo += valor;
        }
    }
}

Na classe conta definimos o evento SaldoInsuficiente usando um delegate. Este evento deve ser manipulado por um manipulador de eventos que usa dois parâmetros:

  1. sender  - representando o objeto que gerou o evento ;
  2. SaldoInsuficenteArgs - define os campos ValorDebito e Libera

 public event EventHandler<SaldoInsuficienteArgs> SaldoInsuficiente;

Aqui estamos usando o delegate EventHandler predefinido para definir o evento.

O código usa também ArgumentOutOfRangeException que é uma exceção que é gerada quando o valor de um argumento está fora do intervalo permitido de valores conforme definido pelo método invocado.

Disparando o Evento

Após definir o evento temos no método Debito o disparo do evento .

Primeiro, o método verifica se o montante a ser debitado é menor que zero e lança uma exceção, se for.

Em seguida, o método verifica o saldo da conta. Se o saldo for grande o suficiente para cobrir o débito, o método subtrai o saldo.

Se o saldo for insuficiente, o método gera o evento SaldoInsuficiente.

A instrução if (SaldoInsuficiente! = Null) verifica se algum código se inscreveu no evento. É uma sintaxe estranha, mas se não houver manipuladores de eventos registrados para o evento, o SaldoInsuficiente será nulo, então o método não precisa disparar o evento.

De fato, se ele tentar levantar o evento, ele receberá uma exceção “Referência de objeto não definida para uma instância de um objeto”.

Se um manipulador de eventos estiver inscrito no evento, o método criará um objeto SaldoInsuficienteArgs e definirá seu ValorDebito. Em seguida, ele usa a sintaxe SaldoInsuficiente(this, args) para disparar o evento, passando o objeto atual e o objeto SaldoInsuficienteArgs para os manipuladores de eventos inscritos.

Quando os manipuladores de eventos retornam, o método verifica o valor Libera do objeto SaldoInsuficienteArgs para ver se ele deve debitar a conta mesmo assim. Se Libera for true, o método subtrai o valor do débito do saldo atual.

Inscrevendo-se no Evento

No formulário Form1 vamos usar o evento Load para criar uma instância da classe Conta, fazer uma inicialização do saldo com um valor igual a 100 , exibir o valor e a seguir fazer a inscrição no evento SaldoInsuficiente:

        // Define obeto do tipo Conta 
        private Conta ContaBancaria;
        private void Form1_Load(object sender, EventArgs e)
        {
            //Cria novo objeto do tipo conta e define o saldo
            ContaBancaria = new Conta();
            ContaBancaria.Saldo = 100m;
            //exibe o valor do saldo
            txtSaldo.Text = ContaBancaria.Saldo.ToString("C");
            // Inscrever-se no evento SaldoInsuficiente
            ContaBancaria.SaldoInsuficiente += Conta_SaldoInsuficiente;
        }    

Fazer o tratamento do evento

Vamos definir o código que será executado quando o objeto Conta dispara o evento SaldoInsuficiente, para isso temos que incluir o código abaixo no método Conta_SaldoInsuficiente:

        // Trata o evento SaldoInsuficinte da conta
        private void Conta_SaldoInsuficiente(object sender, SaldoInsuficienteArgs e)
        {
            // Cria um objeto do tipo Conta
            Conta conta = sender as Conta;
            // Pergunta se permite a transação
            if (MessageBox.Show("Saldo Insuficiente.\n\n    Saldo Atual: " +
                conta.Saldo.ToString("C") + "\n    Valor Débito: " + e.ValorDebito.ToString("C") + "\n\n" +
                "Deseja permitir esta transação ?",
                "Permite ?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                // Permite o debito
                e.Libera = true;
            }
        }

Este método converte o parâmetro do remetente(sender) em um objeto Conta.

Em seguida, o manipulador de eventos exibe uma caixa de mensagem que fornece informações sobre a transação e pergunta ao usuário se deve permitir a transação.

Se o usuário clicar em Sim, o código define o campo Libera do objeto SaldoInsuficienteArgs como true, de modo que o objeto Conta que gerou o evento sabe que deve permitir a transação.

Executando o projeto iremos obter:

Pegue o código do projeto aqui :    WF_ClasseEventos.zip

"Por que estás abatida, ó minha alma, e por que te perturbas dentro de mim? Espera em Deus, pois ainda o louvarei, o qual é a salvação da minha face, e o meu Deus."
Salmos 42:11

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti