Usando Procedimentos e Funções no Visual Basic

A maioria dos procedimentos que você escreveu nos capítulos anteriores foram procedimentos de eventos - isto é, eles foram projetados especialmente para responder a eventos específicos disparados pelo usuário ou pelo sistema. Este capítulo mostra-lhe como escrever e usar procedimentos genéricos (incluindo sub-procedimentos e procedimentos de funções) que executam uma certa tarefa quando são chamados explicitamente por outras instruções do programa (em vez de serem disparados por um evento). Este capítulo explora também o uso de parâmetros, que permite ao programa passar certos valores para os procedimentos e funções que chama. Como você verá, até mesmo objetos do Visual Basic podem ser passados como argumento. Finalmente, este capítulo aborda alguns procedimentos de eventos avançados, como aqueles que lhe permitem rastrear o movimento do mouse e implementar recursos do tipo arrastar e soltar.

Procedimentos Genéricos

As primeiras aplicações que você escreveu tinham um controle de fluxo linear: após uma instrução ser executada, era executada a próxima instrução seqüencial. No capitulo anterior, você começou a trabalhar com desvios condicionais (instruções If) e com laços, que modificam a ordem na qual as instruções são executadas

Da mesma forma que laços, os procedimentos permitem que seu programa execute um conjunto de procedimentos mais de uma vez. Entretanto, em vez de executar repetidamente um conjunto de instruções, os procedimentos permitem que você agrupe instruções num conjunto que possa ser executado quando necessário. Por exemplo, um procedimento de evento do Visual Basic é executado quando o usuário faz uma certa ação; um procedimento genérico é executado quando é chamado por uma instrução do aplicativo.

Considere um programa que simule as ações de uma criança pequena passeando de automóvel. Este código ilustra o comportamento conhecido como

"choramingas" :

Resposta = lnputBox("Ainda não chegamos ?")

lf Resposta = "Não" Then

MsgBox "Estou cansado"

End If

Em um programa que simule o comportamento de crianças, é de se esperar que essas linhas sejam duplicadas um boa quantidade de vezes. Porém, em vez de codificar múltiplas cópias dessas instruções, você pode agrupá-las em um procedimento e executá-lo simplesmente invocando o seu nome. Inclua uma declaração do procedimento, dando-lhe um nome descritivo:

Sub Choro ()

Dim Resposta As String

Resposta = lnputBox("Ainda não chegamos ?")

lf Resposta = "Não" Then

MsgBox "Estou cansado"

End lf

End Sub

Com esse procedimento, os fragmentos dos dois programas que seguem são equivalentes:

Figura 7.1

Fluxo de Controle

Obviamente, os procedimentos afetam o fluxo de controle de execução de um programa. Quando o Visual Basic encontra o nome de um procedimento, ele guarda a posição atual no programa e então se move para executar o procedimento designado. Quando é encontrado o final do procedimento, ele retorna à posição original do programa e continua a execução a partir daquele ponto. Esse processo é conhecido como chamada de procedimento, pois o programa "chama" o procedimento. Como o Visual Basic pode rastrear centenas de níveis de chamadas, um procedimento chamado por um programa pode conter dentro de si próprio uma chamada de procedimento e assim por diante.

Parâmetros

Suponha que você esteja escrevendo um programa que precise, em algum ponto, imprimir os inteiros de 1 a 10. Em outro ponto, o mesmo programa precisa imprimir os inteiros de 1 a 17. A codificação para essas duas rotinas é mostrada a seguir:

 

Primeira Rotina Segunda Rotina

For Contador = 1 To 10 For Contador = 1 To 17

Print Contador Print Contador

Next Contador Next Contador

Como essas rotinas são muito semelhantes, é muito simples generalizar -

isto é, fazer com que o mesmo código sirva para os dois propósitos. A única

diferença entre os dois é o valor final no laço For, que pode ser substituído por uma variável. Depois disso você poderá usar um procedimento único, chamando-o após armazenar na variável o valor final desejado, conforme mostrado a seguir. (Assuma que a instrução Dim ContFinal As Integer aparece na seção de declarações gerais do programa.)

 

Figura 7.2

Como se pode ver, o uso combinado de variáveis e procedimentos permite que você encontre soluções genéricas para os programas mais facilmente, além de colocar à sua disposição excelentes ferramentas de programação. Entretanto, há alguns problemas práticos com a implementação feita anteriormente. Em particular, você precisa declarar variáveis globais (públicas) ou em nível de módulo para fazer a comunicação entre o programa principal e o procedimento que está sendo chamado. Além disso, é preciso que as variáveis locais aos procedimentos tenham nomes diferentes das variáveis globais. Do contrário, a chamada de um procedimento poderá fazer com que o valor de uma variável global seja sobreposto por valores locais ao procedimento.

O Visual Basic resolve essas questões, permitindo que os procedimentos aceitem parâmetros que sejam tratados como variáveis locais ao procedimento, porém inicializados pela codificação que chama o procedimento. A rotina chamadora inicializa essas variáveis com a colocação de um conjunto de valores após o nome do procedimento a ser chamado. Eis a sintaxe para uma tal chamada de procedimento:

name[ value[, value] ... ]

Observe que os argumentos são separados por vírgulas. A rotina chamadora tem de saber os nomes dos parâmetros, pois o assinalamento destes valores às variáveis locais ao procedimento chamado será feito segundo a ordem de especificação dos parâmetros.

Na declaração do procedimento, os parâmetros são declarados dentro dos parênteses que seguem o nome do procedimento. O Visual Basic permite que os procedimentos tenham vários parâmetros. Eis a sintaxe para declaração de procedimento que inclua parâmetros:

Sub name ([parâmetro [As tipo], [parâmetro [As tipo]] ... ])

End Sub

Veja como ficou o exemplo anterior, refeito com o uso de parâmetros:

 Figura 7.3

 

Para ilustrar o uso de múltiplos parâmetros, você poderia modificar o procedimento PrintCont para aceitar tanto o valor inicial quanto o valor final, conforme mostrado a seguir:

 

Figura 7.4

 

Procedimentos de Funções

A partir dos exemplos precedentes, você deve ter reparado que, quando se trata de chamadas de procedimentos genéricos, a comunicação é feita apenas num sentido - isto é, a rotina chamadora passa valores para o procedimento que executa alguma ação, mas não retorna qualquer valor para a rotina chamadora. No entanto, você pode usar um tipo especial de procedimento, chamado procedimento de função (ou simplesmente função), que retorna valor ao chamador. A maioria da funções internas do Visual Basic faz isso.

Ao declarar uma função, você deve declarar o tipo de valor que ela retorna, bem como os parâmetros necessários, como mostra a definição de sintaxe:

 

Figura 7.5

 

Como você pode ver, há duas diferenças entre um procedimento de função e um subprocedimento. Um procedimento de função é delimitado pelas instruções Function e End Function, enquanto um subprocedimento requer Sub e End Sub. E, como mostra a definição sintática, em algum lugar dentro do corpo da função, você deve assinalar um valor (expressão) ao nome da função. Esse é o valor que será retornado para a rotina chamadora. Por exemplo, esta função retorna o maior valor entre dois inteiros:

Function MaxInt (X As Integer, Y As Integer) As Integer

If X > Y Then

MaxInt = X

Else

MaxInt = Y

End If

End Function

A sintaxe para a chamada de um procedimento de função é ligeiramente diferente da sintaxe usada para chamar um subprocedimento. Como a função retorna um valor, ela pode ser chamada em qualquer lugar em que uma expressão seja válida. Os parâmetros da função são, portanto, delimitados por parênteses para separá-los de outras partes de uma expressão. Aqui estão algumas instruções chamando a função MaxInt:

Print MaxInt(7, 14)

DuasVezesMaior = MaxInt(3, 4) * 2

lf MaxInt(PrimeiraOpção, SegundaOpção) > 20 Then

Quando a função a ser chamada não requer parâmetros, basta usar o nome da função, como se fosse uma variável. A função interna Now do Visual Basic (que retorna data e hora atuais) é um exemplo de função que não requer a passagem de parâmetros.

Escrevendo Procedimentos

A discussão que se segue usa o termo procedimento para se referir à idéia geral de rotina que pode ser chamada. Quando necessário, o termo procedimento de função (ou simplesmente função) ou o termo subprocedimento será aplicado para dar maior clareza à exposição.

Para criar um procedimento que possa ser chamado em Visual Basic, você deve abrir a janela de código. A partir do menu Tools, escolha a opção Add Procedure. Na caixa de diálogo Add Procedure, digite o nome do novo procedimento, escolha entre Sub ou Function, dando um clique num dos dois botões de opção. Escolha entre público e privado também clicando em uma das opções.Dê um clique no botão OK. O Visual Basic criará então um novo item na caixa de procedimentos da janela de código, já inserindo a instrução End apropriada para o tipo de procedimento que estiver sendo criado. Agora, você pode incluir as declarações de parâmetros necessárias ao seu procedimento.

Nota: você deve ter notado as opções property e event na caixa de diálogo Add Procedure. Trataremos destas opções quando falarmos sobre Classes e Objetos.

Vamos escrever um programa simples chamado Conversão de Temperatura, para executar uma conversão simples de temperatura entre as escalas Celsius e Fahrenheit. Abra um novo projeto e coloque uma barra de deslocamento vertical, seis rótulos e duas caixas de texto no form, dispondo-os como mostrado na Figura 7.7. Configure as propriedades dos objetos de acordo com a tabela mostrada na Figura 7.6.

O programa permitirá ao usuário ajustar a barra de deslocamento para qualquer configuração de temperatura entre os pontos rotulados como -40 e 212 na escala Fahrenheit ou entre as temperaturas correspondentes -40 e 100 na escala Celsius. O aplicativo então exibirá o valor da temperatura para a configuração escolhida nas duas escalas, Fahrenheit e Celsius. Repare que a configuração da propriedade Min para a barra de deslocamento é maior do que a configuração para Max. Essa forma de configuração faz com que a barra de deslocamento coloque o extremo superior de sua faixa de variação no topo, ao contrário do que acontece quando a configuração da propriedade Min é menor do que a da propriedade Max .

Figura 7.6 O projeto inicial do form, para o aplicativo Conversão de Temperatura.

Figura 7.7 Configuração de propriedades para o aplicativo Conversão de Temperatura.

Por questões de simplicidade, o programa sempre "pensará" em termos de graus Celsius, fazendo a conversão para obter a temperatura em Fahrenheit. Em primeiro lugar, você deve escrever a função que retorna a temperatura Fahrenheit quando o programa fornecer a temperatura em graus Celsius. Comece abrindo a janela de código do form. A partir do menu Tools, escolha a opção Add Procedure. Na caixa de diálogo, digite CParaF, escolha o botão de opção Function e dê um clique em OK. Então, edite a nova função como segue (inicie adicionando as declarações do parâmetro e do tipo de valor a ser retornado na instrução Function):

Function CParaF (TempC As Integer) As Integer

CParaF = Cint(TempC * 9 / 5 + 32)

End Function

A função CParaF aplica a fórmula padrão de conversão métrica. Recebendo a temperatura em graus Celsius como parâmetro, ela retorna a temperatura equivalente na escala Fahrenheit. Para simplificar a saída e o uso da barra de deslocamento, os valores estão restritos a inteiros. (A função CInt converte valor em ponto flutuante para inteiro, por arredondamento.)

Agora, você pode escrever o código que lê o valor da barra de deslocamento e exibe os valores das temperaturas. Introduza o seguinte subprocedimento abaixo da função:

Sub ExibeTemp ()

Dim TempC As Integer

TempC = Clnt(VscTermômetro.Value)

TxtGrausC.Text = Str$(TempC)

TxtGrausF.Text = Str$(CParaF(TempC))

End Sub

Após introduzir a primeira linha (a instrução Sub), repare que o Visual Basic adiciona o nome do procedimento à caixa de procedimentos e inclui a instrução End Sub no código que você está escrevendo. Como você pode ver, é possível criar novos procedimentos, tanto por meio da opção Add Procedure do menu Tools quanto da introdução da instrução Function ou Sub em qualquer lugar da janela de código.

O código para o procedimento ExibeTemp lê o valor da barra de deslocamento, VscTermômetro.Value, armazenando-o na variável TempC. O valor é exibido na caixa de texto TxtGrausC. Então, a caixa de texto TxtGrausF é configurada com o resultado gerado pela função de conversão CParaR

Finalmente, selecione o objeto VscTermômetro (a barra de deslocamento) na caixa de objetos da janela de código e edite seus procedimentos de evento como mostrado aqui:

Private Sub VscTermômetro_Change ()

ExibeTemp

End Sub

Private Sub VscTermômetro_Scroll ()

ExibeTemp

End Sub

Com isso, toda vez que o usuário alterar a barra de deslocamento, o procedimento ExibeTemp fará seu trabalho. Processe o aplicativo para ver se ele funciona como esperado. O resultado dever parecer-se com o exemplo mostrado na Figura 7.8.

Figura 7.8 Processando o aplicativo Conversão de Temperatura.

Pensando no Futuro

Pode parecer que você fez alguns trabalhos desnecessários ao escrever o código para o aplicativo Conversão de Temperatura. No final das contas, se cada procedimento é usado apenas uma única vez, por que não fazer todos os cálculos em uma linha só e agrupar todas as instruções no procedimento de evento Change da barra de deslocamento? O código do programa resultante deve parecer-se com o mostrado a seguir:

Sub VscTermômetro_Change ()

Dim TempC As Integer

TempC = CInt(VscTermômetro.Value)

TxtGrausC.Text = Str$(TempC)

TxtGrausF.Text = Str$(Clnt(TempC * 9 / 5 + 32))

End Sub

De fato, esse programa é perfeitamente aceitável, e trabalha tão bem quanto a primeira versão. Porém, o programa original de Conversão de Temperatura é melhor pelos seguintes motivos:

Nomes adicionam clareza e ajudam a explicar o que está acontecendo no programa.

Como uma ilustração, considere uma melhoria do programa. Na versão atual, é preciso usar uma barra de deslocamento para configurar uma certa temperatura. Porém, poderia ser de grande ajuda permitir ao usuário introduzir diretamente o valor da temperatura e então levar o programa a fazer a conversão. Usando os blocos de construção que você criou, será relativamente simples fazer isso. Caso você tivesse escrito o programa como uma peça única, esse tipo de modificação poderia requerer duplicação ou até mesmo reescrever o código.

Generalizando

As melhores ferramentas são as que podem ser usadas em mais de um serviço. Com freqüência, você pode facilmente converter poucas linhas de um código especifico, proveitoso apenas num certo contexto, em uma rotina genérica. Considere o código que você escreveu no Capitulo 6 para pesquisar um determinado nome de doninha; a versão completa é mostrada na Figura 7.9. A Figura 7.10 apresenta uma versão que usa um procedimento genérico derivado do código original. Essa segunda versão oferece uma rotina que encontrará qualquer doninha pelo nome, podendo ser útil várias vezes no programa.

Figura 7.9 Código do programa que pesquisa uma doninha pelo nome.

 

Figura 7.10 Código do programa contendo um procedimento genérico que pode pesquisar qualquer doninha.

Reciclando Código

Hoje você está escrevendo programas para acompanhar doninhas. Amanhã poderá tratar-se de micos-leões. E quando você estiver escrevendo seus programas para micos, será muito prático poder dizer: "Ei, algumas semanas atrás, eu escrevi alguma coisa semelhante a isso". Se você escreve rotinas genéricas, é possível simplesmente recuperar o código de um programa velho e usá-lo no novo programa. Esse código já foi depurado, há menor quantidade de trabalho a realizar e seu projeto será completado mais rapidamente. O que mais você poderia desejar?

Ainda que seja ótimo reutilizar um código desenvolvido por você mesmo, normalmente é antiético (e com freqüência ilegal) copiar diretamente um código escrito por outra pessoa. No entanto, você está liberado para copiar o código dos programas de exemplo que acompanham o Visual Basic; a Microsoft garante a permissão para cópia dos códigos.

Como Trabalham os Parâmetros

Até agora, você usou parâmetros apenas para passar argumentos para os procedimentos. Porém, os parâmetros são muito mais flexíveis do que isso. Vamos dar uma olhada nos dois mecanismos que se encontram disponíveis no Visual Basic para passagem de argumentos.

Passagem por Referência

Quando os parâmetros foram introduzidos pela primeira vez, eu os descrevi como se fossem variáveis locais. Na verdade, nem sempre é esse o caso. Considere o subprocedimento Increment que adiciona 1 ao valor do parâmetro, X:

Sub Increment (X As Integer)

X = X + 1

End Sub

Caso X seja uma variável local, esse procedimento será inútil. O valor do parâmetro da rotina chamadora seria assinalado a X, e o procedimento incrementaria esse valor e em seguida terminaria sua execução destruindo efetivamente as variáveis locais. Na realidade, X é uma referência ao valor original da rotina chamadora, e qualquer modificação em X altera o valor original. Se o código mostrado a seguir chamasse o procedimento Increment, seria impresso o valor 4:

Dim A As Integer

A = 3

Increment A

Print A

Uma vez que o parâmetro X não passa de uma referência ao valor de uma variável, não será alocada memória no subprocedimento Increment para armazenar esse valor. O Visual Basic apenas armazena a informação de que X é uma referência. O uso de referência permite ao Visual Basic operar mais eficientemente. Essa eficiência não é muito representativa quando se trata de variáveis inteiras. No entanto, considere o procedimento DeLado, que imprime seu parâmetro string entre parênteses:

Sub DeLado (Str As String)

Print "("

Print Str

Print ")"

End Sub

Se os parâmetros fossem realmente variáveis locais, o Visual Basic teria de alocar espaço para cada string passada para o procedimento, e copiar a string a cada chamada. Como as strings podem conter centenas de caracteres, seria muito mais simples criar uma referência à string original.

Passagem por Valor

Você também pode fazer com que os parâmetros trabalhem de outra forma - isto é, como variáveis locais. Se você iniciar a declaração do parâmetro com a palavra-chave ByVal, o Visual Basic alocará memória local para o parâmetro e copiará o valor do argumento correspondente para o parâmetro. Eis um exemplo desse tipo de declaração:

Sub Increment (ByVal X As Integer)

X = X + 1

End Sub

A passagem por referência geralmente é mais eficiente, porém a passagem por valor dá origem a variáveis locais que algumas vezes são necessárias nos seus programas. Por exemplo, vejamos a função que calcula X elevado à potência Y, isto é, X ^ Y. Esse cálculo usa multiplicações repetidas (por exemplo, X ^ 3 é igual a X . X . X). Por questões de simplicidade, essa função ignorará valores negativos para o expoente. O parâmetro Y especifica a quantidade de multiplicações necessárias. O valor de Y é decrementado após cada multiplicação, e o cálculo termina quando Y chega a 0.

Function Potência (X As Single, _

ByVal Y As Integer) As Single

Dim Result As Single

Result = 1

Do While Y > 0

Result = Result * X

Y=Y-1

Loop

Potência = Result

End Function

Uma vez que você deseja modificar Y na função, porém não quer que seja alterado o valor original da variável que foi passada, deve declarar Y com a palavra-chave ByVal. Examine o seguinte fragmento de código, que chama a função Potência:

'Aproximar E elevado a N, usando as séries de Taylor

Result = 1

For Aprox = 1 to 10

Result = Result + Potência(N, Aprox) / Factorial(Aprox)

Next Aprox

O programador espera que essa rotina faça o laço 10 vezes enquanto estiver fazendo o seu cálculo. Entretanto, se a função Potência que ela está chamando não usar a palavra-chave ByVal, essa rotina jamais sairá do laço, entrando em looping infinito. Após a função Potência ser chamada pela primeira vez, Aprox seria zerada (pois a variável local Y iria tornar-se uma referência a Aprox). A função Factorial seria chamada com o valor errado e então a instrução For incrementaria Aprox e iniciaria o laço novamente. Como Potência continua a zerar a variável Aprox, o laço For jamais terminaria.

Uma boa regra prática é que qualquer declaração de parâmetro para o tipo Integer, Long ou Single deveria ser uma declaração ByVal (isto é, o parâmetro deveria ser passado por valor), a menos que a rotina chamadora espere que o procedimento modifique o valor que está sendo passado. Por outro lado, strings e matrizes, por questões de eficiência, deverão ser passadas por referência. Controles e tipos de dados definidos pelo usuário (tal como DoninhaTFB) podem ser passados apenas por referência.

Para outros tipos de dados como double, currency e variant, a decisão não é tão simples. A boa prática de programação sugere que você passe esses parâmetros por valor, pois dessa forma estará menos sujeito a usar uma variável parâmetro incorretamente. Entretanto, é mais eficiente passar esses parâmetros por referência.

Meu conselho é considerar em primeiro lugar os bons princípios de programação. Passar inicialmente todas as variáveis (exceto strings, matrizes e tipos definidos pelo usuário) por valor. Após completar a construção de seu programa e depois de testá-lo, você poderá investigar como melhorar o seu desempenho. Então, se o seu programa passar a operar incorretamente, após a remoção de algumas palavras-chave ByVal, você saberá onde olhar para encontrar os problemas.

Passagem de Parâmetros de Matriz

Você pode declarar um parâmetro de matriz como faz com qualquer outro parâmetro, omitindo da declaração a dimensão da matriz. Por exemplo, esta função calcula a média dos elementos de qualquer matriz de valores com dupla precisão.

Function Média (DMatriz( ) Double, _
ByVal Dcont As Integer) As Double

Dim Total As Double, Ix As Integer

Total = 0

For lx = 0 To Dcont - 1

Total = Total + DMatriz(lx)

Next Ix

If Dcont = 0 Then

Média = 0

Else

Média = Total / Dcont

End If

End Function

O parâmetro DMatriz é declarado como uma matriz do tipo Double, porém a sua dimensão não é declarada. Esse código é um outro exemplo de uma solução generalizada. Como a dimensão não é declarada, essa mesma função pode ser chamada com argumentos que sejam matrizes de qualquer dimensão. (Basta que os elementos sejam do tipo Double.)

Para passar uma matriz como argumento para um procedimento, use apenas o nome da variável matriz seguido de um par de parênteses vazio. Este fragmento de código mostra como o procedimento Média pode ser usado:

Dim MeusDados(100) As Double, ltensLidos As Integer

ItensLidos = CargaDados(MeusDados( ), 100)

Print "A média dos valores é : " & _

Média(MeusDados( ), ltensLidos)

MeusDados é declarado como uma matriz com dimensão 100. O procedimento CargaDados (não mostrado aqui) é chamado, sendo passadas como parâmetros a matriz MeusDados e a dimensão máxima da matriz.

Provavelmente, a rotina CargaDados preenche o maior número de elementos da matriz possível, retornando como valor a quantidade real de itens preenchidos. A seguir, o programa imprime a média. A chamada à Média retorna a matriz e a quantidade de elementos que foram preenchidos por CargaDados.

Naturalmente, quando necessário, você pode passar elementos individuais de matrizes. Basta indexar a matriz para indicar o elemento a ser passado, como nesta linha de código:

Print "A raiz quadrada do item 3 é " & Sqr(MeusDados(3))

Você pode modificar o aplicativo Banco do Capitulo 6 para usar a função

Média, como mostrado na Figura 7.11. Cada vez que você der um clique no botão Média, o programa redimensionará a matriz dinâmica ListaValor para acomodar a quantidade de itens na caixa de lista. Como a caixa de lista contém uma matriz de strings; em vez de números, cada elemento deve ser convertido à medida que for copiado para ListaValor. Então, o programa passa a matriz como argumento para a função Média.

Figura 7.11 Uma modificação no aplicativo Banco.

Esse programa modificado é um pouco mais longo do que o aplicativo original. Porém, você criou uma nova ferramenta - a função Média - que pode ser usada em vários lugares. Por exemplo, para ampliar o programa Banco para trabalhar com mais de uma conta, você pode chamar a função Média para cada conta, em vez de escrever um laço separado para cada uma. Se você tiver de escrever um programa que calcule a média pluviométrica, pode carregar o programa Banco, copiar a função Média e colocá-la no novo programa.

Alocação Local

Em função da discussão sobre escopo feita no Capitulo 3, você já está familiarizado com variáveis locais. Agora que você está trabalhando com parâmetros similares às variáveis locais, será útil verificar a maneira pela qual o Visual Basic gerencia a memória usada por variáveis locais.

Quando você inicia um programa, o Visual Basic sabe quantas variáveis globais existem e aloca memória para elas. No entanto, ele não sabe quantas variáveis locais existem ou quando cada procedimento do programa será chamado. Variáveis locais e parâmetros são criados quando o procedimento que os contém é chamado, e são destruídos quando o procedimento acaba. Se um procedimento for chamado novamente, as variáveis são recriadas. Esse processo não apenas retarda a alocação de memória até que ela seja necessária, mas permite que a memória seja reutilizada por variáveis que pertençam a procedimentos diferentes. Os computadores modernos executam essa alocação temporária e a liberação muito eficientemente.

No entanto, algumas vezes você pode querer que o valor armazenado em uma variável local não seja perdido quando o procedimento que a contém for encerrado. Você poderia declarar uma variável em nível de módulo ou pública, mas essa solução não seria ideal se a variável viesse a ser usada apenas em um procedimento.

O Visual Basic oferece a palavra-chave Static para tratar dessa questão. Se você usa Static em vez de Dim dentro de um procedimento, as variáveis declaradas dessa forma terão escopo local, visível apenas dentro daquele procedimento; no entanto, serão permanentes, como se fossem declaradas em nível de módulo. O Visual Basic inicializa todas as variáveis Static como 0, tornando-as úteis para situações como a que se segue:

Sub Command1_Click ()

Static Contador As Integer

Contador = Contador + 1

MsgBox "Este botão já foi pressionado" & _

Str$(Contador) & "vezes."

End Sub

Abra um novo form, inclua um botão de comando simples com o procedimento acima e tente executar o programa. Então, mude a palavra-chave Static para Dim para verificar que, no caso de uma variável puramente local, o programa não funciona.

Objetos Do Visual Basic como Parâmetros

Os procedimentos que você escreveu até agora têm passado apenas valores numéricos, matrizes e strings como parâmetros. O Visual Basic também permite-lhe passar controles e forms. A sintaxe é similar: basta usar a palavra-chave Control ou Form, em vez de String, Integer, e assim por diante, na declaração do procedimento.

Essa característica lhe dá a habilidade de escrever código totalmente genérico. Por exemplo, suponha que você esteja escrevendo um aplicativo com uma certa quantidade de botões na tela. Em vários pontos do aplicativo, você deseja dar atenção especial a um botão particular, colocando a característica itálica em seu título e configurando a sua cor de fundo para vermelho. O seguinte subprocedimento faz isso:

Sub Atenção (Botão As Control)

Botão.FontItalic = True

Botão.BackColor = vbRed

End Sub

Você passa um objeto como um parâmetro usando o seu nome. Você poderia passar para esse subprocedimento um botão chamado btnNext, com a instrução Atenção btnNext.

Similarmente, você pode declarar um parâmetro como sendo um form. Você pode configurar ou examinar as propriedades do form e chamar os seus métodos. Pode passar o form pelo nome ou usar a variável interna Me (eu), que é o form ativo no momento.

O Aplicativo Evasivo

Vamos desenvolver um aplicativo, chamado Evasivo que faz uma brincadeira com o usuário. Ele exibirá uma mensagem de erro, como se tivesse ocorrido uma falha durante sua inicialização. O usuário verá uma caixa de diálogo com dois botões de comando, Cancel e Retry. Entretanto, o que acontece quando o usuário dá um clique em um desses botões não é habitual. O botão caminhará para um ponto qualquer do form, e o usuário jamais será capaz de cancelar a execução do programa dando um clique num dos botões. Esse aplicativo ilustra como você pode passar objetos como parâmetros e como usar o método Move - um método que pode ser implementado para a maioria dos controles do Visual Basic.

Cada botão de comando deverá ter o mesmo comportamento. De certa forma, você pode assegurar isso, chamando o mesmo procedimento para cada um dos botões. No entanto, uma das ações do procedimento será mover o botão para uma nova posição. Essa ação requer uma chamada ao método Move dos botões, exigindo que você passe o botão como um parâmetro.

Crie um novo projeto e arranje dois campos de rótulo e dois botões de comando no form como mostrado na Figura 7.12. A Figura 7.13 lista as configurações de propriedades que serão necessárias.

Figura 7.12 O projeto inicial do form para o aplicativo Evasivo.

Figura 7.13 Configuração de propriedades para o aplicativo Evasivo.

 

 A Figura 7.14 contém o código do programa para o aplicativo Evasivo, e a

Figura 7.15 mostra como o aplicativo ficará quando o usuário processá-lo.

 

Figura 7.14 Código de programa para o aplicativo Evasivo.

Figura 7.15 Processando o aplicativo Evasivo.

O procedimento Salto usado nesse aplicativo pode ajustar a posição não apenas dos dois botões de comando, mas de quase qualquer tipo de controle que lhe seja passado como parâmetro, pois a maioria dos controles possui o método Move. Entretanto, algumas vezes você pode querer executar tipos de processamento especiais, que sejam específicos a certos tipos de objetos. O Visual Basic oferece a instrução If TypeOf para determinar o tipo de controle do parâmetro. Ela funciona da mesma maneira que a instrução If convencional; apenas a primeira linha de sua sintaxe é diferente.

If TypeOf objeto Is TipoObjeto Then

Se você quisesse que o procedimento Salto distinguisse botões de opção de outros tipos de controles, a primeira linha da instrução If TypeOf deveria ser

semelhante a isto:

If TypeOf CtI Is OptionButton Then

Procedimentos de Eventos Avançados

Esta seção examina procedimentos de eventos do Visual Basic mais complexos do que os procedimentos Click e DblClick. Essas técnicas permitem-lhe construir aplicativos com tratamento mais sofisticado para teclado e mouse, e implementar uma técnica gráfica chamada arrastar e soltar (drag and drop).

Eventos de Mouse

Nossos exemplos anteriores levaram em consideração o mouse apenas quando usado para dar um clique em um objeto. O máximo que você fez foi escrever um código para os procedimentos de evento Click ou DblClick. No entanto, certos tipos de aplicativos requerem que as atividades do mouse sejam acompanhadas mais de perto.

Um programa de desenho ou pintura, por exemplo, precisa saber a posição do mouse quando você está arrastando um gráfico ou criando uma nova imagem. Um jogo pode usar a posição do mouse para controlar alguns de seus aspectos. Porém, o Visual Basic não contém um objeto mouse. Em vez disso, o evento MouseMove é disparado quando a posição do mouse se modifica. Com freqüência, os eventos de mouse são associados com forms e caixas de imagem, porém eles podem ser usados também com os outros objetos. Com propósitos de ilustração esta seção irá concentrar-se apenas nos forms.

Cada form possui estes três procedimentos padrões:

Figura 7.16

O Visual Basic chama esses procedimentos quando o usuário interage com

o mouse. O evento MouseDown ocorre sempre que um botão do mouse é pressionado. O evento MouseUp é sinalizado quando o botão do mouse (que se encontrava pressionado) é liberado. O evento MouseMove ocorre quando a posição do mouse é alterada. A Figura 7.17 descreve os parâmetros que são passados para esses procedimentos de eventos.

 

Figura 7.17 Parâmetros dos procedimentos de eventos do mouse.

 

A melhor forma de ilustrar estes eventos é a criação de um aplicativo de exemplo. Criaremos aqui o aplicativo Desenho de Linha. Esse aplicativo usa o método Line, um método gráfico para desenho de um form. (Discutiremos o método Line em maiores detalhes no Capítulo 11.)

 

Figura 7.18 Código de programa para o aplicativo Desenho de Linha.

 

Quando você processar o aplicativo, a variável Desenhando será inicialmente configurada como False. Porém, quando o botão esquerdo do mouse for pressionado, a variável será configurada para True. Quando o botão for liberado, Desenhando passará a ser False novamente. À medida que você mover o mouse, mantendo o botão pressionado, o programa traçará a linha a partir da última posição conhecida (CurrentX, CurrentY), até a posição atual do mouse. Se você mantiver a tecla Shift pressionada enquanto traça uma linha, a sua cor será alterada para vermelho até você soltar a tecla Shift.

Nota: Mouses com um único botão têm esse botão definido como left-button (botão esquerdo). Se o seu mouse tiver dois botões e você alterou o Painel de Controle do Windows para inverter os botões esquerdo e direito do mouse, o Visual Basic passará o valor vbLeftButton quando for pressionado o botão da direita e vice-versa.

Eventos de Teclado

O Capitulo 6 introduziu o evento KeyPress, que é disparado para certos objetos quando o usuário está digitando. O Visual Basic permite um grau de controle ainda mais refinado com os eventos KeyUp e KeyDown, que são disparados quando o usuário pressiona e solta uma tecla. Apenas aplicativos muito específicos requererão o uso desses eventos, entretanto a ajuda do Visual Basic contém informações completas a esse respeito.

Os forms possuem outra característica muito útil relacionada ao teclado. Normalmente, o pressionamento de teclas é enviado diretamente para o objeto (por exemplo, uma caixa de texto) que está ativo no momento. Entretanto, configurando a propriedade KeyPreview de um form para True, você fará com que os procedimentos de eventos KeyPress, KeyDown e KeyUp do form sejam disparados em primeiro lugar. Esses procedimentos podem filtrar a informação a ser passada para os procedimentos de eventos KeyPress, KeyDown e KeyUp dos objetos.

Por exemplo, para registrar cada tecla pressionada pelo usuário, você poderia definir a seguinte função (suponha que SalvarTudo seja declarada como uma variável string em nível do form):

Sub Form_KeyPress (KeyAscii As Integer)

SalvarTudo = SalvarTudo & Chr$(KeyAscii)

End Sub

Caso a propriedade KeyPreview do Form seja configurada para True e o procedimento de tratamento de tecla em nível do form vier a modificar a variável KeyAscii, o procedimento de evento KeyPress do objeto atualmente ativo receberá a tecla modificada. Se o procedimento em nível do form configurar KeyAscii para 0, o procedimento KeyPress do objeto não será chamado.

Arrastar e Soltar

O Visual Basic fornece também um suporte especial para outra forma de interação com usuários; chamado arrastar e soltar. As palavras arrastar e soltar referem-se ao uso do mouse para deslocar um objeto exibido para outro lugar. Você deve ter visto esse processo no Explorer, que lhe permite mover arquivos de uma pasta para outra, arrastando-se os ícones dos arquivos.

Você permite que um objeto do Visual Basic (normalmente uma caixa de imagem ou controle de imagem) seja arrastado configurando a sua propriedade DragMode para 1. Quando DragMode é configurada para 1, o controle não recebe mais o evento Click ou MouseDown. Em vez disso, o objeto se movimenta quando o usuário dá um clique nele, começando a arrastá-lo. O item que está sendo arrastado é chamado de objeto-fonte, e o item que recebe o evento DragDrop é chamado de destino. Quando o usuário solta o objeto-fonte (soltando o botão do mouse), o Visual Basic envia o evento DragDrop para o objeto de destino. Adicionalmente, eventos DragOver são enviados para quaisquer objetos sobre os quais passar o objeto-fonte.

O mecanismo arrastar e soltar permite-lhe projetar uma interação simples e prática com os usuários, na qual podem ser executadas várias tarefas, sem o uso de comandos, menus ou botões. Esse método é muito intuitivo em diversas situações e freqüentemente mais veloz do que outros métodos.

Para demonstrar o mecanismo arrastar e soltar, vamos escrever um aplicativo chamado Correio Eletrônico, que simulará o ambiente de trabalho para a troca eletrônica de mensagens. Ele permitirá que você crie novas mensagens e arraste-as para uma caixa postal, a partir da qual elas poderão ser distribuídas para a rede. (Será apresentado aqui apenas o mecanismo arrastar e soltar. O código completo para um sistema de mensagens eletrônicas ocuparia o livro inteiro.)

Inicie pela criação de um novo projeto. Inclua dois botões de comando, sete pequenos controles de imagem, conforme mostrado na Figura 7.19. Os três controles de imagem na parte inferior do form armazenarão cópias mestras de ícones, a exemplo do que foi feito com alguns controles de imagens no aplicativo Caça-níqueis do Capítulo 6. Após inicializar esses controles, você pode redimensionar o form para eliminá-los da área de visão. Os ícones usados neste aplicativo podem ser encontrados no diretório de ícones do CD deste pacote.

Configure as propriedades dos três controles de imagens inferiores (Image5, Image6 e Image7), como listado na Figura 7.20. A propriedade Tag é ignorada pelo Visual Basic; você pode usá-la para armazenar qualquer texto identificador que desejar. Esse aplicativo usa a propriedade Tag para distinguir ícones de cartas dos outros tipos de ícones.

Figura 7.19 O projeto inicial do form para o aplicativo Correio Eletrônico

Figura 7.20 Configuração de propriedades para os três controles de imagens inferiores no aplicativo Correio Eletrônico.

Configurando-se a propriedade DragMode do objeto Image7 (ícone de carta) para 1, possibilita-se ao usuário arrastar as cartas ao longo da janela. A configuração da propriedade Index para 0 transforma o ícone de carta em uma matriz de controles, contendo até agora apenas um elemento. (Dentro de instantes você verá como o código do programa usará essa matriz.) A Figura 7.21 mostra-lhe como a linha inferior de controle de imagens deve ficar após essa configuração de propriedades.

 

Figura 7.21 Os três ícones inferiores usados no aplicativo Correio Eletrônico.

 

Agora, configure as propriedades dos outros objetos, conforme indicado pela Figura 7.22. Após modificar as dimensões do form para que as três imagens inferiores não apareçam, o seu form deverá parecer-se com o mostrado na Figura 7.23. Os controles de imagens que aparecem na parte superior da janela servirão como caixas postais, para as quais os ícones de cartas podem ser arrastados. Os ícones que você vê sobre a "mesa de trabalho" (a caneta, o telefone e o clipe) não podem ser distribuídos pelo correio. A caixa postal exibirá o ícone de caixa vazia (aquele de Image5) quando estiver vazia, e o ícone de caixa cheia (o de lmage6) quando o usuário arrastar uma carta para a caixa postal. Os botões de comando permitirão ao usuário criar novas cartas (com o ícone de carta) e limpar a caixa postal.

Figura 7.22 Configuração de propriedades restantes usadas no aplicativo Correio Eletrônico.

Figura 7.23 O projeto final do form para o aplicativo Correio Eletrônico.

O código para esse aplicativo requer a seguinte declaração:

Dim ProxCarta As Integer

Dois procedimentos são necessários em nível do form. O procedimento Form_Load inicializa o ícone da caixa postal e a variável ProxCarta; o procedimento Form_DragDrop move o controle para o ponto que o usuário indicar:

Sub Form_Load ( )

ImgCorreio.Picture = ImgCaixaVazia.Picture

ProxCarta = 1

End Sub

Sub Form_DragDrop (Source As Control, X As Single, Y As Single)

Source.Move X, Y

End Sub

A janela básica mostra apenas três ícones. Você deve escrever a codificação para o botão MsgNova, para permitir ao usuário criar novos objetos-cartas.

Sub btnNova_Click ( )

Load lmgCarta(ProxCarta)

lmgCarta(ProxCarta).Left = btnNova.Left - 2000 + ProxCarta * 100

lmgCarta(ProxCarta).Top = ScaleHeight - 500

lmgCarta(ProxCarta).Visible = True

ProxCarta = ProxCarta + 1

End Sub

O primeiro programa chama o procedimento Load. Como lmgCarta é uma matriz de controles, Load cria um novo controle, dando-lhe o índice especificado por ProxCarta. Após o Visual Basic criar o novo membro da matriz de controles (ImgCarta), esse novo membro recebe uma posição na janela e torna-se visível. Então, a variável ProxCarta é incrementada de forma que o próximo evento Click crie uma nova carta.

O botão Limpar Caixa simplesmente reconfigura o ícone (a propriedade Picture) da caixa postal para o valor default:

Sub btnLimp_Click ( )

lmgCorreio.Picture = ImgCaixaVazia.Picture

End Sub

A caixa postal requer o seu próprio procedimento arrastar e soltar. Quando um controle é arrastado para a caixa, o programa verifica a propriedade Tag do objeto-fonte. Caso não seja uma carta, o procedimento provoca o seu encerramento, não permitindo que o objeto seja colocado sobre a caixa postal. Do contrário, o procedimento Unload remove o ícone da carta da tela e faz com que a propriedade Picture da caixa postal seja configurada para o ícone de ImgCaixaCheia.

Sub ImgCorreio_DragDrop (Source As Control, X As Single, Y As Single)

If Source.Tag <> "Carta" Then

Beep

Exit Sub

End If

Unload Source

lmgCorreio.Picture = lmgCaixaCheia.Picture

End Sub

Processe esse aplicativo e tente dar cliques no botão MsgNova algumas vezes, para criar novas cartas. Excluindo a caixa postal, todas as imagens que estão na janela do aplicativo podem ser arrastadas. Arrastar uma carta para a caixa postal modifica o ícone da caixa postal, e dar um clique no botão Limpar Caixa restaura o ícone da caixa postal. Note que o programa não permitirá que você arraste o telefone, a caneta ou o clipe para a caixa postal.

O programa usa a variável ProxCarta para especificar um valor continuamente crescente para os elementos subseqüentes da matriz de controles ImgCarta.

 

Use RabJump e adicione código automático para GotFocus, LostFocus, KeyPress, KeyDown