Word 2000 e 97
Segredos e Soluções
ASSOCIAÇÃO BRASILEIRA DE DIREITOS REPROGRÁFICOS
Preencha a ficha de cadastro no final deste livro e receba gratuitamente informações sobre os lançamentos e as promoções da Editora Campus. Consulte também nosso catálogo completo e últimos lançamentos em www.campus.com.br
CARLOS MACHADO
Word 2000 e 97
Segredos e Soluções
© 2000, Editora Campus Ltda. Todos os direitos reservados e protegidos pela Lei 5.988 de 14/12/73. Nenhuma parte deste livro, sem autorização prévia por escrito da editora, poderá ser reproduzida ou transmitida sejam quais forem os meios empregados: eletrônicos, mecânicos, fotográficos, gravação ou quaisquer outros.
Capa Simone Villas Boas Editoração Eletrônica RioTexto Copidesque Maria Thereza R. Corrêa de Araújo Revisão Gráfica Eliane de Souza Cláudia Gomes de Amorim Projeto Gráfico Editora Campus Ltda. A Qualidade da Informação. Rua Sete de Setembro, 111 – 16º andar 20050-002 Rio de Janeiro RJ Brasil Telefone: (21) 509-5340 FAX (21) 507-1991 E-mail:
[email protected] ISBN 85-352-0591-8 CIP-Brasil. Catalogação-na-fonte. Sindicato Nacional dos Editores de Livros, RJ M13w Machado, Carlos Word 2000 e 97 : segredos e soluções / Carlos Machado. – Rio de Janeiro : Campus, 2000 : + CD-ROM ISBN 85-352-0591-8 1. Microsoft Word (Programa de computador). 2. Visual Basic (Linguagem de programação de computador). I. Título 00-0486
CDD – 005.3 CDU – 004.42
00 01 02 03
5
4
3
2
1
Todos os esforços foram feitos para assegurar a precisão absoluta das informações apresentadas nesta publicação. A editora responsável pela publicação original, a Editora Campus e o(s) autor(es) deste livro se isentam de qualquer tipo de garantia (explícita ou não), incluindo, sem limitação, garantias implícitas de comercialização e de adequação a determinadas finalidades, com relação ao código-fonte e/ou às técnicas descritos neste livro, bem como ao CD que o acompanha. A Editora Campus e o(s) autor(es) não se responsabilizam por problemas relacionados à funcionalidade do código-fonte para datas a partir de 01/01/2000.
DEDICATÓRIA Para Maria José, minha mulher, pelo apoio de sempre.
AGRADECIMENTOS Ao analista de sistemas Antonio Augusto Ferreira, que, a meu pedido, desenvolveu a biblioteca Extens32.dll, para escrever valores monetários por extenso. Tenho especial apreço por essa DLL porque sei que é, comprovadamente, uma ferramenta utilíssima no dia-a-dia dos escritórios. A Alexandre Palareti, que talvez não faça idéia do quanto me estimulou ao fazer insistentes cobranças para que eu escrevesse este livro.
O AUTOR Carlos Machado é jornalista especializado em informática, área em que trabalha desde 1988. Envolvido com programação desde os pré-históricos tempos do DOS, dedica especial atenção a soluções que atacam dificuldades do dia-a-dia. Acredita que, mesmo modestas, elas é que mostram ao usuário a verdadeira utilidade da tecnologia. Carlos Machado é formado em jornalismo e também estudou engenharia, de onde trouxe o gosto por temas técnicos. Embora tenha escrito dezenas de soluções na área de programação, publicadas em revistas e na Internet, este é o seu primeiro trabalho na forma de livro. Carlos Machado é editor da revista Info Exame, da Editora Abril.
V
Sumário PARTE 1 INTRODUÇÃO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . IX
1
TRINTA PROJETOS E UM LANCE DE DADOS . . . . . . . . . . . . . . . . . . . 3 Um sumário das soluções práticas desenvolvidas neste livro . . . . . . . . . . . . . . 3 O que é cada projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2
COMO USAR O CD-ROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Instalação e uso dos arquivos de exemplo . . . . . . . . . . . . . . . . . . . . . . 9 Como instalar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Observações importantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3
DICAS, TRUQUES E TOQUES . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Pequenas soluções que tornam seu trabalho com o Word mais rápido e agradável. . . 12 Ao lado do texto, sem truques . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4
FIQUE RICO NA BOLSA DE TEMPO . . . . . . . . . . . . . . . . . . . . . . . 34 Invista um minuto e ganhe milhões (de minutos) Estilos . . . . . . . . . . . . . . . . . . . Modelos . . . . . . . . . . . . . . . . . . Menus e barras de ferramentas . . . . . . . . Formulários. . . . . . . . . . . . . . . . . Macros . . . . . . . . . . . . . . . . . . .
5
em produtividade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
34 35 37 38 42 43
MUITO PRAZER, VBA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 Uma breve apresentação do Visual Basic for Applications Um pouco de história . . . . . . . . . . . . . . . . . VB, VBA e VBScript . . . . . . . . . . . . . . . . . . Com você, o VBA . . . . . . . . . . . . . . . . . . . Partes de um projeto VBA . . . . . . . . . . . . . . . Procedimentos do Visual Basic . . . . . . . . . . . . . Tipos de variáveis . . . . . . . . . . . . . . . . . . . Escopo das variáveis . . . . . . . . . . . . . . . . .
6
. . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
46 47 48 48 50 55 56 59
PARTE 2 O SISTEMA INFORMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Um programa que mostra dados sobre a máquina, o Windows e o Word . . . . . . . 69 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7
QUANTOS ANOS VOCÊ TEM? . . . . . . . . . . . . . . . . . . . . . . . . . 76 Calcule idades com precisão, baseado na data atual fornecida pelo micro . . . . . . 76 Desenho do formulário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
8
DUAS DATAS E UM NÚMERO . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Um programa que calcula datas e prazos simples . . . . . . . . . . . . . . . . . . 80 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9
SEU WORD SALVA ARQUIVOS XMW? . . . . . . . . . . . . . . . . . . . . . . 86 Descubra quais arquivos externos seu Word sabe ler ou escrever . . . . . . . . . . . 86 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10
MUITO ALÉM DO VELHO E CONHECIDO DOC . . . . . . . . . . . . . . . . . 94 Crie um conversor de arquivos do Word para vários outros tipos de documentos . . . 94 A lógica do conversor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
11
NA TABUADA DO 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Crie documentos com numeração seqüencial automática . . . . . . . . . . . . . 104 Memorando numerado interno . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
12
DE UM EM UM, PARA VÁRIOS DOCUMENTOS . . . . . . . . . . . . . . . . . 113 Crie documentos com numeração seqüencial automática . . . . . . . . . . . . . 113 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
13
PALAVRAS QUE VALEM DINHEIRO . . . . . . . . . . . . . . . . . . . . . . . 117 Duas soluções para escrever, automaticamente, valores monetários por extenso . A macro EscreveExtenso . . . . . . . . . . . . . . . . . . . . . . . . . . O aplicativo Valor por Extenso . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
. . . .
. . . .
. . . .
117 118 121 122
. . . .
. . . .
. . . .
. . . .
124 125 127 130
PARA NÃO REPETIR O REPETIDO . . . . . . . . . . . . . . . . . . . . . . . . 132 Ensine o Word a contar e marcar palavras já usadas dentro de um documento. O mecanismo de busca e contagem. . . . . . . . . . . . . . . . . . . . Histórico de busca . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
. . . .
DOCUMENTOS À LA CARTE . . . . . . . . . . . . . . . . . . . . . . . . . . 124 Torne, realmente, automática a emissão de memorandos e outros papéis Construção do formulário . . . . . . . . . . . . . . . . . . . . . . O código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . .
15
. . . .
. . . .
. . . .
. . . .
. . . .
132 134 136 137
TODAS AS FONTES, NA TELA E NO PAPEL . . . . . . . . . . . . . . . . . . . 139 Um aplicativo para visualizar e imprimir as fontes instaladas no sistema . . . . . . . 139 Catálogo de fontes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
17 VIII
UM PROJETO CHEIO DE ESTILO . . . . . . . . . . . . . . . . . . . . . . . . 150 Organize catálogos de todos os estilos disponíveis no Word. . . . . . . . . . . . . 150 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
18
NO PARAÍSO DAS SECRETÁRIAS . . . . . . . . . . . . . . . . . . . . . . . . 155 Um programa que demonstra recursos do VBA aplicados a tabelas do Word . . . . . 155 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
19
O WORD TAMBÉM TOCA MÚSICA! . . . . . . . . . . . . . . . . . . . . . . 164 Uma aplicação para executar arquivos MP3, WAV e MID . . . . . . . . . . . . . . 164 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
20
OS OBJETOS COMANDAM O RITMO . . . . . . . . . . . . . . . . . . . . . 169 Faça a programação com objetos soar como música aos seus ouvidos A solução tradicional . . . . . . . . . . . . . . . . . . . . . . . Com orientação a objeto . . . . . . . . . . . . . . . . . . . . . SoundPlayer: criação de uma classe de objetos . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . .
21
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
169 170 173 175 182
MALA DIRETA NUM CLIQUE . . . . . . . . . . . . . . . . . . . . . . . . . . 184 Produza cartas personalizadas com dados do Word, do Access ou do Excel . . . . . 184 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
22
NO COMANDO DE TODAS AS BARRAS . . . . . . . . . . . . . . . . . . . . 193 Uma aplicação que lista todas as barras de menus e de ferramentas do Word . . . . . 193 O código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
23
TODOS OS ÍCONES À SUA DISPOSIÇÃO . . . . . . . . . . . . . . . . . . . 202 Como criar um catálogo de todas as imagens de botões disponíveis no Word . . . . . 202 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
24
UM QUEBRA-CABEÇA DE NÚMERO E CORES . . . . . . . . . . . . . . . . . . 211 Domine os 16,7 milhões de cores existentes no arco-íris de seu monitor O aplicativo Cores. . . . . . . . . . . . . . . . . . . . . . . . . O código do programa . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . .
25
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
211 213 214 216
FAZENDO OS PAPÉIS DA SORTE . . . . . . . . . . . . . . . . . . . . . . . . 218 Construa um programa que organiza e imprime cupons para sorteios O formulário do projeto . . . . . . . . . . . . . . . . . . . . . O código . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . .
26
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
218 220 221 224
CLONES AO GOSTO DO FREGUÊS . . . . . . . . . . . . . . . . . . . . . . 226 Produza documentos numerados com quantidade de cópias indicada pelo usuário . . 226 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
27
PARA ACABAR COM A “LETRA DE MÉDICO” . . . . . . . . . . . . . . . . . . 230 Um programa que emite receitas para médicos e dentistas O documento-modelo . . . . . . . . . . . . . . . . . O formulário frmReceita . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . .
28
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
230 231 232 239
EPA! WORD COM BANCO DE DADOS? . . . . . . . . . . . . . . . . . . . . 242 Use o processador de texto para gerenciar arquivos do Access . . . . . . . . . . . 242 O código por trás do formulário. . . . . . . . . . . . . . . . . . . . . . . . . . 246 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
IX
29
OS DADOS ESTÃO NA PLANILHA . . . . . . . . . . . . . . . . . . . . . . . 255 No Word, use os métodos do Access para gerenciar arquivos do Excel . . . . . . . . 255 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
30
DO BANCO DE DADOS PARA O DOCUMENTO . . . . . . . . . . . . . . . . 260 Monte um controle imobiliário com o Word e recursos de bancos de dados Ficha do projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . As caixas Tratamento, Mês e Ano . . . . . . . . . . . . . . . . . . . . Caixa de combinação Localizar . . . . . . . . . . . . . . . . . . . . Detector de alterações . . . . . . . . . . . . . . . . . . . . . . . . . Emissão de recibos . . . . . . . . . . . . . . . . . . . . . . . . . . Relatório . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
. . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . . . . .
260 260 262 263 263 265 267 269
. . . . . . . .
. . . . . . . .
. . . . . . . .
273 274 277 279 280 282 289 289
. . . . . . .
. . . . . . .
. . . . . . .
291 294 295 297 297 298 300
. . . . .
. . . . .
. . . . .
302 305 311 313 318
MAGO DE CASA TAMBÉM FAZ MILAGRE . . . . . . . . . . . . . . . . . . . . 323 Crie, você mesmo, um Assistente para extrair informações de bancos de dados. A lógica do Assistente . . . . . . . . . . . . . . . . . . . . . . . . . . . Saída para o Word . . . . . . . . . . . . . . . . . . . . . . . . . . . . Saída para o Excel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Detalhes finais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
. . . . . . . .
NÃO FIQUE FORA DO PRAZO! . . . . . . . . . . . . . . . . . . . . . . . . 302 Um calendário que faz operações com datas, e sabe contar sábados, domingos e feriados . . . . . . . . . . . . . . . . . . . . . . . . Construção do formulário principal . . . . . . . . . . . . . . . . . Formulário frmFeriados . . . . . . . . . . . . . . . . . . . . . . Formulário frmNovosFeriados . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . .
34
. . . . . . . .
FAÇAM AS SUAS APOSTAS! . . . . . . . . . . . . . . . . . . . . . . . . . . 291 Como construir um jogo de dados gráfico com três modalidades de apostas . Desenho da interface . . . . . . . . . . . . . . . . . . . . . . . . . . A lógica do programa . . . . . . . . . . . . . . . . . . . . . . . . . . Modalidade Comum. . . . . . . . . . . . . . . . . . . . . . . . . . . Modalidade Ordem . . . . . . . . . . . . . . . . . . . . . . . . . . . Modalidade Soma . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
. . . . . . . .
UM RECIBO PARA TODAS AS TRANSAÇÕES. . . . . . . . . . . . . . . . . . . . 273 Crie um gerador de recibos configurável para diferentes usos e situações O que faz o Gerador de Recibos . . . . . . . . . . . . . . . . . . . Orelha Empresa e Cidade . . . . . . . . . . . . . . . . . . . . . . O aplicativo por dentro . . . . . . . . . . . . . . . . . . . . . . . O formulário . . . . . . . . . . . . . . . . . . . . . . . . . . . . O código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comentários finais . . . . . . . . . . . . . . . . . . . . . . . . . . Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . .
32
. . . . . . . .
. . . . . .
. . . . . .
. . . . . .
323 327 337 338 339 341
INSTALE E DESINSTALE SEUS PROGRAMAS . . . . . . . . . . . . . . . . . . 343 Monte um instalador de aplicações num documento do Word. . . . . . . . . . . . 343 Para ir mais além . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
X
Introdução O LADO ESCURO DA LUA Este livro foi escrito com um objetivo: mostrar ao leitor que alguns dos programas de escritório, com os quais ele convive no dia-a-dia, também podem funcionar como poderosas ferramentas para a criação de funções novas e personalizadas, que jamais estarão disponíveis nas caixas de diálogo e menus desses programas. A idéia do software de escritório como ferramenta para criar outras ferramentas pode ser aplicada, hoje, a uma vasta gama de produtos. No entanto, escolhi trabalhar com o Microsoft Word, que é, de longe, o software mais utilizado no Brasil. Assim, se o livro fizer jus ao seu objetivo, a idéia poderá chegar a um número mais amplo de usuários. A obra está organizada em duas partes. Na primeira, tenta-se aproximar o leitor de alguns aspectos do uso interativo do Word que vão facilitar a compreensão da parte final. Esta última é dedicada à criação de aplicações, usando o Visual Basic for Applications, VBA, a linguagem de desenvolvimento embutida em aplicativos do Office 97 e do 2000 e em dezenas de outros programas do mercado. Na porção inicial do livro, mostra-se, por exemplo, como criar, personalizar e apagar menus e barras de ferramentas. Esse conhecimento será útil mais tarde, quando o leitor quiser que as próprias macros sejam acionadas a partir de menus ou de botões em barras de ferramentas. Fica evidente que essa parte do volume foi concebida com os olhos voltados para a outra, que trata de programação. Mesmo assim, tudo que está incluído nela interessa a qualquer usuário avançado, mesmo aquele que não se deseja envolver com o que vem depois. Interessa ainda a qualquer usuário minimamente curioso o capítulo “Dicas, truques e toques”, também incluído na primeira parte. Esse capítulo reúne dezenas de informações úteis sobre a operação do Word 2000 ou do 97. Não se trata de procedimentos mirabolantes, que serviriam apenas para o leitor mostrar que sabe. São dicas escolhidas a dedo, com o critério do uso prático. Com esse mesmo espírito, a primeira parte do livro contém ainda um capítulo no qual se faz uma apresentação sumária de cada um dos projetos que serão desenvolvidos na Parte 2. A idéia é que, antes mesmo de analisar os projetos, o leitor possa tirar proveito deles, inclusive utilizando-os no dia-a-dia. A Parte 2 consiste em trinta pequenos capítulos, cada qual correspondente a um projeto. Seguem-se, aqui, os mesmos critérios adotados no início do livro:
quase todos os projetos voltam-se para necessidades práticas do dia-a-dia.Um calcula datas e prazos, levando em conta fins de semana e feriados. Outro gera recibos para quem presta serviços mensais. Outro, ainda, automatiza a emissão de receitas médicas. São, quase sempre, soluções pequenas e de utilidade indiscutível. Cada projeto termina com uma seção chamada “Para ir mais além”. Nela, o leitor encontra sugestões sobre como ampliar ou redirecionar a solução. Em alguns casos, foi feito um esforço para tornar o produto imediatamente utilizável. O exemplo típico desse esforço é dado pela aplicação Receita Médica. Trata-se de um programa no qual o usuário pode configurar tudo (nome, endereço, especialidade médica ou odontológica e até o cabeçalho e o rodapé da receita emitida). Portanto, é só pegar e usar. No corpo de cada um desses trinta capítulos, o projeto é comentado passo a passo e discutem-se as dificuldades de desenvolvimento e as saídas encontradas. Com essa abordagem, o objetivo do livro não é ensinar o leitor a programar em Visual Basic. É, principalmente, mostrar como essas soluções foram feitas e que ele também poderia chegar a elas, com algum esforço e curiosidade. Curiosidade. Acredito que esta seja a chave. Não sei de nenhuma pessoa com grande conhecimento na área de computadores que não tenha sido impulsionada por esse motor. E isso vale, inclusive, para quem concluiu um curso de graduação na área. Portanto, este livro é dedicado aos curiosos, a quem gosta de saber que sempre é possível tirar mais de uma ferramenta como o Word, fazendo-a trabalhar em seu favor. Acredito que este volume também seja útil a estudantes que estejam dando os primeiros passos em programação. Não existe aqui uma postura didática formal, mas há alguma experiência a transmitir na base da aprendizagem fundamentada em exemplos. Penso também que algumas soluções mostradas aqui interessam até mesmo aos profissionais de informática. Em boa parte dos casos, eles estão voltados para os grandes problemas – bancos de dados corporativos, redes, aplicações para a Internet – e não têm condições de debruçar-se diante de um assunto “menor”, como o desenvolvimento em programas de escritório. Nesse aspecto, o usuário comum fica órfão, inclusive nas grandes empresas. Ele não tem a técnica que lhe permite resolver os problemas. E quem a tem, está ocupado em outras tarefas... Pelas características mostradas acima, o livro volta-se para o usuário intermediário ou avançado do Word. Isso não entra em contradição com as indicações registradas na ficha que abre cada capítulo de desenvolvimento. Lá, você vai encontrar níveis iniciante, intermediário e avançado. Essa classificação não se refere aos usuários do Word em geral, mas especificamente ao programador VBA. O Word é como a lua. Tem um lado claro, brilhante, ao alcance de todos os olhares. Mas esconde, também, uma face desconhecida. Entretanto, tenho certeza, existe no ar uma curiosidade imensa. As pessoas querem saber, explorar, descobrir. Este volume é uma lanterna apontada para esse lado escuro. Espero, sinceramente, que ele seja útil. XII
CARLOS MACHADO São Paulo, janeiro de 2000
PARTE 1
1 Trinta projetos e um lance de dados Um sumário das soluções práticas desenvolvidas neste livro Os trinta projetos de desenvolvimento reunidos na Parte 2 deste livro trabalham com a idéia de mostrar, na prática, como usar os principais recursos de programação do VBA/Word disponíveis nas versões 97 e 2000. Cada projeto ocupa um capítulo e exige um nível técnico crescente. Inicialmente, o foco do projeto está apenas num item técnico. Passo a passo, a complexidade das aplicações vai-se expandindo. Vamos avançar de um simples cálculo de idade até a estrutura complicada de um assistente para extrair informações de qualquer banco de dados Access. Sempre que possível, são discutidas as dificuldades do processo de desenvolvimento, ao lado de saídas alternativas que o leitor poderia adotar. Ao percorrer estas páginas, deve-se sempre ter em mente que o objetivo delas não é decretar: “Esta é a maneira correta de fazer isso.” Assim como um idioma natural, as linguagens e ferramentas de programação também são flexíveis. Não há uma forma “certa” de escrever uma carta, um relatório, um bilhete. Do mesmo modo, não existe uma receita definitiva em programação. Portanto, o leitor deve encarar estes projetos como pontos de partida, como sugestões para que ele mesmo encontre soluções.
Nos projetos, procura-se trabalhar com os recursos disponíveis no VBA/Word, sempre de olho em objetivos práticos. Nada de pirotecnias de programação, acessíveis somente aos que dominam artes inacessíveis para mortais comuns. Alguns projetos voltam-se imediatamente para tarefas cotidianas. É o caso, por exemplo, daquele que automatiza a emissão de memorandos numerados (Capítulo 14). Ou, então, do outro que emite receitas médicas (Capítulo 27) e ainda do gerador universal de recibos para prestadores de serviços (Capítulo 31). Trata-se de aplicações prontas. Mesmo quem não tem a menor curiosidade a respeito de programação pode beneficiar-se, imediatamente, desses programas.
F I G U R A 1 . 1 Gerador de Recibos: para prestadores de serviços
Outros projetos, embora sem o mesmo apelo prático, voltam-se para o desenvolvedor em ambiente VBA/Word. São ferramentas nas quais ele pode se apoiar em sua tarefa de criar novas soluções. Destacam-se, nesse caso, o Catálogo de Fontes (Capítulo 16), os Exercícios com Tabelas (Capítulo 18) e o Instalador de Aplicações (Capítulo 35). Este último, adaptado, pode servir de modelo para a instalação de quaisquer programas no ambiente do Word. Para não dizer que cuidei apenas de assuntos ligados direta ou indiretamente à reprodução vil metal, incluí também um projeto que é útil em termos da experiência de programação, mas absolutamente fútil em termos de produtividade. Trata-se do game Álea, um jogo de azar com dois dados e três modalidades de aposta. Além de demonstrar procedimentos gráficos, o desafio desse programa é manter a aleatoriedade em cada jogada. Aqui, o comando pertence ao acaso. Afinal, como escreveu o mestre Mallarmé, um lance de dados jamais abolirá o acaso. 4
F I G U R A 1 . 2 Catálogo de Fontes: listagem na tela e em
documento para impressão
F I G U R A 1 . 3 Álea, um jogo de azar com dois dados e três
modalidades de aposta
Além de apresentar em detalhes a lógica de construção de cada projeto, os capítulos da Parte 2 deste volume sempre terminam com uma seção chamada “Para ir mais além”. Nela, o leitor encontra sugestões sobre como ampliar e personalizar o projeto ou simplesmente refazê-lo a partir de outra perspectiva. Também no final de cada capítulo estão uma ou mais dicas de programação. Algumas delas são bastante complexas e renderiam assunto para outros desenvolvimentos. Todos os projetos e dicas de código encontram-se no CD-ROM que acompanha o livro. Veja no próximo capítulo como instalar e usar os recursos desse CD. Espero que os trinta projetos da Parte 2 sirvam de base para você tomar gosto e construir trinta vezes trinta outras soluções da própria cabeça. Veja, a seguir, uma descrição sumária de cada projeto incluído na parte final do livro. 5
O que é cada projeto
6
CAPÍTULO
PROJETO
O QUE FAZ
6
Informações do Sistema
Demonstra como obter informações sobre o sistema operacional, a máquina e o Word. Acessa o Painel de Controle e o utilitário Informações do Sistema, do Windows.
7
Cálculo de Idade
Calcula a idade exata, em anos, a partir da data de nascimento. Propicia um primeiro contato com funções do VBA associadas a data e hora.
8
Cálculo de Datas
Executa operações de soma e subtração com datas e prazos. Representa a introdução a um tema que será desenvolvido integralmente no Capítulo 33, num dos projetos mais complexos do livro.
9
Conversores de Arquivos
Produz uma tabela com todos os conversores de arquivos disponíveis no Word. Indica os tipos de arquivo que o programa pode ler e salvar.
10
Conversor de Arquivos DOC
Converte arquivos no formato DOC, do Word 2000 ou 97, para TXT, RTF e HTML, além de DOC do Word 6.0 e do Word 2.0. Útil quando se deseja converter grande quantidade de arquivos.
11
Documento Numerado Simples
Produz documentos com numeração seqüencial do tipo 001/2000, 002/2000. Na virada do ano, recomeça a contagem. Trabalha com um arquivo INI para armazenar as informações de data e numeração.
12
Vários Documentos Numerados
Semelhante ao projeto 11, controla a numeração seqüencial de vários tipos de documentos ao mesmo tempo: memorandos, cartas, ofícios e relatórios.
13
Gerador de Valores por Extenso
Escreve, automaticamente, números por extenso, em reais. Trabalha também com outras moedas.
14
Memorando Numerado
Produz memorandos numerados, automatizando também o preenchimento do cabeçalho do documento: nomes do remetente e dos destinatários principal e secundários, além de data e assunto.
15
Contador de Expressões
Ferramenta auxiliar na revisão de texto. Conta palavras ou expressões repetidas e, opcionalmente, destaca-as com o pincel marcador.
16
Catálogo de Fontes
Exibe na tela amostras das fontes disponíveis no Windows. Também produz um catálogo para impressão com fontes em tamanho escolhido pelo usuário.
CAPÍTULO
PROJETO
O QUE FAZ
17
Catálogo de Estilos
Lista, na tela e no papel, todos os estilos disponíveis no Word – os embutidos no programa e os criados pelo usuário.
18
Exercícios com Tabelas
Executa dez operações com tabelas, usando métodos do VBA/Word. Cria, destrói e formata tabelas. Adiciona e subtrai linhas e colunas. Efetua cálculos com valores das células.
19
Som no Word 1
Toca arquivos de áudio WAV e MID a partir do Word, usando o acessório Mídia Player. Demonstra como executar operações por meio de programas externos.
20
Som no Word 2
Gera listagens de arquivos de áudio e dispara a execução de sons WAV, MID e MP3 em programas MP3 players. Destaque para o desenvolvimento, empregando programação orientada a objeto.
21
Mala Direta
Organiza malas diretas com informações extraídas de três diferentes fontes de dados: arquivos do Word, bancos de dados Access e planilhas Excel.
22
Barras de Ferramentas
Lista todas as barras de comandos existentes no Word, permitindo exibi-las e ocultá-las. Oferece informações detalhadas sobre cada barra. Lista também os botões e outros controles existentes em cada uma.
23
Botões de Comando
Exibe, em barras de ferramentas enormes, todos os botões de comando existentes no Word. Auxilia o programador a escolher ícones para os próprios aplicativos.
24
Cores RGB
Demonstra o sistema de cores usado nos monitores. Apresenta, na prática, como se formam os 16,7 milhões de cores do sistema RGB e como se calculam os números associados a essas cores.
25
Bilhetes Numerados
Produz bilhetes numerados com base num layout de documento definido pelo usuário. Demonstra a programação com objetos de desenho do Word.
26
Documentos Repetidos
Gera documentos numerados múltiplos, em quantidades indicadas pelo usuário. Se, por exemplo, a quantidade é 3, os documentos obedecem à seqüência 1,1,1-2,2,2 etc.
27
Receita Médica
Automatiza completamente a emissão de receitas médicas. Configurável, pode ser usado, de imediato, por médicos ou dentistas, sem a necessidade de qualquer modificação no projeto.
7
8
CAPÍTULO
PROJETO
O QUE FAZ
28
Banco de Dados no Word
Gerencia bancos de dados Access a partir do Word, usando apenas programação VBA. Não requer a presença do Access no sistema.
29
Planilha Excel como Banco de Dados
Trabalha, no Word, com uma planilha Excel, tratada como banco de dados do Access. Não exige a presença do Excel.
30
Do Banco de Dados para o Documento
Gera relatórios e outros documentos com informações extraídas de um banco de dados Access. O exemplo é adaptado para uma imobiliária e produz recibos de aluguel.
31
Gerador de Recibos
Um programa que é a mãe de todos os recibos. Assim como o Receita Médica (projeto 27), é configurável para cadastrar clientes, usuários e tipos de serviços prestados.
32
Jogo de Dados Álea
Afinal, uma diversão – que ninguém é de ferro! Jogo de azar com dois dados e três modalidades de aposta. Demonstra características gráficas do VBA.
33
Prazo Certo
Calcula prazos, levando em conta uma tabela de feriados atualizável pelo usuário. Traz embutidos os feriados nacionais, fixos e móveis, até 2010. Para advogados, contadores e escritórios comerciais.
34
Assistente de Bancos de Dados
Um assistente que conduz o usuário, passo a passo, para abrir e extrair informações de qualquer banco de dados Access. O resultado é apresentado numa tabela do Word ou numa planilha Excel.
35
Instalador de Aplicações
Mostra como instalar programas a partir de um documento do Word. Este demonstrativo instala os arquivos e os módulos de programação do projeto 31 (Gerador de Recibos).
2 Como usar o CD-ROM Instalação e uso dos arquivos de exemplo Todos os exemplos de programação citados neste livro estão reproduzidos no CD-ROM que o acompanha. Na maioria dos casos, o texto inclui apenas as partes mais importantes do código. Não se preocupe: no CD, invariavelmente, você vai encontrar a rotina ou projeto na íntegra. Isso vale, inclusive, para as rotinas incluídas como dicas no final dos capítulos. A estrutura dos arquivos e diretórios do CD-ROM é simples. Somente na Parte 2 do livro existem listagens de código. Como essa parte contém trinta capítulos, estes se refletem no CD. Para cada um existe uma pasta: Capítulo 6, Capítulo 7 etc. Dentro desses diretórios, você encontra todos os itens necessários para o funcionamento do projeto ou dica constante no capítulo. Desse modo, você vai encontrar, nesses diretórios, arquivos conforme a tabela seguinte: TIPO DE ARQUIVO
O QUE É
.DOC
Documento comum do Word
.DOT
Modelo do Word. Pode conter documento-base (por exemplo, o modelo para um tipo de recibo) e/ou projeto de automação VBA
.FRM
Arquivo de formulário do VBA
.FRX
Arquivo complementar do formulário VBA
TIPO DE ARQUIVO
O QUE É
.BAS
Arquivo de módulo do VBA. Contém apenas código
.CLS
Arquivo de classe VBA. Refere-se a programação orientada a objeto
.MDB
Banco de dados do Access
.XLS
Pasta de trabalho do Excel
Como instalar Somente alguns arquivos são necessários para que você possa executar qualquer um dos projetos em seu micro. Portanto, dê um clique duplo no arquivo Instalar.dot. Esse modelo do Word, que contém código, vai copiar para o seu disco os arquivos necessários à execução de qualquer programa do livro. A qualquer momento, você pode recorrer ao CD e executar programas (normalmente arquivos DOT) diretamente a partir dele. Como já foi dito antes, cada programa está contido no diretório correspondente ao capítulo.
Observações importantes 1. Todos os projetos e códigos isolados deste livro foram testados exausti-
vamente em máquinas com o Word 97 e o Word 2000.No entanto, há mais versões do Word espalhadas por aí do que supõe a nossa vã filosofia. Então – embora não tenhamos constatado isso –, há possibilidade de algum código não funcionar. Nesse caso, primeiro tente reconstruir o projeto em seu micro, carregando os componentes (arquivos FRM, BAS etc.) que estão no diretório correspondente. Se, mesmo assim, não funcionar, entre em contato conosco.
2. Ao carregar projetos que envolvem o uso da tecnologia DAO (Data
Access Objects) de bancos de dados, eles tendem a não funcionar, num primeiro momento, em sua máquina. Isso vale tanto para rodar um projeto pronto – diretamente do CD ou copiado dele – como para a importação do projeto a partir dos arquivos FRM e BAS. O programa vai exibir uma mensagem nos seguintes termos: “Erro de compilação. O tipo definido pelo usuário não foi definido.” Não se preocupe. No ambiente do VBA, acione Ferramentas/Referências. Na tela que se abre, marque a opção:
10
n
Microsoft DAO 3.5 Object Library, se estiver no Word 97; ou
n
Microsoft DAO 3.6 Object Library, se seu Word for o 2000.
F I G U R A 2 . 1 Ajuste para bancos de dados: aqui, marcada a
opção para o Word 2000
3. Quando você copiar um arquivo do CD para o seu disco rígido, não se
esqueça de ajustar os atributos desse arquivo. No CD-ROM, por definição, ele é do tipo somente leitura. Ao ser copiado para o disco rígido, essa característica se mantém. Então, você não poderá editar esse arquivo. Para evitar que isso ocorra, depois de copiar o arquivo, clique nele com o botão direito, selecione Propriedades e desligue a opção Somente Leitura.
4. Se sua versão do Word é a 2000, vá em Ferramentas/Macro/Segurança e
escolha o nível de segurança médio. Se o nível vigente for alto, você não vai conseguir abrir os programas executando os arquivos DOT. Com essa configuração, o Word simplesmente desativa as macros contidas no documento – e não dá nenhum aviso ao usuário.
11
3 Dicas, truques e toques Pequenas soluções que tornam seu trabalho com o Word mais rápido e agradável Neste capítulo você encontra dezenas de jeitinhos e macetes extraídos da pesquisa ou do uso cotidiano do Word 97 e do 2000. Na maioria dos casos, os procedimentos se aplicam às duas versões do produto. As situações de validade exclusiva para uma ou outra versão estão claramente indicadas no texto.
Seleção rápida: coluna Para selecionar rapidamente toda uma coluna de tabela, faça o seguinte: leve o ponteiro do mouse até a borda superior da primeira célula da coluna e clique quando aparecer uma pequena seta em negrito apontando para baixo.
F I G U R A 3 . 1 Quadrados para seleção de tabelas
Seleção rápida: tabela inteira O Word 2000 aperfeiçoou bastante os recursos de tabelas. Uma das novidades é um pequeno quadrado que aparece no canto superior esquerdo ou no canto inferior direito da tabela quando se passa o mouse sobre ela. Para selecionar toda a tabela, basta clicar num desses quadrados. Os dois têm também outras funções. O quadrado superior, cujo nome oficial é alça de movimentação, serve como ponto de apoio para arrastar toda a tabela. O outro, chamado alça de redimensionamento, permite redefinir o tamanho da tabela.
F I G U R A 3 . 2 Lista numerada na tabela
Numeração automática de colunas Você está trabalhando com uma tabela. Depois de aprontá-la, decide que seria melhor incluir uma coluna à esquerda, numerando seqüencialmente cada linha da tabela. Por favor, nem pense em sair escrevendo: 1, descer para a próxima linha; 2, descer para a linha seguinte... Nada disso: selecione a coluna a ser numerada e acione o botão numeração (aquele que mostra os números 1, 2, 3, seguidos de linhas). Pronto. A coluna está numerada. Em textos comuns, se você tiver uma série de parágrafos e quiser numerá-los, o procedimento é o mesmo: selecione os parágrafos e clique no botão Numeração.
Tabulação dentro da célula Cada célula de uma tabela funciona como uma página normal do Word. Nela, você pode definir diferentes tipos de parágrafos, recuos à esquerda e à direita, fontes etc. Você só não pode acionar a tecla Tab para obter o espaçamento de tabulação, como faz numa página normal. Isso porque, na tabela, Tab serve como comando de mudança para a próxima célula. Então, o que fazer para tabular dentro de uma célula? Simples: acione Ctrl+Tab.
Célula sem quebras Quando há no documento uma tabela que passa de uma página para outra, é comum uma linha da tabela ficar dividida entre as duas páginas. Há uma forma de evitar isso, que é a mesma usada para os parágrafos. Selecione as células partidas e acione o comando Formatar/Parágrafo, clique na orelha Quebras de Linha e 13
de Página e ligue a opção Manter Com o Próximo. Para que essa configuração valha para todas as linhas da tabela, aplique o mesmo comando à tabela inteira. Isso vale para o Word 97 e para o Word 2000. Neste último, no entanto, também há um novo caminho. Selecione a linha desejada e clique em Tabela/Propriedades da Tabela. Na caixa de diálogo Propriedades da Tabela (não existia no Word 97), selecione a orelha Linha e desligue a opção Permitir Quebra de Linha Entre Páginas. Como padrão, essa opção fica ligada.
Hifenização automática Quer que, como padrão, o documento seja automaticamente hifenizado? Escolha Ferramentas/Idiomas/Hifenização. Na caixa de diálogo que se abre, ligue a caixa de verificação Hifenizar o Documento Automaticamente. Acione OK. Mas ainda não terminou. A hifenização automática é uma função do Word que só vale para cada documento. Para transformá-la num padrão, o caminho é um pouco mais longo. Crie um documento e adicione a ele a hifenização automática. Em seguida, salve-o como um modelo. Agora, quando quiser trabalhar com esse recurso, abra um documento novo baseado naquele modelo.
F I G U R A 3 . 3 Word 2000: nova caixa de diálogo para 14
tabelas
Ao lado do texto, sem truques Até o Word 97, você precisava fazer algumas ginásticas para posicionar uma tabela lado a lado com o texto do documento. Um desses truques era colocar a tabela dentro de uma caixa de texto (objeto de desenho). No Word 2000, esse truque não é mais necessário. A nova caixa de diálogo exibida pelo comando Tabela/Propriedades da Tabela permite dispor a tabela em várias posições em relação ao texto: à esquerda, centralizada, à direita etc. Basta selecioná-la e escolher a posição desejada.
Direto do Excel, sem escala Aposto que, para inserir num documento uma tabela do Excel, você abre a planilha, copia o pedaço desejado e cola-o no texto do Word. Não é preciso fazer isso; o Word sabe ler diretamente a planilha. Acione Arquivo/Abrir, no Word. Na caixa Arquivos do Tipo, indique Planilha do Microsoft Excel (*.xls) e aponte para o arquivo desejado. Surge nova janela, Abrir Planilha. Ela lhe permite abrir a pasta de trabalho inteira ou escolher uma folha de cálculo específica. Dentro desta, pode ainda escolher uma região de células.
Buquê de documentos Você está trabalhando num projeto que envolve, por exemplo, vários textos do Word e planilhas Excel. Que tal organizar um documento que funcione como um menu, ou porta de entrada, para todos os documentos do projeto? Faça o seguinte: escreva uma página de introdução com a lista dos documentos e o sumário do conteúdo de cada um deles. Agora, a dica: adicione hyperlinks. Assim, a partir dessa introdução, com um clique você passa a ter acesso direto aos outros documentos. Essa solução funciona, inclusive, em ambiente de rede – desde que os documentos se encontrem num local acessível a todos os usuários. Se quiser fazer o encadeamento perfeito, inclua em cada documento do buquê um link para o documento-índice.
Palavra errada no dicionário Sem querer, você inclui no dicionário do Word uma palavra errada. E agora? Calma. Acione Ferramentas/Opções e ponha em primeiro plano a orelha Ortografia e Gramática. Clique no botão Dicionários e, na próxima tela, no botão Editar. Se for o caso, o Word vai avisar que, para editar o dicionário, será preciso desligar a correção automática. Aceite. A lista de palavras que você incluiu no dicionário vai aparecer. Localize o termo incluído por engano, elimine-o (ou corrija-o) e salve o documento. 15
F I G U R A 3 . 4 Sinônimos para a palavra “aparece”
Sinônimos Ao escrever, de repente você percebe que já usou a mesma palavra uma porção de vezes e gostaria de variar. Quer encontrar um sinônimo rapidamente? Coloque o cursor sobre a palavra e tecle Shift+F7. O Word abre a janela do dicionário e oferece o leque de sinônimos que ele tem a apresentar. Não é nenhum superdicionário, mas quebra o galho. Se você é um profissional da escrita e quer mais, instale em sua máquina um Aurélio ou um Michaelis eletrônico.
Dicionários personalizados Quem escreve documentos ligados a diferentes áreas de conhecimento precisa, muitas vezes, de dicionários especializados. Por exemplo, um vocabulário para a área médica, outro para a de psicologia etc. O Word aceita que você crie esse tipo de dicionário. Primeiro, crie um novo documento e escreva nele as palavras iniciais de um dicionário específico. Cada termo deve ocupar uma linha. Assim: Explorer Java WinZip Word add-on barramento bug cavalo-de-tróia drive drives Observe que há duas ordens alfabéticas: primeiro, as palavras com iniciais maiúsculas; depois, as outras. Salve o arquivo como Somente Texto, com 16
a extensão DIC. Verifique onde está seu dicionário personalizado padrão (Usuario.dic) e coloque lá o novo glossário. O diretório varia, conforme a versão do Word. Agora, dê o comando Ferramentas/Opções, orelha Ortografia e Gramática. Clique no botão Dicionários e, na caixa Dicionários Personalizados, marque aquele que você acabou de criar. Na hora de fazer a verificação ortográfica (Ferramentas/Ortografia e Gramática), clique no botão Opções e escolha o dicionário desejado.
Salvar tudo, fechar tudo No Word 97, você pode trabalhar ao mesmo tempo com múltiplos documentos. Edita aqui, corrige ali, de repente você fez modificações em vários deles e não salvou. Para evitar a tarefa de colocar cada documento em primeiro plano e salvá-lo, faça o seguinte. Pressione a tecla Shift e abra o menu Arquivo. Veja que lá aparece um comando incomum: Salvar Tudo. Dispare esse comando e todos os documentos serão salvos de uma vez. O mesmo procedimento pode ser adotado para fechar os arquivos. Observe que, com a tecla Shift, também aparece o comando Fechar Tudo.
F I G U R A 3 . 5 Estrutura do documento: navegação título a título
Estrutura do documento Quando você estiver escrevendo documentos como o deste capítulo – no formato um título, um bloco de texto – e quiser tomar pé da situação (quantos blocos já escreveu, qual título vem antes de qual), clique no botão Estrutura do Documento. Ele divide a janela do Word em duas partes. À esquerda, os títulos. À direita, seu texto. Clique num título e o cursor se desloca exatamente para o trecho correspondente. 17
Pincel de formatos Durante a elaboração de um texto, você quer que este parágrafo tenha o mesmo formato (recuo, fonte, cor etc.) de outro que você já escreveu no documento atual ou em outro documento. Não perca tempo refazendo, passo a passo, aquela formatação. Vá ao parágrafo que deseja tomar como modelo, selecione-o e clique no botão Pincel. Observe que o cursor assume a figura de um pincel. Agora, selecione o parágrafo de destino. Pronto.
Aperte Ctrl+Z, o texto sumiu! Digitou algo errado, o texto sumiu? Acione imediatamente Ctrl+Z (Desfazer) e fique tranqüilo: tudo volta ao que era antes do susto. O comando Ctrl+Z também é providencial para anular ações automáticas que o Word faz e você não quer. Entre essas ações estão iniciar uma lista numerada; transformar em hyperlink um endereço da Internet que você digita; ou colocar maiúscula inicial numa palavra que você escreveu, conscientemente, com minúscula.
Personalização Você já usava o Word (95, 97 ou 2000) há algum tempo e agora, por qualquer motivo (micro ou disco novo, reformatação etc.), precisa mudar de máquina. Como transferir para a nova instalação as macros do Word, as entradas de AutoTexto, os itens de AutoCorreção e as palavras adicionadas ao dicionário personalizado? Fácil. Copie para a nova máquina os arquivos ACL (AutoTexto), DIC (Dicionário) e o modelo Normal.dot (macros e personalizações do ambiente de trabalho). Cada um desses arquivos deverá substituir um novo – e não personalizado – criado pelo Word. Não se esqueça de copiar também outros arquivos DOT que você tenha criado.
F I G U R A 3 . 6 Barra de ferramentas padrão: dobrada em L 18
Mágica na barra de ferramentas No Word 2000, as barras de ferramentas fazem um pouco de mágica. Para economizar espaço, as duas barras principais (Padrão e Formatação) ficam lado a lado na tela, e não uma em cada linha. É óbvio que, dessa maneira, não há lugar para as duas, completas, mesmo em telas de 800´ 600 ou 1024´ 768 pixels. A solução: cada barra forma uma espécie de L, cuja perna vertical se abre quando você clica numa pequena seta. Nessa perna fica escondido o restante dos botões. O mais interessante, porém, é que, quando se aciona um dos botões escondidos, ele, imediatamente, passa para o lado visível da barra de ferramentas.
Seguro morreu de velho... Por mais cuidadoso que você seja, não há como considerar-se fora do perigo de perder um texto no qual você vinha trabalhando durante horas. No entanto, você pode minimizar os riscos. Para isso, configure o Word para sempre fazer uma cópia de segurança dos documentos. Como? Clique em Ferramentas/Opções e traga para o primeiro plano a orelha Salvar. No quadro Opções de Salvamento, ligue a caixa Criar Sempre Backup. Sempre que você salvar um documento, a versão mais nova vai para o arquivo DOC normal e a anterior é mantida num arquivo com o mesmo nome e extensão WBK (Word Backup). Se, por acaso, você perder o DOC, pode recuperá-lo – se não todo, pelo menos parte dele – abrindo o arquivo WBK.
Impressão sem aquela folha a mais É comum encontrar usuários reclamando que o Word os faz desperdiçar papel porque sempre imprime, além do documento, uma folha com o resumo estatístico: número de páginas do documento, número de linhas, data de elaboração etc. Muitas vezes esses usuários agem como se essa folha viesse do outro mundo. Mas não há mistério nenhum. Basta acionar Ferramentas/Opções, orelha Imprimir, e desligar o item Incluir no Documento/Propriedades do Documento. E é só. Aliás, o padrão dessa opção é desligado. Se a impressão inclui as propriedades do documento, deve ser porque o próprio usuário, ou outra pessoa com acesso ao micro, modificou a configuração básica.
Modelo com imagem Acione, no menu, o comando Arquivo/Novo. Passe pelas diferentes orelhas da janela Novo e verifique: alguns modelos, quando selecionados, exibem uma imagem no lado direito da tela, outros não. Para incluir aquela miniatura de pré-visualização num modelo, abra o arquivo DOT, dê o comando Arquivo/Propriedades e, na caixa de diálogo, ative a orelha Resumo. Ligue a caixa Salvar Visualização da Figura. Salve o modelo e feche-o. Agora, tire a prova: volte 19
ao comando Arquivo/Novo e selecione modelo recém-criado. Detalhe: o arquivo do modelo fica maior, porque incorpora um bitmap com a imagem miniaturizada do documento.
F I G U R A 3 . 7 Modelo com previsão de página: figura
embutida
Desformatação rápida (1) Suponha que você esteja editando um texto que outra pessoa escreveu. Pouco moderada nos destaques, essa pessoa abusou das expressões em negrito, por exemplo. Naturalmente, você pode selecionar os trechos um a um e desligar o negrito. Mas há um meio bem mais rápido. Selecione o texto todo (Ctrl+T) ou uma parte dele, ligue o negrito e em seguida o desligue. Pronto. Toda a área selecionada retorna ao padrão normal.
Desformatação rápida (2) Você vai editar um texto escrito por outra pessoa. Naturalmente, ela trabalha com um padrão de formatos que não tem nada a ver com o seu. Para ajustar o documento ao padrão que você usa, selecione todo o texto e dispare a combinação de teclas Ctrl+Shift+B. Esse comando aplica o estilo Normal à seleção.
Imprimir parte de um documento Quando você quer imprimir somente parte de um texto, certamente você copia aquela parte, cola-a num documento novo, imprime e descarta esse documento. Experimente um caminho mais curto. Selecione o trecho a ser impresso e, no 20 menu, acione Arquivo/Imprimir. Na janela Imprimir, clique na opção Seleção.
Atenção para o detalhe: isso não vai funcionar se você clicar no botão Imprimir – que não exibe a caixa de diálogo e assume que você quer uma cópia do documento inteiro.
Figuras ocultas Ao trabalhar com documentos longos, cheios de objetos gráficos (tabelas, figuras, desenhos), o Word se torna lento quando é preciso navegar nele para cima e para baixo. Isso ocorre porque o programa consome recursos do sistema para exibir as imagens. Para ganhar mais agilidade nesses documentos, garanta que o modo de visualização está em Exibir/Layout da Página. Em seguida, clique em Ferramentas/Opções/orelha Exibir e ligue a caixa Espaços Reservados a Figuras. A partir de agora – e não só para o documento atual –, o Word passará a mostrar um quadro em branco no lugar das imagens. Para exibir as figuras, desfaça a configuração.
Seleção vertical Para selecionar texto verticalmente, mantenha pressionada a tecla Alt e movimente o mouse. Essa função é útil, por exemplo, quando se deseja negritar os números de itens de uma lista numerada. Pode ser usada também para destacar a primeira letra de uma seqüência de parágrafos de uma só linha – não somente com negrito, mas também aumentando o corpo da letra. Outra forma de trabalhar com a seleção vertical é ligá-la com a combinação de teclas Ctrl+Shift+F8 e depois desligá-la com o mesmo comando.
“Incorretor” ortográfico Logo depois que o Word 2000 é instalado, quando se digita uma palavra como “transformá-lo”, o corretor ortográfico entra em ação e automaticamente muda a expressão para “transforma-lo”, sem acento. Se, imediatamente, você der o comando Ctrl+Z, confirmando o seu “erro”, o Word incluirá a palavra com acento na lista de exceções da AutoCorreção. Para corrigir essa trapalhada, vá até Ferramentas/ AutoCorreção/orelha AutoCorreção e desligue a caixa Usar as Sugestões do Verificador Ortográfico Automaticamente. A idéia é boa, mas se é para entortar o que está certo – não, obrigado.
Clicar e digitar Um dos novos recursos do Office 2000 chama-se Clicar e Digitar. Ele permite que você insira texto numa área em branco do documento, sem antes teclar Tab ou Enter para posicionar o cursor no local desejado. Para experimentar o Clicar e Digitar, primeiro garanta que o documento esteja na opção Exibir/Layout de 21
Impressão ou Exibir/Layout da Web. Depois, escolha um ponto qualquer da página e dê nele um clique duplo. O cursor se posiciona no local, e você começar a digitar. Se o recurso não funcionar, é possível que esteja desativado. Então, acione Ferramentas/Opções, orelha Editar e ligue a caixa Ativar Clicar e Digitar.
F I G U R A 3 . 8 Janela Data e Hora no Word 2000: três idiomas
Data e hora Para inserir a data e a hora atuais no seu documento, você pode acionar o comando Inserir/Data e Hora e escolher, na caixa de diálogo que se abre, um dos vários formatos de data e/ou hora que o programa oferece. No Word 2000, essa caixa, além de trazer mais opções, opera em três idiomas: português, inglês e espanhol. Se você quiser que a data seja atualizada quando o documento for aberto, não se esqueça de ligar a caixa de verificação Atualizar Automaticamente. Mas atenção: você verá uma nova data cada vez que abrir o documento em dias diferentes. Se a intenção é registrar a data em que o documento foi produzido, prefira trabalhar com a opção Inserir/Campo. Nela, indique, em Categorias, Data e Hora; e em Campo, CreateDate.
Dias e meses sem maiúscula Quem reclamava, com razão, que o Word 97 escrevia o nome dos meses e dos dias da semana com iniciais maiúsculas não tem mais do que se queixar. No Word 2000 e mesmo no 97 com o Windows 95 ou o 98 atualizados, isso não ocorre mais. Na verdade, o problema não estava no Word, e sim no arquivo Oleaut32.dll. As versões mais novas dessa DLL corrigiram o problema. A atualização pode ocorrer por meio da instalação de qualquer programa que traga o 22 arquivo em edição mais recente – por exemplo, o Internet Explorer 5.0.
F I G U R A 3 . 9 Detalhe da janela Imprimir do Word 2000: zoom
Dezesseis páginas por folha! O Word 2000 traz uma característica nova que, com certeza, será muito bem recebida por todos os usuários. Trata-se da opção de Zoom na impressão, que permite imprimir 2, 4, 6, 8 ou 16 páginas de um documento na mesma folha de papel. Para utilizá-la, acione Arquivo/Imprimir e, no quadro Zoom, indique o número de páginas e um tamanho de papel (Carta, A4 etc.). Atenção: como padrão, a caixa Ajustar ao Tamanho do Papel fica em Sem Dimensionamento. Se você não determinar um tamanho específico, a impressão sairá com as letras encavaladas. Pelo menos é o que acontece com uma HP DeskJet 660C. A impressão com duas páginas por folha é interessante quando se quer produzir um documento no formato de livro. Antes de imprimir, experimente para descobrir o melhor tamanho de letra a ser usado. Lembre que, para aumentar a quantidade de texto ocupando o mesmo espaço, o tamanho da letra terá de ser reduzido. Naturalmente, só é possível obter dezesseis páginas por folha se o texto de cada uma não passar de algumas poucas palavras, escritas com letras bem grandes.
Economia de papel A opção de imprimir múltiplas páginas por folha não se aplica apenas à produção de livretos. Ela serve também para economizar papel. Quando estiver trabalhando com rascunhos ou documentos para sua própria leitura, imprima duas páginas por folha. Não é nada, não é nada, você consome a metade do papel!
Impressão frente e verso Quer imprimir um documento em frente e verso, usando apenas os recursos do Word? Comande Arquivo/Imprimir e, na caixa onde está a opção-padrão Todas as Páginas do Intervalo, escolha Páginas Ímpares. Depois, pegue todas as páginas impressas e coloque-as outra vez na bandeja de papel, para imprimir o outro lado. Agora, acione outra vez Arquivo/Imprimir e escolha Páginas Pares. 23
Maiúsculas e minúsculas Depois de escrever o título de um documento em letras maiúsculas e minúsculas, você decide que seria melhor colocá-lo todo em maiúsculas. Então, o que faz? Seleciona o texto e procura o comando Formatar/Maiúsculas e Minúsculas? Está certo, mas há alternativas de teclado: acione Ctrl+Shift+A para deixar a seleção toda em maiúsculas. O mesmo comando desfaz a formatação. Outra solução similar é Shift+F3, que troca os caracteres todos de caixa alta para baixa, ou vice-versa.
Um pincel que pinta formatos Muita gente vê aquele pincelzinho na barra de ferramentas e não sabe direito para que ele serve. O pincel copia formatos. Digamos que existe em seu documento um parágrafo com tipo e tamanho de letra, ajuste de margens e outras definições. Você quer aplicar em outro parágrafo os mesmos padrões. Coloque o cursor no parágrafo-modelo e clique no botão Pincel. O cursor do mouse passa a exibir um pincel. Agora, selecione o parágrafo de destino. Pronto, ele assumiu as definições do outro.
Pincel via teclado Você também pode usar o teclado para copiar e colar formatos, em lugar da ferramenta pincel. Para isso, coloque o cursor no parágrafo cuja formatação você deseja copiar e acione simultaneamente as teclas Ctrl+Shift+C. Agora, selecione o parágrafo de destino e tecle Ctrl+Shift+V. Note a semelhança desses comandos com Ctrl+C (Copiar) e Ctrl+V (Colar). A vantagem da operação com o mouse é exatamente a dica visual representada pelo cursor em forma de pincel. Em compensação, é preciso “armar”o pincel a cada aplicação de formato, enquanto a cópia pelo teclado pode ser aplicada seguidamente a diferentes parágrafos.
Interface monodocumento Até a versão 97, o Word era um aplicativo do tipo MDI – sigla, em inglês, de Multiple Document Interface, ou seja, interface de múltiplos documentos. No Word 2000 isso mudou. Agora, o programa é monodocumento. Claro que você continua podendo abrir diversos documentos de uma só vez. Mas cada um deles ocupa uma cópia – instância, como dizem os programadores – do Word. Portanto, quando se executa o comando Janela/Organizar Tudo, os vários clones ativos do Word dividem entre si o espaço disponível na tela do PC. Antes, eles compartilhavam a tela do Word. Como são várias cópias do programa, a melhor maneira de passar para outro documento é clicar no ícone dele na barra de tare24 fas do Windows.
Fechar e abrir Como o Word 2000 adotou a interface monodocumento (veja a dica anterior), você não encontra mais, no menu Arquivo, a opção Fechar. Então, ao fechar o documento, você fecha com ele a instância do programa que o contém. Há apenas uma exceção para esse comportamento. É quando somente um documento está aberto. Nesse caso, o Word exibe, à direita da barra de menus, um botão com um X, que fecha apenas o documento, mantendo o programa ativo. Quando o Word se encontra nessa situação – nenhum documento ativo –, o primeiro arquivo aberto vai-se alojar na instância ativa do programa. Os seguintes assumirão janelas próprias.
Temas da Web Na versão 2000, o Word e todos os outros programas do Office tornaram-se ferramentas da Web. Um dos sinais disso no Word é a possibilidade de gerar documentos com figuras de fundo e estilos próprios – os chamados temas –, similares às páginas da Web. Para escolher um tema, acione Formatar/Tema. A opção se aplica mesmo que sua intenção seja gerar um arquivo DOC tradicional, e não um documento HTML.
Arquivos utilizados No menu Arquivo, o Word – assim como numerosos outros programas – apresenta uma lista dos últimos arquivos editados. Como padrão, aparecem ali apenas os quatro últimos arquivos. Se você quiser que esse número seja outro – por exemplo, cinco, seis ou oito –, acione Ferramentas/Opções e traga para o primeiro plano a orelha Geral. Na caixa, junto à Lista de Arquivos Utilizados, indique o número desejado. Você pode também não querer que essa lista seja mostrada. Nesse caso, desmarque a caixa Lista de Arquivos Utilizados.
Fontes TrueType incorporadas Você escreve um documento, no qual capricha na formatação e usa uma fonte TrueType bonita instalada em seu micro. Só que sua intenção é enviar esse documento para outra pessoa, que possivelmente não tem essa fonte em seu micro. Se for assim, todo o seu esforço de designer vai por água abaixo. E então? Calma, dá-se um jeito. Acione Ferramentas/Opções/orelha Salvar e ative o item Incorporar Fontes TrueType. Com isso, a fonte vai ficar embutida no próprio documento. O destinatário, portanto, vai poder ver o texto exatamente como você o está vendo agora. 25
F I G U R A 3 . 1 0 Temas no estilo da Web, outra novidade do Word
2000
Listas numeradas Ao trabalhar com listas numeradas, sempre que você abre um novo parágrafo, o Word abre um novo item da lista. Mas há situações em que é preciso abrir um parágrafo sem criar novo item. Para isso, em lugar de Enter, digite Shift+Enter. Quando você voltar a usar Enter, a lista continuará.
Ir para onde? Num documento longo, para fazer o Word saltar rapidamente para uma determinada página, acione Editar/Ir Para, digite o número da página e clique no botão Ir Para. Se quiser um caminho mais curto, dê um duplo clique na divisão da barra de status que exibe os números da linha e da coluna em que o cursor está localizado.
Substituição inteligente
26
No Word 2000, é possível substituir uma expressão no texto pelo conteúdo da Área de Transferência do Windows. Veja como. Copie para a Área de Transferência (Editar/Copiar, ou Ctrl+C) o objeto que substituirá o texto. Esse objeto pode ser texto ou imagem. Em seguida, acione o comando Editar/Substituir (Ctrl+U). Na caixa Localizar, digite o texto a ser substituído. Na caixa Substi-
tuir Por, digite ^c. Isso equivale ao conteúdo da Área de Transferência. Exemplo de situação em que esse recurso pode ser útil: sua empresa opera em São Paulo e no Rio de Janeiro, sendo que alguns serviços ela oferece somente numa das duas cidades. Você está escrevendo um guia desses serviços e quer incluir, ao lado da descrição de cada um, um pequeno ícone de São Paulo ou do Rio. Então, no lugar onde deve ficar o ícone, escreva, por exemplo: img_SP e/ou img_RJ. Depois, coloque os ícones na memória e comande Editar/Substituir. Clique no botão Mais e desligue todas as caixas de verificação. Por fim, clique em Substituir Tudo. Um lembrete: para evitar erros, marque o lugar para as imagens com códigos que não corram o risco de ser confundidos com alguma parte do texto.
Linhas rápidas Existem várias maneiras rápidas de traçar uma linha horizontal. Todas elas estão definidas como formatos automáticos do Word. Basta digitar uma seqüência de sinais iguais e teclar Enter. Veja a tabela a seguir: SEQÜÊNCIA DE... (+Enter) PRODUZ Hifens (- - -)
Linha fina
Sublinhados (___)
Linha grossa
Sinais de igual (===)
Linha fina dupla
Til (~~~)
Linha ondulada
Cercas (###)
Uma linha grossa entre duas finas
Asteriscos (***)
Linha interrompida, formada por quadrados
EXEMPLO
~~~~~~~~~~~~~~~~~
Tamanho da fonte No Word 97 ou 2000, não é necessário ir à caixa Tamanho da Fonte, na barra de ferramentas, para redimensionar as letras. Selecione o texto desejado e acione Ctrl+Shift+>, repetidamente, para aumentar a fonte, ponto a ponto; e Ctrl+Shift+
Descrição de formatos Para saber rapidamente as formatações de parágrafo e caractere que estão aplicadas num documento, acione Shift+F1. O ícone do cursor se transforma numa seta combinada com um sinal de interrogação. Clique em qualquer ponto de um parágrafo e o Word apresenta uma caixa de diálogo com a descrição completa dos formatos de fonte e parágrafo, aplicados ao trecho escolhido. Para desativar o recurso, acione Esc.
Adeus, sobrescrever Durante a digitação, você esbarra na tecla Ins e, sem perceber, liga o modo Sobrescrever (o quadrinho SE fica ativado, em preto, na barra de status). Daí em diante, o que você digita passa a substituir o que já estava escrito. Tempo perdido, porque é necessário apertar Ins (ou dar um duplo clique no quadro SE) e acionar o botão Desfazer até retomar a situação anterior. Para evitar esses pequenos acidentes, desligue definitivamente a possibilidade de acionar Ins de forma inadvertida. Acione Ferramentas/Personalizar e clique no botão Teclado. Agora, na caixa Categorias, escolha Todos os Comandos e, em Comandos, selecione Sobrescrever. Por fim, selecione Insert em Teclas Atuais e, em seguida, o botão Remover. Feche as duas caixas de diálogo ativas.
Gravação rápida? Fuja dela! Seguro morreu de velho. Por isso, acione Ferramentas/Opções/orelha Salvar. Nela, ligue as opções Criar Sempre Backup, Permitir Gravações em Segundo Plano e Salvar Info de AutoRecuperação a Cada 10 Minutos (o número você define). No entanto, desligue a caixa Permitir Gravação Rápida. Essa opção tem mais chances de gerar documentos corrompidos. 29
F I G U R A 3 . 1 2 Descrição de formatos para fontes e parágrafos
Os três últimos passos Você acabou de editar um trecho lá no meio de um documento longo e voltou para o início dele. Quer voltar àquele trecho? Acione Shift+F5. Esse comando, emitido sucessivamente, devolve o cursor aos três últimos locais de edição. Funciona, inclusive, para arquivos já gravados em disco e recém-abertos. Ao abri-los, tecle Shift+F5 e retorne ao último ponto editado.
O cão e a raposa Muitas vezes você está produzindo o layout de um documento e precisa preenchê-lo com texto falso. Então, ou você perde tempo criando um texto qualquer ou abre um documento, copia um trecho dele e cola-o repetidamente no documento-layout. Existe uma forma muito mais rápida de fazer isso. Escreva: =rand(2,5) Depois, tecle Enter. Epa, o texto foi escrito automaticamente! É isso. Esse comando usa a frase “A ligeira raposa marrom ataca o cão preguiçoso.” Os algarismos entre parênteses indicam, respectivamente, o número de parágrafos e as vezes em que a frase se repete em cada parágrafo. Essa dica funciona tanto no Word 2000 como no 97.
30
Entrelinhamento Quer mudar, num piscar de olhos, o entrelinhamento do texto no Word? Pressione Ctrl+1, para espaço simples; Ctrl+2, para espaço duplo; e Ctrl+1.5, para a distância de uma linha e meia.
F I G U R A 3 . 1 3 Cadastre figuras como entradas de
AutoCorreção
Assinatura digital A AutoCorreção é um recurso que vai além do texto: ela também trabalha com figuras. Se você está escrevendo um documento no qual precisa, repetidamente, incluir uma mesma imagem como vinheta, faça o seguinte. Insira a figura no texto e selecione-a. Em seguida, comande Ferramentas/AutoCorreção. Uma cópia da imagem aparece na caixa Por da tela AutoCorreção. Na caixa Substituir, digite a expressão que deve ser trocada pela vinheta. Crie uma seqüência de caracteres incomum (por exemplo, “vt_jazz”) para evitar substituições indesejadas. Agora, digite vt_jazz nos lugares onde desejar colocar aquela vinheta. Com esse procedimento, você pode também cadastrar, como entrada de AutoCorreção, sua assinatura digitalizada.
31
Configurações e personalizações Uma das coisas mais corriqueiras é encontrar usuários do Word reclamando que o programa não se comporta da maneira esperada. Em geral, essas queixas se voltam contra as numerosas automatizações do programa. Como ponto de partida, deve-se ter em mente que todo procedimento automático, com certeza, tem uma forma de ser desligado. É interessante, por exemplo, que o processador de texto corrija as frases não iniciadas com letra maiúscula. No entanto, há situações em que o usuário quer, conscientemente, começar as sentenças com letra minúscula. Nesses momentos, a automação, em vez de ajudar, atrapalha. Para esses casos, você tem sempre duas alternativas. Se a situação é passageira, uma típica exceção, acione Ctrl+Z (desfazer), logo após a intervenção do Word, e anule a correção indesejada. Mas você também pode concluir que aquele item de correção automática não será bem-vindo em nenhum momento. Portanto, a solução é desativá-lo de forma permanente. No Word, em qualquer das versões mais recentes, a desativação de ações automáticas, assim como a maioria das configurações do comportamento do programa, encontra-se em alternativas do menu Ferramentas – notadamente na caixa de diálogo Opções. Vejamos, a seguir, alguns problemas comuns nessa área e como resolvê-los.
1. Os objetos de desenho não são impressos – Acione Ferramentas/Opções/orelha Imprimir. Certifique-se de que a caixa Objetos de Desenho esteja ligada.
2. O Word demora muito na hora de imprimir – Em Ferramentas/Opções/orelha Imprimir, confira se está ativada a caixa Impressão em Segundo Plano. Ligada, essa opção garante impressão mais rápida. Com ela, o Word transfere todo o documento para um arquivo temporário no disco rígido. Feita a transferência, o processador de texto fica livre para outros trabalhos. Em segundo plano, o gerenciador de impressão do Windows entra em ação e vai liberando aquele arquivo conforme a velocidade da impressora. Isso ocorre, mesmo quando há mais de um trabalho esperando para ser impresso (a chamada fila de impressão, ou spool). Se a impressão em segundo plano não estiver ativa, o trabalho é enviado diretamente para a impressora, sem a intermediação do gerenciador. Assim, o Word só fica liberado depois que a última página do documento é processada pela impressora.
32
Atenção: esse problema pode, também, ter uma raiz localizada fora do Word. A impressora, no Windows, pode estar configurada para comunicação direta com o micro, sem a intervenção do gerenciador (spooling). Para ajustar isso, vá ao Painel de Controle, acione Impressoras e ponha em primeiro plano a orelha Detalhes. Agora, clique no botão Configuração do Spool e ative a opção Colocar os Trabalhos em Spool para que a impressão termine mais rapidamente.
3. O Word imprime uma página a mais com o resumo do documento – Muitos usuários se queixam de que o programa os força a desperdiçar uma folha de papel ao imprimir cada documento. Para eliminar isso, acione Ferramentas/Opções/ orelha Imprimir e desligue o item Propriedades do Documento.
4. Verificação ortográfica – Como você sabe, o Word sublinha em verme-
lho as palavras digitadas com erro ou as desconhecidas em seus dicionários. Para desativar a verificação ortográfica, acione Ferramentas/Opções/orelha Ortografia e Gramática e desligue a caixa Verificar Ortografia ao Digitar. Também é possível fazer isso somente para o documento atual: Ferramentas/Opções/Ortografia e Gramática, caixa Ocultar Erros de Ortografia Neste Documento.
5. Alinhamento do texto e das linhas de tabela – Ao passar do Word 6.0/95
para o Word 97 ou 2000, você pode enfrentar problemas com o alinhamento do texto dentro das células em tabelas. Tipicamente, a tabela se mostra normal na tela, mas desencontrada ao ser impressa. Para corrigir isso, acione Ferramentas/Opções, orelha Compatibilidade. Em Opções Recomendadas Para, escolha a versão do Word com a qual as tabelas funcionavam corretamente. Em seguida, ligue a opção Ajustar a Altura da Linha à Altura da Grade na Tabela. Há ainda numerosas outras opções. Experimente com elas e veja os resultados.
Outra fonte de configurações e procedimentos automáticos é o item AutoCorreção do menu Ferramentas. Se você não deseja que endereços da Web ou de correio eletrônico sejam automaticamente associados a hyperlinks, desligue, na orelha AutoFormatação, os itens Substituir/Internet e Caminhos de Rede por Hyperlinks. Vários outros ajustes podem ser feitos em Opções ou em AutoCorreção. Esses dois itens são principais “suspeitos” de automações indesejadas. Procure lá e, na maioria dos casos, a resposta estará com eles. Veja também outras soluções que podem ajudá-lo a aproveitar melhor o Word no próximo capítulo deste livro, que reúne dezenas de dicas sobre como obter melhores resultados no uso do processador de texto.
33
4 Fique rico na bolsa de tempo Invista um minuto e ganhe milhões (de minutos) em produtividade A idéia deste livro, conforme foi mostrado no texto de apresentação, não é ensinar o leitor a usar o Word. Presume-se que quem se aproximou deste volume já tenha razoável prática com o programa. A intenção, aqui, é destacar aspectos do Word que normalmente passam despercebidos para o usuário – e, em alguns casos, até mesmo para os mais curiosos. Tenho certeza de que esses aspectos meio esquecidos, quando passam a fazer parte do dia-a-dia do usuário, podem contribuir diretamente para elevar sua produtividade – seja para atingir objetivos profissionais, seja apenas para satisfação pessoal. Em qualquer caso, o ponto central é extrair muito mais da ferramenta que está aí, instalada em sua máquina. Neste capítulo, vamos analisar alguns itens básicos para o uso mais profundo e mais produtivo do Word. Avançando dos temas mais simples para os mais complexos, vamos passar por itens como modelos, menus e barras de ferramentas, formulários e macros. Vamos visitar – ou revisitar, para quem já conhece – os conceitos básicos de alguns recursos do Word que serão fundamentais para a compreensão do que virá nos próximos capítulos. Portanto, o que você vai encontrar aqui não são ainda os exercícios práticos de desenvolvimento, mas o está-
gio preparatório para chegar a eles. Estamos apenas aquecendo os motores para então entrar no Visual Basic for Applications, VBA. Mesmo para quem não pretende entrar no território da programação, existem vantagens em prestar atenção nesses aspectos do Word. Usar recursos como estilos e modelos representa um investimento. Gasta-se um quarto de hora aqui e ganha-se esse espaço de tempo multiplicado por milhões, no dia-a-dia. É uma bolsa de valores sempre em alta, e a moeda em jogo é o tempo. Como já disseram que tempo é dinheiro, aplique nesses recursos e torne-se um milionário...
Estilos Já tive a oportunidade de receber textos de usuários do Word de longa data, nos quais os parágrafos eram definidos à mão. Ou seja, para estabelecer o recuo indicativo do parágrafo, o autor acionava a tecla de espaço um certo número de vezes. Pensando tecnicamente, isso representa um absurdo. Afinal, a função básica de um programa como o Word é facilitar o trabalho, o que inclui a automação de tarefas desse tipo. Só que, por falta de curiosidade ou de aviso, muita gente não aproveita o potencial da incrível ferramenta que tem nas mãos. Um recurso do Word que resolveria o problema dos parágrafos definidos com a barra de espaço são os estilos. O que é isso? Estilo é um conjunto de formatos reunidos num padrão. Esses formatos podem abranger: fontes, parágrafos, tabulações, bordas, idiomas, molduras e numeração. Digamos que você sempre escreve relatórios nos quais é preciso – ou você quer – manter um padrão. Por exemplo, letra Courier New, corpo 12; parágrafo com linhas justificadas, espaço entre linhas equivalente a 1,5 linha e recuo de 2 cm na primeira linha. Pelo caminho mais longo, toda vez que iniciar um novo
F I G U R A 4 . 1 Caixa de diálogo para criar e modificar estilos
35
relatório, você será obrigado a navegar pelos menus e fazer todos esses ajustes. Ao contrário, se você definir um estilo, fará essa operação apenas uma vez. Nas outras, simplesmente escolherá o estilo, que vai trazer, automaticamente, todos os formatos desejados. Para criar um estilo, acione o comando Formatar/Estilo e clique no botão Novo. Na tela Novo Estilo, dê um nome ao estilo a ser criado e clique na caixa Adicionar ao Modelo. Isso garante que o estilo será armazenado e poderá ser usado em outros documentos. Caso contrário, ele será válido apenas para o documento atual. Agora, clique no botão Formatar e escolha o que pretende definir: fonte, parágrafo etc. Repita esta última operação para especificar outras características, como idioma. Ao concluir as definições e acionar OK, o novo estilo passará a fazer parte da lista existente na caixa de ferramentas Formatação. Para acioná-lo, basta escolhê-lo no início da escrita de um documento. Ou, então, selecionar um texto inteiro, ou parte dele, e aplicar o estilo. Em certos casos, é possível criar estilos encadeados. Observe, na tela Novo Estilo (ou Modificar Estilo – que é a mesma, em outra situação), a presença da caixa Estilo para o Parágrafo Seguinte. Normalmente, ela é preenchida com o próprio estilo que está sendo criado ou modificado. Se você indicar ali outro estilo, o que acontece? Sempre que abrir um novo parágrafo, o Word o ajustará para o estilo indicado. Você deve estar pensando: muito bem, e onde isso pode ser útil? Um exemplo são as teses e outros escritos acadêmicos. Em geral, o autor da tese costuma apresentar o próprio texto em parágrafo e caracteres normais e as citações que faz de outros autores num parágrafo com grande recuo à esquerda, letras em itálico e menor espaçamento entre linhas. Veja, na Figura 4.2, um exemplo esquemático desses dois estilos.
F I G U R A 4 . 2 Exemplo dos estilos usados em teses: texto e 36
citação
Numa tese, o usuário poderia criar um estilo e batizá-lo, por exemplo, como Texto Principal. Para as citações, criaria outro, chamado Citação. Este poderia ter Texto Principal na caixa Estilo para o Parágrafo Seguinte. Assim, concluída uma citação, quando o usuário acionasse Enter, o próximo parágrafo cairia automaticamente em Texto Principal. Embora simples, esses exemplos mostram bem as vantagens de quem aproveita melhor os recursos do Word. O conceito de estilo pode ser aplicado, por exemplo, para mudar o padrão do parágrafo com que o Word trabalha. Em geral, esse padrão corresponde a um parágrafo alinhado à esquerda, com espaçamento de uma linha e caracteres Times New Roman, no tamanho 10 pontos. Para mudá-lo, basta acionar Formatar/Estilo, escolher o estilo Normal e clicar no botão Modificar. Você pode querer transformar em padrão outra fonte e outro esquema de parágrafo. As definições de estilo feitas no Word são gravadas no modelo Normal, contido no arquivo Normal.dot. No Word 97, esse arquivo fica num subdiretório do Office. No 2000, ele foi transferido para \Application Data\Microsoft\ Modelos, um subdiretório do Windows. Isso significa que, se você apagar o arquivo Normal.dot, perderá os estilos personalizados que criou e passará a ter apenas os estilos que vêm no Word, em situação idêntica à do momento da instalação.
Modelos Ao lado do estilo, o modelo é outro conceito fundamental para a compreensão de como funciona o Word. Mais amplo que o estilo, o modelo representa uma espécie de matriz para a criação de documentos. Veja um exemplo. No dia-a-dia de um escritório, é muito comum a emissão de documentos idênticos, em formato e em conteúdo. Para escrever um memorando, pode-se abrir um documento anterior, já enviado, modificar o que for necessário (data, nome do destinatário etc.), e salvá-lo com outro nome. Para evitar isso – que, além de dar trabalho, seria uma fonte de erros –, criou-se o modelo. No exemplo do memorando, o modelo pode conter todos os dados básicos já preenchidos e todas as formatações. Ele é salvo como um arquivo DOT (o T vem de template – modelo, gabarito, em inglês). Quando se cria um documento novo a partir de um modelo, tem-se uma cópia fiel do original, sem afetar a matriz que permanece inalterada para servir de base a outros documentos idênticos. Num escritório comercial ou de advocacia, por exemplo, preenchem-se todos os dias vários contratos de conteúdo idêntico. Num contrato de aluguel, por exemplo, mudam apenas os nomes do locador e do locatário, além do endereço do imóvel. Eis aí uma aplicação típica para um modelo. Contido num modelo, o contrato já está pronto: falta apenas preencher os dados variáveis. Mais adiante, neste volume, vamos mostrar como automatizar o preenchimento de documentos desse tipo.
37
Como você já deve ter percebido, o modelo vai mais longe que o estilo, porque abraça o documento como um todo, e não apenas definições de formato. Portanto, o modelo pode estabelecer o tamanho e a orientação da página (retrato, paisagem), cabeçalho, rodapé, ilustrações e todo o conteúdo básico do documento. Embora os modelos sejam tão abrangentes, criar um modelo é uma das operações mais desconcertantemente fáceis que existem no Word. Pegue um documento qualquer e acione Arquivo/Salvar Como. Dê um nome para o documento e, na caixa Salvar Como Tipo, escolha Modelo do Documento (*.dot). Clique em OK, e pronto: você acabou de criar um modelo. Isso leva a uma definição sumária: modelo, no Word, é qualquer documento que se salve como modelo. Não existe exagero nesta frase: ela vale até mesmo para um documento vazio. O Word admite dois tipos de modelos: o global e o temporário. Global é aquele cujas características estão disponíveis durante todo o tempo. O temporário, ao contrário, só oferece seus recursos quando um documento baseado nele está aberto no Word. O Normal.dot é um exemplo de modelo global. Tudo que ele contém – estilos, macros etc. – pode ser utilizado a qualquer momento. No entanto, os recursos de modelos como o Memorando Elegante – um dos gabaritos que acompanham o Word – só podem ser usados se um documento-clone estiver aberto no momento. Você pode transformar qualquer modelo em recurso global. Basta copiá-lo para a pasta Iniciar, dentro do diretório do Office. No Word 2000, o diretório correto é C:\Windows\Application Data\Microsoft\Word\Inicialização. A localização exata muda com a versão do programa e com o tipo de instalação do Office ou do Word que você possa ter feito. Em todo caso, a indicação encontra-se em Ferramentas/Opções/orelha Arquivos, item Inicialização. Existe uma diferença sutil entre abrir um modelo e abrir um novo arquivo baseado nesse modelo. Para abrir um modelo, acione Arquivo/Abrir e aponte para o arquivo do modelo, um DOT. Você faz isso quando pretende modificar o modelo. Para abrir um novo arquivo com base num modelo, acione Arquivo/Novo e indique, na caixa de diálogo, o modelo que deseja usar. Outra forma é, no Windows Explorer, dar um duplo clique no arquivo DOT. O Word cria, automaticamente, um novo clone daquele modelo. Documentos e modelos do Word 97 e do 2000 têm ainda um ponto comum muito importante no contexto deste livro: ambos podem conter macros. Ou seja, programas em Visual Basic for Applications (VBA), em geral destinados a automatizar tarefas repetitivas.
Menus e barras de ferramentas Os menus e barras de ferramentas representam um dos recursos do Word que mais oferecem campo para a criatividade do usuário. É possível criar novos me38 nus e barras de ferramentas, modificar os existentes, ocultar botões, redefinir
comandos – enfim, fazer uma festa completa. Aqui, nossa intenção é destacar algumas operações fundamentais para quem pretende desenvolver ou simplesmente usar macros. Vamos ver como criar, incluir ou excluir botões na barra de ferramentas, como criar ou eliminar opções de menu, e como associar macros a botões ou comandos de menu. As operações com menus e barras de ferramentas passam todas pelo comando Personalizar. Há várias maneiras de acioná-lo. Uma delas é clicar com o botão direito do mouse em qualquer menu ou barra de ferramenta e, no menu pop-up, escolher Personalizar. Com isso, abre-se a caixa de diálogo Personalizar. Outro caminho é acionar o menu Ferramentas, opção Personalizar. Aprenda este procedimento: ele se repetirá diversas vezes nos próximos passos.
F I G U R A 4 . 3 Tela Personalizar: edição de menus e barras de
ferramentas
Criar uma barra de ferramentas Para criar uma nova barra de ferramentas, o primeiro passo é abrir a caixa de diálogo Personalizar. Agora, traga para o primeiro plano a orelha Barras de Ferramentas e clique no botão Nova. Digite o nome da barra de ferramentas – digamos, Teste – e acione OK. Uma pequena barra flutuante aparece na tela. Vamos, a seguir, agregar botões a essa barra de ferramentas.
Incluir um botão numa barra de ferramentas Para instalar um novo botão numa barra de ferramentas, exiba outra vez a tela Personalizar. Desta vez, selecione a orelha Comandos. Neste exemplo, vamos integrar à barra de ferramentas Teste um comando interno do Word que não é representado por um botão nas barras de ferramentas clássicas do programa. Trata-se do comando Configurar Página, pertencente ao menu Arquivo.
39
F I G U R A 4 . 4 Para instalar o botão, arraste a macro para a barra
Em Personalizar/Comandos, selecione Arquivo na caixa Categorias. Na janela à direita, localize o item Configurar Página e arraste-o para a barra Teste. Feche a janela e experimente o novo botão. O procedimento seria idêntico se, em vez de instalar esse comando na barra Teste, você o arrastasse para uma barra de ferramentas do Word. Nesse caso, a operação resultou muito simples porque escolhemos um comando do Word para o qual já existia internamente um ícone associado. Vamos agora a um exercício um pouco mais complexo. Volte à tela Personalizar/Comandos. Escolha AutoTexto em Categorias e arraste o item Atenciosamente para a barra Teste. Agora, o que apareceu lá não foi um ícone, mas a expressão “Atenciosamente,”. Podemos deixá-la como está (seria um botão apenas com texto) ou modificá-la.
40
F I G U R A 4 . 5 Menu pop-up para alterar botão de comando
Volte à tela Personalizar/Comandos e clique no botão Atenciosamente, em Teste. Agora, clique em Modificar Seleção. No menu que surge, você tem numerosas opções. Veja algumas: a) Você pode mudar a legenda que aparece no botão. Basta editar a caixa Nome. b) Outra alternativa é escolher Imagem e Texto. Isso permite criar um botão que reúne um ícone e uma legenda. c) A alternativa Estilo Padrão dá lugar apenas para um ícone. d) Alterar Imagem do Botão exibe uma coleção de ícones. Escolha um para o botão. e) Editar imagem do botão abre um editor de ícones, no qual você pode desenhar um botão, pixel a pixel. f) Copiar Imagem do Botão permite que você clique em outro botão e copie o ícone dele. g) Colar Imagem do Botão completa a operação do comando anterior quando aplicada ao botão de destino.
F I G U R A 4 . 6 Opções de imagens para a face dos botões
Como criar um novo menu e suas opções Ative Personalizar/Comandos e escolha o último item de Categorias, Novo Menu. Agora, arraste o item Novo Menu, da caixa Comandos para a barra de ferramentas Teste. Em seguida, selecione a categoria Todos os Comandos e arraste alguns itens para Novo Menu, na barra de ferramentas.
41
Excluir um botão ou item de menu A eliminação de qualquer item de menu ou da barra de ferramentas também passa pela tela Personalizar. Basta abrir essa caixa de diálogo e arrastar o item até ela. Outra forma de eliminar um botão ou o menu inteiro (não vale para item de menu) é pressionar a tecla Alt e, ao mesmo tempo, arrastar com o mouse o botão ou menu para fora da barra onde se encontra. Dessa forma, é possível excluir, até mesmo, os menus e botões do próprio Word.
Excluir uma barra de ferramentas Na tela personalizar, orelha Barra de Ferramentas, marque o objeto que deseja apagar e acione o botão Excluir.
Formulários Os formulários constituem uma forma de trabalhar com documentos protegidos contra modificações. Normalmente, eles são um modelo de documento provido de campos para digitação. Como o texto é protegido, o usuário só pode escrever nesses campos. A criação de um formulário começa com a barra de ferramentas Formulários (Exibir/Barras de Ferramentas). Essa barra oferece vários botões, com campos, como caixa de texto (ou campo de formulário texto, como o Word denomina), caixa de seleção e caixa de combinação. Assim, além de digitar, o usuário pode marcar com um tique ou escolher opções numa lista.
F I G U R A 4 . 7 Barra de ferramentas Formulários
Para o formulário, escreve-se o documento normalmente e, depois, inserem-se os campos nos locais onde devem entrar as variáveis. Veja, na Figura 4.8, um exemplo bem simples. Mas o formulário só funciona como tal depois de protegido. Para isso, acione Ferramentas/Proteger Documento e escolha Formulários. A senha é opcional. Salve o arquivo como um modelo, feche-o e abra, em seguida, um documento baseado nesse modelo. Observe o documento: as partes cinzentas são os campos. Para adicionar opções na caixa de combinação, durante o desenho do formulário, clique nela com o botão direito e acione Propriedades. Uma caixa de diálogo se abre, com espaço para você digitar as opções desejadas. Tente alterar o texto fora dos campos – não é possível. Para fazer novas edições no formulário, é necessário voltar 42 ao menu Ferramentas e acionar Desproteger Documento.
F I G U R A 4 . 8 Exemplo de um formulário para digitação
Os formulários podem ser usados para o preenchimento e automatização de documentos mais complexos, como pedidos comerciais. O usuário digita os produtos, quantidades e preços e o documento calcula os totais. Embora os formulários de digitação direta sejam um recurso engenhoso, sua tendência é perder espaço para aplicações escritas em Visual Basic for Applications (VBA), adotado pelo Word a partir da versão 97. As possibilidades oferecidas por essa linguagem são infinitamente superiores. Eis porque não vamos nos estender aqui sobre a elaboração de formulários. Na segunda parte deste volume, você vai encontrar vários projetos que automatizam o preenchimento de documentos a partir do VBA.
Macros Desde os primeiros tempos da microcomputação, alguns aplicativos mais sofisticados oferecem ao usuário meios para que ele possa automatizar seqüências de tarefas repetitivas, ou expandir as funções existentes nesses aplicativos. Normalmente, as macros são o recurso usado para isso. No Word para Windows, a primeira linguagem de macro foi o WordBasic, presente no aplicativo até a versão 7.0 (ou 95). A partir da versão seguinte, a 8.0 (ou 97), o WordBasic foi substituído pelo Visual Basic for Applications (VBA), linguagem de macros que representa um subconjunto da linguagem Visual Basic. Como em qualquer outro aplicativo, o VBA no Word tem dois grandes blocos de comandos. Um vem diretamente da linguagem Visual Basic. Trata-se dos itens genéricos da linguagem, como sua sintaxe e comandos básicos. O outro bloco reúne os objetos específicos do Word – ou de outro aplicativo que tenha incorporado o VBA. Para entender melhor a diferença entre o núcleo genérico e o núcleo específico do VBA, vejamos um exemplo. Tanto o Word como o Excel trazem embutido o VBA como ferramenta de programação. Em qualquer um dos dois aplicativos – assim como no Access, no PowerPoint e em mais de uma centena de outros programas do mercado –, o código abaixo vai funcionar do mesmo modo, exibindo ao usuário a mensagem mostrada na Figura 4.9. Sub Mensagem() MsgBox “Bem-vindo ao VBA.”, vbOKOnly, “Mensagem” End Sub
43
F I G U R A 4 . 9 Mensagem ao usuário
Isso acontece porque o comando MsgBox faz parte do núcleo básico do Visual Basic. No entanto, você não vai obter sucesso se tentar rodar no Excel uma macro escrita no Word, fazendo referências a parágrafos, notas de rodapé ou páginas com duas colunas. Claro, esses são objetos específicos do processador de texto, não existem no Excel. Do mesmo modo, é inútil tentar rodar no Word um código que envolva objetos típicos do Excel, como planilha ativa ou célula A23. Portanto, em cada aplicativo o VBA junta a linguagem comum, Visual Basic, a comandos, funções e propriedades específicos do aplicativo. Isso significa que mesmo o programador experiente em Visual Basic precisa familiarizar-se com os objetos privativos de qualquer programa que tenha adotado o VBA.
VBA rápido e prático Se alguém lhe dá uma macro para Word (97 ou 2000), como você faz para integrá-la ao seu programa? Vejamos. Você pode receber apenas o texto do programa. Exemplo: um amigo lhe fornece o texto abaixo e diz que ele abre a Calculadora do Windows, a partir do Word. O que você faz? Public Sub Calcular() On Error GoTo Calc_Err Dim lngX As Long Dim Caminho As String, Programa As String Caminho = “c:\windows\calc.exe” Programa = “Calculadora” If Tasks.Exists(Programa) = True Then Tasks(Programa).Activate Else lngX = Shell(Caminho, vbNormalFocus) End If Tasks(Programa).WindowState = wdWindowStateNormal Calc_Fim: 44
Exit Sub Calc_Err: MsgBox Err.Description Resume Calc_Fim End Sub
Em primeiro lugar, no Word, acione Alt+F11. Abre-se a janela do Visual Basic. Vá ao menu Inserir e escolha Módulo. Na janela à esquerda, surgirá um item chamado Módulo1 (se já houver um Módulo1, será Módulo2 etc.). Copie para a memória o texto do programa e cole-o na janela maior. Salve e volte para o Word (Alt+F11), de novo. A macro está lá, mas como você vai fazer para executá-la? Você poderia ir ao menu Ferramentas/Macro/Macros. A opção Calculadora está lá. Selecione-a e clique no botão Executar. Perfeito, não? Mas facilitemos as coisas: que tal se você puder chamá-la a partir de um botão na barra de ferramentas? Vamos lá. Clique em Ferramentas/Personalizar. Ponha em primeiro plano a orelha Comandos e localize Macros, na caixa Categorias. A macro Normal.Módulo1.Calculadora aparece na lista da direita.
F I G U R A 4 . 1 0 Tela Personalizar: para instalar uma macro
num botão
Arraste a linha da macro para uma barra de ferramentas onde haja espaço para mais um botão. No início, o botão vai ficar enorme. Na caixa Personalizar, clique em Modificar Seleção e escolha Estilo Padrão. Agora, clique de novo em Modificar Seleção e selecione Alterar Imagem de Botão. Escolha um ícone (existe um que é uma calculadora), e pronto. Agora, você pode acionar a Calculadora sem sair do Word. (O texto desta macro, para você não precisar digitá-lo, está no arquivo Calculadora.txt, no diretório Capítulo 6, do CD-ROM.) 45
5 Muito prazer, VBA Uma breve apresentação do Visual Basic for Applications Se você nunca se aproximou do Visual Basic, em nenhuma de suas formas, neste capítulo terá oportunidade de travar um contato inicial com essa linguagem. O Visual Basic, somadas todas as suas modalidades, é hoje a ferramenta de desenvolvimento mais usada no mundo inteiro. Para essa popularidade do VB, concorre, em primeiro lugar, o fato de ser uma linguagem de estrutura ágil e acessível ao aprendizado. É muito mais fácil – e rápido – aprender VB que qualquer outra linguagem de programação. Outro fator que impulsiona o Visual Basic para a posição que ocupa hoje no mercado é, inegavelmente, a poderosa influência da Microsoft. Segundo a empresa, o Visual Basic conta hoje com mais de 3 milhões de programadores, que utilizam: n o Visual Basic completo – o pacote da linguagem, vendido como produto independente; ou n a linguagem de macro Visual Basic for Applications (VBA), presente em numerosos aplicativos, como Word, Access e Excel, da própria Microsoft, além de títulos de outras 150 empresas; ou n a linguagem de scripts VBScript, embutida no Windows – 98, NT 4.0 e 2000 – que serve para implementar tarefas de automação nesses sistemas operacionais ou em páginas da Web.
Assim, o Visual Basic é uma espécie de esperanto, dentro da plataforma Windows. Ele está em toda parte: em aplicativos de escritório (CorelDraw, Corel WordPerfect, nas versões mais recentes), traçadores de fluxogramas (Visio), programas de engenharia (AutoCAD) e até em softwares de gestão, como o R/3, da SAP. Para mais informações sobre o VBA, consulte o site da linguagem: http://msdn.microsoft.com/vba.
Um pouco de história Criado, em 1964, pelos americanos John Kemeney e Thomas Kurtz, o Basic é uma ferramenta concebida desde o início com o objetivo de facilitar o aprendizado. Aliás, isso está expresso no próprio nome da linguagem, que significa “básico”, mas na verdade é um acrônimo de Beginner’s All-purpose Symbolic Instruction Code – algo como código simbólico de instrução para principiantes. A caracterização como linguagem para principiantes representou, ao mesmo tempo, um bem e um mal para o Basic. O lado positivo está na facilidade de aprendizado, que se mantém até hoje, mais de 35 anos depois da primeira formulação da linguagem. Mas a combinação das expressões “fácil”e “para principiantes” conduziu também à idéia de uma linguagem de pouca profundidade, voltada apenas para exercícios breves, sem maior conseqüência. Isso mudou com a ascensão do Visual Basic, cuja primeira versão apareceu no mercado em 1992. A Microsoft, que fora responsável, nos anos 70, por trazer o Basic dos computadores de grande porte para os micros, lançava, nos anos 90, o primeiro Basic para o ambiente gráfico Windows, que então começava a decolar. Esse Basic para Windows trazia algumas novidades de peso. A primeira, claro, era o aspecto visual. Podia-se desenvolver aplicações, com os mesmos procedimentos usados para desenhar com o Paintbrush (antecessor do Windows Paint): escolher um controle numa caixa de ferramentas e usá-lo para traçar objetos nas tela. Outro aspecto pioneiro do Visual Basic era o nível de expansibilidade. Além de trazer controles embutidos (caixa de texto, caixa de combinação, barras de rolamento etc.), o programa aceitava a agregação de controles externos, adquiridos no mercado. Assim, mesmo os programadores que não tinham condições técnicas de elaborar objetos mais sofisticados podiam usá-los, sem grande dificuldade. Em muitos casos, tudo que era preciso saber sobre esses objetos era escolher características numa caixa de propriedades. Esses controles externos foram, primeiro, os VBX (Visual Basic Extensions, de 16 bits), e, depois, os OCX, de 32 bits. A idéia revelou-se tão poderosa que os controles OCX tornaram-se um padrão, adotados por várias outras linguagens, como C/C++, Delphi etc. Do ponto de vista econômico, os VBX e OCX propiciaram o surgimento de um ramo especializado da indústria de software: os fabricantes de componentes, essas peças e partes inteligentes que po-
47
dem ser incluídas num projeto para acelerar e facilitar seu desenvolvimento. Depois da Internet, os OCX evoluíram, passando a se chamar objetos ActiveX e podendo ser incluídos em projetos isolados, em rede local ou ligados à Web. Outra faceta da expansibilidade do Visual Basic está no acesso quase irrestrito que a linguagem pode ter à API (funções internas) do Windows. Com isso, os programadores mais avançados podem estender o poder de fogo da linguagem, referindo-se diretamente a objetos e procedimentos existentes na própria estrutura do sistema operacional. Hoje, o Visual Basic encontra-se na versão 6.0. Ao longo do tempo, o produto foi incorporando novidades, entre as quais se destacam a capacidade de gerenciar bancos de dados. Nesse caso, o programa cedeu sua linguagem ao Access e tomou emprestado deste o sistema de gerenciamento de dados. Isso ocorreu na versão 3.0 do Visual Basic. Outra aquisição importante foram os recursos de programação orientada a objeto, presentes no VB desde a versão 4.0.
VB, VBA e VBScript Quais as diferenças entre Visual Basic, Visual Basic for Applications e VBScript? VBA e VBScript são subconjuntos do Visual Basic. O VB é a linguagem plena, com todos os recursos para o desenvolvimento de aplicações comerciais e gráficas. O VBA, por sua vez, inclui grande parcela dos recursos do VB, mas tem uma limitação fundamental: ele não gera aplicativos autônomos. Pode, na verdade, produzir soluções complexas, mas elas funcionam somente dentro do programa hospedeiro: por exemplo, Word, Excel. O VBScript representa um subconjunto de recursos ainda menor. Trata-se de uma linguagem para a automação de procedimentos na plataforma Windows e em páginas Web. Ainda com relação às diferenças, é importante notar que toda aplicação VBA funciona nos moldes de uma caixa de diálogo do programa hospedeiro. Isso equivale a dizer que, em geral, só é possível retomar o trabalho normal com o programa-pai depois de encerrada a aplicação. Neste parágrafo, já usei duas vezes a palavra aplicação VBA. Antes, todo código escrito com uma linguagem embutida se chamava macro. Mas, com o nível de complexidade atingido pelo VBA, é possível criar verdadeiras aplicações. Elas abrem bancos de dados, escrevem arquivos, manipulam recursos do Windows e têm condições de acessar a Internet. Portanto, embora o termo macro esteja correto, os programas VBA são muito mais que isso.
Com você, o VBA
48
Muito bem, você está aí com seu Word 97 ou 2000. Tenha o prazer de conhecer o VBA. Para isso, acione Ferramentas/Macro/Editor do Visual Basic. Ou, mais curto, Alt+F11. Abre-se o ambiente de programação do Visual Basic. Para voltar ao Word, acione de novo Alt+F11.
F I G U R A 5 . 1 Janela do VBA: quatro subdivisões principais
O ambiente de desenvolvimento do VBA compõe-se de algumas divisões fundamentais, com as quais você se depara logo no primeiro contato. Além do menu e da barra de ferramentas, a janela divide-se em cinco partes (todas ocultáveis via menu Exibir): n
Janela Explorer de Projeto, ou Janela de Projeto – Exibe os todos os elementos que fazem parte de um ou mais projetos: formulários, módulos e módulos de classe.
n
Janela Propriedades – Lista as definições de propriedades de um objeto selecionado num formulário.
n
Janela de Verificação Imediata ou, simplesmente, Janela Imediata — Espaço auxiliar de programação. A partir desta janela, pode-se verificar o valor de uma variável, executar programas no ambiente VBA ou fora dele, consultar o valor de constantes intrínsecas. Enfim, uma janela de extrema utilidade. Com o tempo, você aprenderá a tirar bom proveito dela.
n
Janela de Código ou de Formulário – Trata-se da maior subdivisão do conjunto. Nela estão reunidos todos os módulos de formulário e módulos de código disponíveis.
n
Janela Pesquisador de Objeto – Lista toda a hierarquia de objetos do Word e do VBA, com suas funções e sintaxe de acesso. O Pesquisador de Objeto é uma das fontes mais importantes para o entendimento das relações entre recursos do Word. À medida que você for se aprofundando mais no uso do VBA, mais vai perceber a importância dessa janela. Para acessá-la, acione Exibir/Pesquisador de Objeto ou simplesmente F2.
49
Naturalmente, como se trata de um ambiente de programação, o VBA tem ainda uma série de outras janelas, como a Caixa de Ferramentas, que reúne os controles (caixa de texto, etiquetas, caixas de listagem etc.) com o quais se desenham os formulários. Mas, por enquanto, fiquemos por aqui. Nossa intenção, neste volume, não é fazer uma descrição completa de cada canto escondido do VBA. Ao contrário, a abordagem é prática: vamos falar deste ou daquele item à medida que ele se tornar necessário para o desenvolvimento de uma solução concreta. Essencialmente, mostraremos aqui os componentes básicos de um projeto VBA e como criá-los. Observação importante – Neste livro, sempre que escrevermos Visual Basic, VB, VBA, estaremos nos referindo a esse ambiente ou à linguagem que lhe dá suporte. Quando o objeto da citação for o Visual Basic – a linguagem como produto separado –, isso ficará claro no texto.
Partes de um projeto VBA Todas as partes que compõem um projeto VBA estão contidas em módulos. Há três tipos de módulos: formulários, módulos de código (ou apenas módulos) e módulos de classe. Os formulários (ou, em inglês, forms) são as telas dos aplicativos. Eles abrigam caixas de texto, caixas de combinação, barras de rolamento e outros objetos que compõem a interface do Windows. Ao longo deste livro, usaremos, indiferentemente, os termos formulário e form. Em alguns casos, você verá, de forma redundante, formulário (form) ou formulário (UserForm) . Aí, a intenção é diferenciar o formulário de programação, do VBA, do formulário-documento do Word. Cada formulário possui uma parte visível, que são as telas, e outra invisível, que abriga código de programação. Naturalmente, os termos visível e invisível são aqui empregados do ponto de vista do usuário: para você, programador, tudo é visível. Os módulos propriamente ditos abrigam apenas código de programação. Os módulos de classe são idênticos nesse aspecto. A única diferença está em que os módulos de classe são utilizados somente em programação orientada a objeto. Como se cria um formulário? É simples. Basta selecionar o projeto desejado e acionar o comando Inserir/UserForm. Por que selecionar um projeto? Porque você pode criar formulários no projeto Normal (que fica gravado no modelo Normal.dot e disponível para todos os projetos) ou em qualquer outro documento ou modelo personalizado. Inserido o formulário, ele, geralmente, assume o nome UserForm1, UserForm2 etc. Para dar a ele um nome personalizado, clique no form e vá à janela Propriedades. Lá, escreva o novo nome na linha Name. Em geral, para facilitar o reconhecimento, os formulários recebem, no 50 nome, o prefixo frm: frmCores, frmAtributos.
F I G U R A 5 . 2 Módulo de programação, com destaque para a
janela Declaração
A inserção de módulos e de módulos de classe é semelhante à de formulários. Basta recorrer ao menu Inserir. Formulários, módulos e módulos de classe têm, em cada projeto, seu armazenamento próprio. Na janela Projeto, eles ficam dentro de pastas separadas. Isso ajuda a organizar os componentes. Os módulos de programação – de puro código ou a janela de código associada aos formulários – têm aspecto idêntico. Trata-se de janelas para edição de texto, encimadas por duas caixas de combinação que exibem os nomes Geral e Declaração. Nos formulários, a caixa Geral exibe a lista dos controles (caixas de texto, rótulos, botões de comando etc.). Quando se seleciona um desses controles na caixa Geral, a outra caixa, Declaração, passa a exibir a lista dos eventos associados ao controle em destaque. Nos módulos de puro código, a caixa Geral, normalmente, não tem conteúdo, enquanto Declaração lista as subs e funções pertencentes ao módulo.
Caixa de ferramentas e controles Outro item importante no ambiente de programação do Visual Basic é a Caixa de Ferramentas. Nela estão os controles que você deve arrastar para o formulário. Para tornar a caixa de ferramentas visível, acione o comando Exibir/Caixa de Ferramentas. Mas, coerentemente, isso só ocorre quando um formulário tem o foco. Se você não consegue distinguir o controle pelo ícone que o identifica, passe o cursor do mouse sobre ele e aparecerá uma legenda pop-up. O VBA traz catorze controles embutidos. Veja abaixo a caracterização básica de cada um. As expressões entre parênteses correspondem aos nomes dos controles como vão aparecer no código VBA. 51
F I G U R A 5 . 3 Caixa de ferramentas
52
CONTROLE
DESCRIÇÃO
Rótulo (Label)
Caixa de texto que não pode ser editada pelo usuário. Neste livro, pelo hábito de lidar com versões antigas do Access e com todas as versões do Visual Basic, esses controles são chamados, indiferentemente, de rótulos, etiquetas ou labels.
Caixa de texto (TextBox)
Espaço no formulário reservado para digitação. Pode ter uma ou múltiplas linhas.
Caixa de combinação (ComboBox)
Controle que combina (daí o nome) uma caixa de texto e uma caixa de listagem.
Caixa de listagem (ListBox)
Caixa que exibe informações textuais, uma em cada linha.
Caixa de seleção (CheckBox)
Pequeno controle que pode ser ligado ou desligado com um clique do mouse. Ativado, ele apresenta o sinal de um tique. No VBA atual é chamado de caixa de seleção, mas a própria Microsoft já chamou esse controle de caixa de verificação.
Botão de opção (OptionButton)
Também conhecido como botão de rádio, esse controle, normalmente, é utilizado em grupos e dentro de uma moldura. Qual a diferença entre um grupo de botões de opção e um grupo de caixas de seleção? No primeiro, somente uma alternativa pode ser escolhida. No outro, pode-se marcar uma, nenhuma ou várias alternativas.
Botão de ativação (ToggleButton)
Talvez uma tradução mais correta para esse controle fosse botão de comutação. Ele ativa ou desativa uma característica. Essencialmente, tem papel semelhante ao da caixa de seleção. Se ele se mostrar pressionado, a opção está ativa.
Moldura (Frame)
Em geral, esse controle não faz nada. Apenas ajuda a organizar a interface – por exemplo, reunindo em si um conjunto de botões de opção ou caixas de seleção.
CONTROLE
DESCRIÇÃO
Botão de comando (CommandButton)
Normalmente, um clique nesse controle dispara uma operação. É, com certeza, o objeto mais popular de todas as interfaces gráficas.
Faixa de tabulação (TabStrip)
Embora tenha funções diferentes, esse controle é quase sempre substituído pelo Multipágina.
Multipágina (Multipage)
Implementa, num formulário, o recurso encontrado no Windows e no próprio Word: as caixas de diálogo com múltiplas orelhas, ou guias.
Barra de rolagem (ScrollBar)
Representa um controle deslizante como os que se usavam antigamente em aparelhos de som para aumentar ou baixar o volume. Conforme você a desenhe, assume posição horizontal ou vertical.
Botão de rotação (SpinButton)
Botão com duas setas, uma para cima e outra para baixo, usado em combinação com caixas de texto ou rótulos nos quais é preciso incrementar ou reduzir um número. Um exemplo desse objeto está no ajuste do ano, na opção Data/Hora do Painel de Controle do Windows.
Imagem (Image)
Objeto para suporte de figuras no formulário.
Além dos controles nativos, o ambiente do VBA aceita a incorporação de outros disponíveis no Windows. Um exemplo disso é mostrado no Capítulo 33, na aplicação Prazo Certo. Seu formulário principal inclui o objeto calendário, que vem no pacote do Office Professional, tanto na versão 97 como na 2000. Esse controle contém em si toda a inteligência do calendário gregoriano e facilita não somente as consultas, como também as entrada de dados e as operações baseadas em datas.
Propriedades e eventos Formulários e objetos apresentam propriedades e eventos. As propriedades correspondem a características do objeto, como comprimento (Width), altura (Height) ou Cor de fundo (BackColor). Algumas propriedades são comuns a quase todos os controles, como a posição horizontal, ou esquerda (Left), e a posição vertical (Top). A referência, no caso, é o canto superior esquerdo do formulário, que representa a posição (0, 0). Observe que, nesse caso, a coordenada vertical, ao contrário do que você aprendeu sobre o plano cartesiano, cresce de cima para baixo (Figura 5.4).
53
F I G U R A 5 . 4 Coordenadas no formulário
Os eventos correspondem a ocorrências que afetam o objeto enquanto o programa está em execução. São eventos, por exemplo, as seguintes ações: n
o clique do mouse num botão de comando ou num formulário;
n
o pressionamento de uma tecla;
n
a recepção do foco da ação por uma caixa de texto;
n
o duplo clique do mouse numa caixa de listagem.
Nesses exemplos, todos os eventos são provocados pelo usuário. No entanto, também existem eventos que não dependem diretamente do usuário. É o caso de Initialize e Terminate, que ocorrem respectivamente quando um formulário é ativado e quando ele está sendo desativado e excluído da memória. A cada evento pode corresponder um trecho de código, indicando o comportamento que o programa deve assumir diante daquela situação específica. Se você tem, por exemplo, um botão chamado cmdMensagem, a rotina cmdMensagem_Click representa o que o programa deve fazer quando ocorrer um clique no botão. Se a intenção é exibir um aviso, a rotina ficará assim: Private Sub cmdMensagem_Click() MsgBox “Você clicou no botão Mensagem.” End Sub
E o resultado, na prática, será a exibição de uma caixa de mensagem:
F I G U R A 5 . 5 Caixa de mensagem 54
Cada controle tem um evento-padrão e uma propriedade-padrão. O evento-padrão você identifica da seguinte maneira. Num formulário, desenhe uma caixa de texto. O nome dela, antes de você personalizá-lo, será TextBox1. Dê um duplo clique nesse objeto. Abre-se a janela de código do formulário, no evento-padrão. Para caixas de texto e barras de rolagem, o padrão é Change. Para botões de comando, figuras Image e formulários, o evento-padrão é Click. O método para identificar a propriedade-padrão também é simples. Para definir uma propriedade, usa-se o nome do objeto e o nome da propriedade, separados por ponto, e o valor. Por exemplo, para ocultar uma caixa de texto chamada txtValor: txtValor.Visible = False
A propriedade-padrão é aquela assumida pelo objeto sem necessidade de nomeá-la. Cada controle tem sua propriedade padrão. Para caixas de texto, ela é a propriedade Text. Portanto, em lugar de escrever txtValor.Text = “250,56"
você pode indicar, direto: txtValor = “250,56"
Veja, a seguir, as propriedades-padrão de alguns controles:
CONTROLE
PROPRIEDADE-PADRÃO
Caixa de texto
Text
Rótulo, ou etiqueta
Caption
Botão de opção
Value (True/False)
Procedimentos do Visual Basic Procedimento é uma seqüência de instruções executadas como uma unidade. Por isso mesmo, um procedimento não pode estar contido em outro. Além disso, todo código executável deve estar dentro de um procedimento. Há dois principais tipos de procedimentos no Visual Basic: Sub (abreviação de subprograma ou sub-rotina) e Function. As diferenças entre Sub e Function são sutis. Mas, para uma distinção inicial, vale considerar que a Sub executa ações, mas não retorna um valor. Diferentemente, o procedimento Function sempre retorna um valor. Em outras palavras, você recorre a um procedimento sub quando deseja que um conjunto de 55
ações seja realizado; e a um procedimento Function quando precisa obter uma resposta. À sub você diria: “imprima o documento”. À function, perguntaria: “qual o valor das contas a pagar para o mês de agosto?” e receberia o valor como resposta. Toda sub começa com a instrução Sub e termina com End Sub. Da mesma forma, um procedimento function começa com Function e termina com End Function. Todos os procedimentos associados a eventos do Visual Basic são do tipo Sub.
Tipos de variáveis Ao desenvolver um aplicativo, você lida com diferentes tipos de variáveis. Algumas são números, outras são texto e outras se referem a objetos do sistema, como controles ou programas. Nas versões atuais, o Visual Basic e o VBA (não o VBScript) reconhecem os tipos de dados mostrados na tabela a seguir.
TIPO DE DADOS
TAMANHO DE ARMAZENAMENTO
INTERVALO
Byte
1 byte
de 0 a 255
Boolean
2 bytes
True ou False
Integer (inteiro)
2 bytes
de -32.768 a 32.767
Long (número inteiro longo)
4 bytes
de -2.147.483.648 a 2.147.483.647
Single (vírgula flutuante de precisão simples)
4 bytes
de –3,402823E38 a -1,401298E-45 para valores negativos; de 1,401298E-45 a 3,402823E38 para valores positivos
Double (vírgula flutuante de dupla precisão)
8 bytes
de –1,79769313486231E308 a -4,94065645841247E-324 para valores negativos; de 4,94065645841247E-324 a 1,79769313486232E308 para valores positivos.
Currency (número inteiro em escala)
8 bytes
de -922.337.203.685.477,5808 a 922.337.203.685.477,5807
Decimal
14 bytes
+/-79.228.162.514.264.337.593.543.950. 335 sem vírgula decimal; +/-7,9228162514264337593543950335 com 28 casas decimais à direita; o menor número diferente de zero é +/-0,0000000000000000000000000001.
56
TIPO DE DADOS
TAMANHO DE ARMAZENAMENTO
Date
8 bytes
De 1 de janeiro de 100 a 31 de dezembro de 9999
Objeto
4 bytes
Qualquer referência Object
String (comprimento variável)
10 bytes + comprimento da seqüência
De 0 a, aproximadamente, 2 bilhões
String (comprimento fixo)
Comprimento da seqüência
De 1 a, aproximadamente, 65.400
Variant (com números)
16 bytes
Qualquer valor numérico até o intervalo de um Double
Variant (com caracteres)
22 bytes + comprimento da seqüência
O mesmo intervalo de String de comprimento variável
INTERVALO
Os valores indicados na coluna Tamanho de Armazenamento referem-se ao espaço consumido para guardar cada tipo de variável. Para obter o melhor resultado em suas aplicações, é preciso que você diga ao VBA a qual tipo pertence cada variável. A identificação das variáveis se faz com a instrução Dim: Dim strNome As String Dim intContador As Integer
Para que não pareça uma palavra vinda do nada, Dim provém de dimensionar. Aliás, por causa disso usa-se “declarar uma variável” ou “dimensionar uma variável”. O termo dimensão faz sentido, porque as variáveis têm tamanho. Primeiro, pelo espaço que ocupam em disco ou na memória. Depois, por causa de sua extensão. No Visual Basic, assim como em outras linguagens, o espectro dos números inteiros (Integer) varia de -32.768 a 32.767. Portanto, se sua variável deve abrigar números além desses limites, você não pode declará-la como inteiro (As Integer). Caso contrário, haverá um estouro de capacidade. Naturalmente, você também não pode querer somar variáveis numéricas com variáveis de texto. Tudo isso parece muito óbvio, mas confusões desse tipo são muito comuns entre os novatos em programação. Para reduzir as possibilidades desse tipo de erro, uma das primeiras coisas que você deve fazer, ao começar a programar com Visual Basic, é acionar, no ambiente do VBA, o comando Ferramentas/Opções e ligar a alternativa Requerer Declaração de Variável. O que é isso? Com essa opção ligada, se você usar uma variável não declarada, o VBA produzirá um erro. Também será um erro se você declarar uma variável chamada intControle e depois referir-se a ela como intContorle, trocando letras na di- 57
gitação. Sem aquela opção ligada, o VBA assume que essa segunda forma é outra variável – o que conduzirá a falhas em seu programa. Aliás, em muitos casos, essas são as falhas mais difíceis de detectar, porque estão justamente onde você não procura. Além disso, você passa e repassa a rotina e não vê a troca de letra. Há ainda uma última desvantagem quando você não explicita suas variáveis. O programa pode não conter erros, mas vai rodar de forma mais lenta, já que o VB terá um trabalho extra convertendo e identificando variáveis. Para resumir a questão dos tipos de variáveis, podemos classificá-los em seis grandes grupos:
GRUPO
TIPOS
Variáveis Numéricas (Inteiros)
Byte Boolean Integer Long
Variáveis Numéricas (Vírgula flutuante)
Single Double Currency Decimal
Variáveis Numéricas (Data)
Date
Variáveis-Objeto
Objeto
Variáveis de Texto
String (Comprimento variável) String (comprimento fixo)
Variants
Variant (texto) Variant (número)
Se você observar direitinho, vai perceber que esses seis grupos na verdade podem ser resumidos em três: variáveis numéricas, variáveis de texto e de objetos. No entanto, as subdivisões dos dois primeiros tipos são necessárias para definir as dimensões das variáveis. As variáveis do tipo Variant merecem atenção especial. Como o nome sugere, elas pertencem a um ramo mutante, que pode assumir a condição de texto, número, data/hora ou objeto. Quando você não declara o tipo de uma variável, este é automaticamente definido como Variant. Aí está mais um problema. Um valor que, por exemplo, poderia ocupar apenas um byte (variável Byte) passa a ocupar até 22 bytes. Também assumem a condição de Variants as variáveis declaradas sem tipo explícito. Por exemplo: 58
Dim Valor
É claro que existem motivos para a existência desse tipo de variável. No entanto, eles não são muito comuns. Por isso, faça um esforço especial para indicar claramente o tipo da variável. Mesmo que seja Variant.
Escopo das variáveis Além da classificação por tipos, as variáveis também se definem conforme a extensão de sua validade dentro de um projeto. Uma aplicação envolve formulários e módulos. Dentro de cada um desses contêineres existem ainda os procedimentos, notadamente as funções e as sub-rotinas. Esquematicamente, pode-se representar um projeto com o esboço mostrado na Figura 5.6. Os quadrados e círculos são, respectivamente, subs e funções. Algumas variáveis são declaradas (Dim strNome As String) dentro de um procedimento (sub ou função), e têm validade apenas nesse espaço. Outras são dimensionadas da mesma forma, na área de declarações do módulo ou formulário. Essas têm alcance mais abrangente: valem em todo o espaço do módulo ou formulário. Um exemplo: a variável strNome está declarada como válida para todo um módulo. Num procedimento qualquer, atribui-se a ela o valor “Teresa”: strNome = “Teresa”
Agora, esse valor pode ser obtido ou redefinido com base em qualquer procedimento do módulo. Há outro tipo de variáveis ainda mais abrangente. Trata-se daquelas que são declaradas a partir de um módulo ou formulário e marcadas com a instrução Public. Por exemplo: Public strNome as String
Essa variável pode ser lida ou modificada a partir de qualquer procedimento do projeto, pertença ele a um módulo ou a um formulário. Para simplificar, podemos associar o projeto a um país; os formulários e módulos aos Estados; e os procedimentos aos municípios. Assim, fica fácil entender que existem variáveis de alcance municipal, estadual e federal. De modo semelhante, existem subs e funções que só valem dentro de seus módulos ou formulários. Essas são marcadas com a instrução Private. Um procedimento do tipo private (privado), pertencente ao módulo A, não pode ser chamado de qualquer código do formulário B. No entanto, se esse procedimento for declarado como Public, será visto de qualquer outro procedimento do projeto. Portanto, existem também procedimentos estaduais e federais. Para que uma macro, contida num módulo de programação, apareça na lista de macros da caixa Ferramentas/Macro/Macros, é preciso que ela seja declarada como pública. Outro detalhe importante: só se consideram macros os
59
F I G U R A 5 . 6 Esquema de um projeto VBA: os quadrados são
subs; os círculos, funções
procedimentos públicos do tipo sub – funções não. Vale lembrar, também, que um procedimento público fica disponível para todos os projetos carregados no ambiente. Mediante o comando Arquivo/Exportar Arquivo, você pode salvar, em disco, formulários, módulos e módulos de classe. Pode, também, fazer a operação inversa com o comando Arquivo/Importar Arquivo. Os arquivos correspondentes a formulários têm a extensão FRM; os de módulo, a extensão BAS (de Basic); e os de classe, a extensão CLS. Cada arquivo FRM se faz acompanhar de outro arquivo com o mesmo nome e extensão FRX. Este contém informações binárias (não textuais) do formulário. Portanto, para transportar um formulário, você precisa levar os dois arquivos, FRM e FRX. Mas, na hora de importar, basta apontar para o FRM – o outro é carregado, automaticamente.
A ajuda pop-up e outras ajudas O ambiente de programação do Visual Basic representa hoje o que há de mais amistoso para o desenvolvedor. Um de seus pontos de destaque é a ajuda pop-up, que literalmente salta aos olhos. Quando por exemplo, você vai declarar uma variável, basta escrever Dim Valor As... e uma caixa com todas as possibilidades da variável aparece na tela. Do mesmo modo, quando você vai chamar uma função ou sub – inclusive aquelas que você mesmo criou em seu programa –, o VBA mostra uma informação com todos os parâmetros pedidos pelo procedimento. Mas o usuário não precisa se restringir ao auxílio que não é solicitado. Há também formas explícitas de pedir ajuda. Se você tem dúvida sobre um objeto, uma propriedade ou um método, clique em cima do nome dele com o botão direito do mouse. Surge um menu de contexto com uma série de opções. 60
F I G U R A 5 . 7 Ajuda pop-up durante a digitação
F I G U R A 5 . 8 Outra forma de ajuda: os parâmetros de uma
função
F I G U R A 5 . 9 Menu de contexto
Escolha Listar Propriedades e Métodos, e a janela pop-up aparecerá. Nessas condições, praticamente não há como errar. Com essa lista à disposição, fica difícil você fazer referência a um método ou propriedade inexistente. Outras alternativas úteis são Informações Rápidas, Listar Constantes e Informações do Parâmetro. Destaque especial merece a opção Pesquisador de Objeto, que abre a janela de mesmo nome e lhe permite pesquisar objetos, métodos e propriedades. Para pesquisar, digite a expressão desejada na segunda caixa de combinação, no canto superior esquerdo da janela, e acione Enter. Pronto: você tem aí uma referência completa do objeto procurado, do projeto em que você está trabalhando e de todas as bibliotecas disponíveis no ambiente. 61
F I G U R A 5 . 1 0 Propriedades e métodos
F I G U R A 5 . 1 1 O Pesquisador de Objeto: referência completa
sobre o ambiente
Segurança no VBA Uma das diferenças entre as versões 2000 e 97 do Office está na forma como são tratados os documentos que contêm macros. No 97, ao abrir um documento desse tipo no Word ou no Excel, o usuário recebe um aviso de que aquele arquivo pode conter vírus. No 2000, há três níveis de proteção: baixo, médio e alto. Com o nível baixo – absolutamente não recomendado –, seu processador de texto deixa a porta escancarada para a entrada de vírus, especialmente se você não estiver usando um antivírus. O nível médio de proteção é, praticamente, obrigatório se você quiser experimentar com os aplicativos do CD que acompanha este livro. Esse nível estabelece um comportamento idêntico ao da versão 97. Ou seja, o Word vai avisar que o arquivo pode conter vírus e você tem a opção de confirmar ou não a ativação das macros. Quando a segurança está colocada no grau mais alto, o programa, simplesmente, desativa as macros contidas no documento e não dá nenhum aviso. 62
Outro aspecto de segurança está ligado à proteção do código escrito em VBA. Mas este é válido para as duas versões. Se você produziu um programa genial e não quer que ninguém venha bisbilhotar o seu código, proteja-o. No VBA, selecione o projeto desejado e acione Ferramentas/Propriedades de <nome do projeto>. Ou então clique com o botão direito na pasta do projeto e escolha a mesma opção no menu de contexto. Agora, na tela de propriedades, traga para o primeiro plano a orelha Proteção. Nela, marque a caixa Bloquear Projeto para Exibição, depois digite e confirme a senha. A partir desse momento, ao tentar abrir o projeto, o Word exigirá a digitação da senha.
F I G U R A 5 . 1 2 Tela para proteção do código com senha
Muito bem, seu código está a salvo de terríveis espiões e sabotadores. Mas cuidado: não cometa a bobagem de esquecer a senha. Nesse caso, o projeto vai-se tornar secreto até mesmo para próprio autor.
Fontes de estudo Conhecer uma linguagem de programação significa trabalhar corretamente com seus tipos de variáveis e instruções. Só que ninguém consegue isso lendo um livro com a lista completa de comandos e palavras-chave. Uma das formas mais eficazes de aprender é partir de códigos já escritos por outrem. Analisando rotinas prontas e funcionais, você vê, na prática, como os procedimentos funcionam e pode ter idéias de modificá-los, ajustando-os a seus próprios projetos. 63
A melhor fonte de consulta a respeito do VBA é a própria Ajuda do produto. Em geral, as instalações típicas do Office não instalam os arquivos de help para o VBA do Word e dos outros programas da suíte. Portanto, se você pressionar F1 no ambiente do Visual Basic e não obtiver ajuda, volte à instalação do Office e inclua esse item. Para escrever este livro, tive de consultar inúmeras vezes o help do VBA no Word 97 e no 2000. Dá para afirmar que se encontra ali um material bastante completo sobre o Visual Basic, em termos genéricos, e sobre a programação com objetos do Word. Além de estar em português, a documentação contida na Ajuda vem recheada de exemplos – que, como foi dito acima, representam o melhor impulso para o aprendizado. Outra fonte de aprendizado é o site da Microsoft dedicado ao desenvolvimento na plataforma Office: http://msdn.microsoft.com/ officedev/default.asp. Também é válido e recomendável aprender VBA “colando” do Word. Em dúvida sobre qual objeto usar ou qual a sintaxe adequada, mande o processador de texto programar e depois copie dele. Como? Grave uma macro: Ferramentas/Macro/Gravar Nova Macro. A isso podemos chamar, literalmente, de aprender com quem sabe. Para não dizer que não falei de fontes impressas, aqui vai uma pequena seleção de manuais de programação em VBA. Infelizmente, todos eles estão em inglês. Coloquei-os em ordem crescente de profundidade técnica. Os dois primeiros têm a vantagem de ser os manuais “oficiais” da linguagem, produzidos pela própria Microsoft ou sob a supervisão dela. Os dois outros são leitura bastante avançada. Deborah Kurata é considerada a papisa da orientação a objeto no Visual Basic. Seu livro Doing Objects já tem uma versão mais atual, voltada para o VB 6.0. Por fim, o manual de Ken Getz e Mike Gilbert é um produto monumental para quem trabalha com VBA ou com o Visual Basic. Traz centenas de rotinas prontas, feitas com programação de altíssimo nível. Em muitos casos, também orientada a objeto. Microsoft Office 97 Visual Basic Programmer’s Guide Microsoft Press, EUA, 1997 David Shank, Mark Roberts & Tamra Myers Microsoft Office 2000 Visual Basic Programmer’s Guide Microsoft Press, EUA, 1999 Deborah Kurata Doing Objects in Visual Basic 5.0 Ziff-Davis Press, EUA, 1997
64
Ken Getz & Mike Gilbert VBA Developer’s Handbook Sybex, EUA, 1997
Não conheço nenhum livro voltado especificamente para o VBA do Word. As publicações tendem a tratar do Office como um todo – o que faz sentido, já que dificilmente alguém usa uma aplicação de escritório de forma isolada. Mesmo neste livro, em vários projetos trabalhamos com recursos do Access e do Excel.
Quem disse que o nome não importa? No capítulo anterior, demos o exemplo de uma macro para abrir a Calculadora do Windows a partir de um botão de comando numa barra de ferramentas do Word. O nome mais óbvio para essa macro seria Calculadora. No entanto, ela se chama Calcular. Esse nome não foi adotado por acaso. Se você batizar a macro como Calculadora, ela simplesmente não vai funcionar. A macro usa o objeto Tasks para testar se um aplicativo com o título Calculadora está ativo no Windows. Se estiver, em vez de carregar o programa no disco, ela simplesmente traz para o primeiro plano a instância que já está rodando. Mas se a macro se chama Calculadora, ela mesma é uma tarefa ativa que tem o nome procurado. Então, o objeto Tasks é induzido a erro: ele acusa que sim, existe um programa rodando. Com isso, o próximo passo é tentar trazer a Calculadora para a frente – e ela não existe. Mesmo que você chame a macro de AbreCalculadora, o problema continua porque há uma coincidência de parte dessa expressão com a outra. Moral da história: atenção com os nomes de macros.
65
PARTE 2
6 O sistema informa Um programa que mostra dados sobre a máquina, o Windows e o Word Ficha do projeto Projeto: Informações do Sistema O que faz: Apresenta informações sobre o computador, o sistema operacional e o Word. Arquivos e requisitos do projeto: Formulário frmSystemInfo e código interno. Conhecimento técnico: Contato com funções do VBA/Word que fornecem informações sobre o Word, a máquina e o sistema operacional. Nível de programação: Iniciante
Neste capítulo, nossa meta é construir uma caixa de diálogo que forneça informações sobre o Word, o sistema operacional e a máquina. Para isso, vamos usar, fundamentalmente, os serviços do objeto System, disponível no VBA/Word. Esse objeto pode ser bastante útil no desenvolvimento de aplicativos no Word, quando é necessário coletar dados como a resolução de vídeo, a versão do sistema operacional ou a situação de periféricos instalados no micro. A seguir, uma lista com as principais propriedades de System:
70
PROPRIEDADE DE SYSTEM
O QUE FAZ
Application
Retorna o nome do aplicativo, no caso o Word.
Country
Fornece o país do sistema operacional. Corresponde, no Word, ao valor da variável interna wdCountry. Em nosso caso, wdCountry é igual a wdBrazil, ou 55.
FreeDiskSpace
Retorna o espaço livre no disco rígido ativo. Esse valor é fornecido em bytes. Portanto, para facilitar a leitura, pode ser transformado em quilobytes (KB), se dividido por 1024; em megabytes (MB), se dividido por 1024x1024; e em gigabytes (GB), quando dividido por 1024x1024x1024.
HorizontalResolution e VerticalResolution
Informam, respectivamente, a resolução horizontal e a resolução vertical, em pixels, usadas no vídeo.
LanguageDesignation
Retorna o nome do idioma, tal como é indicado pelo sistema operacional.
OperatingSystem
Informa o nome do sistema operacional. Por exemplo, “Windows”, “Windows NT” ou “Windows 2000”.
PrivateProfileString
Retorna ou define uma string armazenada no Registro do Windows ou num arquivo INI. Veja exemplo de uso desta propriedade nos Capítulos 11 a 14 nesta parte do volume.
ProcessorType
Indica o tipo de processador usado na máquina. Por exemplo, “Pentium”.
Version
Retorna o número da versão do sistema operacional. Não confundir com a propriedade homônima do objeto Application, que fornece a versão do Word.
Para usar os recursos do objeto System, vamos construir um formulário (frmSystemInfo) nos moldes da Figura 6.1, mostrada adiante. Trata-se de um formulário com uma moldura (Frame1), seis etiquetas (labels) dentro dessa moldura (labTipoProcessador, labResVideo, labIdioma e labAplicação). Além disso, há os botões cmdFechar, cmdInfoSistema e cmdPainel.
Você deve estar perguntando por que escolhemos usar labels, e não caixas de texto, para receber as informações do sistema. A razão é simples: trata-se de controles apenas para exibir os dados ao usuário – que, naturalmente, não poderá modificá-los. Como padrão, as etiquetas são apresentadas com fundo de cor igual à do formulário. Para facilitar a leitura dos dados, a propriedade BackColor das seis etiquetas foi mudada para branco. No caso, esse ajuste foi feito selecionando-se o controle e ajustando a cor na linha BackColor da caixa Propriedades. No entanto, também seria possível fazer isso em código, indicando na sub UserForm_Initialize: labTipoProcessador.BackColor = vbWhite
A constante vbWhite é um valor interno do Visual Basic e corresponde à cor branca. Existem também vbGreen, vbYellow, vbBlack, vbBlue etc. Cada uma das seis labels de dados traz à esquerda outra etiqueta que a identifica: Processador, Espaço Livre em Disco, Resolução do Vídeo, Sistema Operacional, Aplicação e Idioma.
F I G U R A 6 . 1 O formulário frmSystemInfo em construção
Vejamos, agora, como fazer para que o formulário exiba essas propriedades. Na maioria dos casos, a aplicação é direta. Basta atribuir à etiqueta o valor da propriedade: labTipoProcessador = “ ” & System.ProcessorType
Observe que usamos um espaço antes do valor da propriedade. O objetivo é afastar o texto da borda esquerda da etiqueta, assim como ocorre nas caixas de texto. 71
Para as labels labSistOp e labAplicação, a atribuição dos valores não foi direta. No caso do sistema operacional, a informação é fornecida em dois pedaços: o nome do sistema e a versão. Esta última também não é direta. Se o sistema é “Windows” e a versão é “4.0”, trata-se do Windows 95; se “4.10”, temos então o Windows 98. Com a etiqueta labAplicação, o processo é idêntico. Ela é preenchida pela combinação de duas informações: o nome do programa (“Microsoft Word”) e a versão. Aqui, há um pequeno detalhe ao qual se deve prestar atenção. Estas duas propriedades não pertencem ao objeto System, e sim ao objeto Application, que representa o Word. Outra observação: a propriedade Version, de Application, retorna o número serial do Word: “8.0”, “8.0a”, “9.0”. Aqui, “forçamos a barra” para que a série “8.0” venha acompanhada de “(97)”, que é o nome mais comum da versão. Do mesmo modo, “9.0” é transformado em “9.0 (2000)”. Veja na Figura 6.2 a caixa de diálogo exibindo dados do meu sistema.
F I G U R A 6 . 2 A caixa Informações do Sistema
Faltam, agora, os três botões de comando situados no topo da tela. Sobre o botão Fechar, claro, não é necessário falar. E o que faz o botão Info do Sistema? Ele usa o método MSInfo, do objeto System, que abre o utilitário Informações do Sistema, do Windows, oferecendo um painel completo da máquina, do sistema operacional e dos programas instalados. Para isso, basta uma linha de código: System.MSInfo
O outro botão da caixa de diálogo dá acesso ao Painel de Controle. Aqui, o recurso utilizado não tem nada a ver com o objeto System, do VBA/Word. Para 72 abrir o Painel de Controle, estamos lançando mão da função intrínseca Shell e
F I G U R A 6 . 3 Utilitário Informações do Sistema, aberto a partir
da caixa de diálogo
do programa Rundll32.exe, do Windows, combinado com a biblioteca Shell32.dll. Assim, para abrir o Painel de Controle, usamos a seguinte linha de código: Shell “rundll32.exe shell32.dll,Control_RunDLL”, _ vbNormalFocus
Adicionando alguns parâmetros a essa forma de chamar o Painel de Controle, pode-se abrir, diretamente, alguns dos utilitários do Painel. Por exemplo, para o item Adicionar Novo Hardware, usa-se o parâmetro sysdm.cpl @1: Shell _ “rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1", _ vbNormalFocus
Para abrir a janela Propriedades de Vídeo – o equivalente a clicar com o botão direito no desktop e acionar Propriedades –, basta usar como parâmetro o nome do arquivo Desk..cpl: Shell “rundll32.exe shell32.dll,Control_RunDLL desk.cpl”, _ vbNormalFocus
A regra geral, para esses casos, é usar o comando seguido do nome da aplicação do Painel de Controle, um arquivo com a extensão CPL.
73
F I G U R A 6 . 4 Painel de Controle: chamado a partir do aplicativo
Para ir mais além 1. Observe se a propriedade System.ProcessorType funciona adequadamente em seu sistema. No meu, um Celeron 333, ela fornece “Pentium”. No Painel Meu Computador, a informação está correta. Talvez o sistema do Word não seja tão atualizado quanto o do sistema operacional (Windows 98 SE).
2. As aplicações do Painel de Controle são arquivos com a extensão CPL
(de Control Panel) que se encontram no diretório de sistema do Windows. Liste-as e tente abri-las com o método mostrado acima.
3. Os comandos para abrir o Painel de Controle e seus aplicativos funcionam também na caixa Executar, do Windows, aberta pelo comando botão Iniciar/Executar. Lá, você não precisa de Shell. Basta digitar o comando de forma direta. Exemplo:
rundll32.exe shell32.dll,Control_RunDLL intl.cpl
4. A função Shell, do Visual Basic, é usada em vários outros projetos neste
livro. Veja-a, por exemplo, nos capítulos que mostram como tocar arquivos de som a partir do Word. Para entendê-la melhor, consulte a ajuda do VBA.
5. A propriedade Country (país), do objeto System, não retorna uma
string, mas um número. Se você capturar o valor dela em seu Word, vai obter o número 55, que corresponde a Brasil. Para testar se o país é Brasil, use a seguinte linha:
74
If System.Country = wdBrazil Then
A constante intrínseca wdBrazil é igual a 55 – aliás, o mesmo código do sistema de Discagem Direta Internacional, DDI.
6. Em alguns trechos de código citados como exemplos neste capítulo,
aparecem linhas que terminam com o sinal de sublinhado ( _ ). Esse caractere é usado no Visual Basic para quebrar uma linha lógica muito comprida em duas ou mais linhas de texto. O objetivo é facilitar a leitura, na tela ou na página impressa. Para funcionar corretamente, o sinal de sublinhado deve ter um espaço em branco separando-o do caractere que o antecede.
Objetos exclusivos O objeto System, discutido neste capítulo, é um exemplo de recurso diferente de outros existentes no VBA. Se você tomar, por exemplo, o objeto Application ou o objeto FileSearch, verá que eles se aplicam do mesmo modo no Word, no Excel, no Access (versão 2000) e em qualquer outro programa que incorpore o VBA. No entanto, System funciona apenas no Word. Outro exemplo do mesmo tipo é a coleção Tasks, que reúne todas as tarefas (programas) ativas no Windows em determinado momento.
75
7 Quantos anos você tem? Calcule idades com precisão, baseado na data atual fornecida pelo micro Ficha do projeto Projeto: Cálculo da Idade O que faz: Esta pequena aplicação calcula a idade a partir de dois pontos de referência: uma data de nascimento, fornecida pelo usuário, e a data atual, lida no relógio do sistema. Arquivos e requisitos do projeto: Formulário frmIdade e código interno. Conhecimento técnico: Contato com funções do VBA referentes a data e hora. Nível de programação: Iniciante
O objetivo deste capítulo é simples: mostrar como se calcula a idade com precisão, segundo os padrões de uso comum. Normalmente, a idade das pessoas é uma função que se mantém constante ao longo de um ano e aumenta um ponto na data de aniversário. Por causa dessa característica especial, a determinação da idade muitas vezes representa um problema para usuários de programas de escritório.
F I G U R A 7 . 1 O formulário frmIdade
Basicamente, o cálculo da idade consiste numa subtração entre dois anos: o ano atual menos o ano da data de nascimento. Ou, usando funções do VBA: intIdade = Year(Now) - Year(DataNasc)
Só que, como sabemos, essa operação fornece resultados quase corretos. Tudo depende de a data atual situar-se antes ou depois da data de aniversário. Se a data atual estiver antes do aniversário, você vai afirmar que a pessoa tem por exemplo 27 anos, mas a idade correta será 26. O código para ajustar o cálculo fica assim: intIdade = Year(Now) - Year(DataNasc) If Month(Now) < Month(DataNasc) Then intIdade = intIdade - 1 ElseIf Month(Now) = Month(DataNasc) Then If Day(Now) < Day(DataNasc) Then intIdade = intIdade - 1 End If End If
Depois do resultado (inexato) obtido pela subtração dos dois anos, é preciso acrescentar um If para garantir a correção do número procurado. Aí, o algo-
77
ritmo aplica o raciocínio a seguir. Se o mês atual é menor que o mês de nascimento, então o valor intIdade, obtido na primeira operação, deve ser subtraído de 1. No entanto, se o mês de aniversário e os mês atual são iguais, é preciso verificar se o dia atual situa-se ou não antes do dia de aniversário. Tudo isso está no If acima. Mas esse código pode ser abreviado para algo assim: If Now < DateSerial(Year(Now), Month(DataNasc), _ Day(DataNasc)) Then intIdade = intIdade - 1 End If
Com apenas três linhas, resolve-se tudo. Dito em palavras, o novo algoritmo envolve o seguinte: se a data de hoje (Now) é menor que a data formada pelo dia e mês de nascimento aplicados ao ano atual, então subtrai-se 1 do valor inicialmente calculado. Sem dúvida, essa última solução é bem mais econômica, em termos de código. Esta solução engloba, inclusive, os casos raros em que a data de nascimento é 29/02. Tomado como dia posicional – 29/02 é o 60o dia do ano –, ele corresponde ao dia 01/03 nos anos não bissextos.
Desenho do formulário O projeto para calcular idade envolve apenas um formulário, frmIdade. Além de uma caixa de texto para a digitação da data de nascimento e uma label (rótulo ou etiqueta, como queira) para a apresentação da idade calculada, merece destaque a label labHoje, que indica a data atual, com a legenda: “Hoje é dd/mm/yyyy”. O objetivo de labHoje é dar ao usuário uma pista visual para que ele possa ter certeza de que a idade foi calculada corretamente.
Para ir mais além 1. Os cálculos com variáveis de data e hora são, ao mesmo tempo, simples
e cheios de truques. Embora pareçam fáceis, sempre escondem armadilhas que conduzem a erros. Para dominar esse item, faça exercícios de programação, envolvendo as seguintes funções do VBA:
78
n
Now
n
Date
n
Time
n
Day, Month, Year
n
Hour, Minute, Second
n
DateSerial
n
DateValue
n
WeekDay (dia da semana)
n
Format (aplicado a data e hora)
n
DateDiff (subtração de datas)
n
DateAdd (adição de datas)
n
DatePart (parte da data: ano, mês, dia, semana etc.)
2. Em outros capítulos deste volume, você encontra aplicações de várias
dessas funções. Veja, por exemplo, o próximo capítulo, que trabalha com o cálculo de datas, e o projeto dedicado ao cálculo de prazos comerciais.
Datas na virada do milênio Quando você digita datas com apenas dois dígitos no ano, os aplicativos do Office 97 e do Office 2000, assim como o Windows 98, interpretam essas datas da seguinte maneira. Se o ano varia entre 00 e 29, então o século será o XXI: 2000 a 2029. Os anos seguintes – de 30 a 99 – serão entendidos como pertencentes ao século XX: 1930 a 1999. Esse procedimento facilita as coisas para quem escreve anos com dois dígitos. Mas, para eliminar dúvidas na entrada de dados, o melhor é você ir ao Painel de Controle/Configurações Regionais e definir a data para o padrão dd/mm/aaaa. Isso não muda nada, mas trará mais segurança visual. Pelo menos, quando digitar 15/03/05, o programa mostrará, claramente, que você escreveu 15/03/2005. Esta é uma boa providência a tomar nestes primeiros anos da passagem para o novo século.
79
8 Duas datas e um número Um programa que calcula datas e prazos simples Ficha do projeto Projeto: Cálculo de Datas O que faz: Calcula datas pela adição ou subtração simples de um determinado número de dias. Arquivos e requisitos do projeto: Formulário frmCalculaDatas e código interno. Conhecimento técnico: Operações com funções do VBA referentes a data e hora. Nível de programação: Iniciante
Depois de um projeto que calcula idade a partir da data de aniversário, mostrado no capítulo anterior, vamos agora desenvolver um aplicativo que executa operações aritméticas com datas. O Cálculo de Datas é um aplicativo que trabalha com três variáveis básicas: uma data inicial, um número de dias e uma data final. Flexível, ele trabalha da seguinte maneira: o usuário digita dois dos itens e o programa oferece o terceiro como resultado.
F I G U R A 8 . 1 Cálculo com datas: resultado (Data Final) em azul
Para a montagem deste projeto, comecemos com a construção do formulário, frmCalculaDatas. Ele tem três caixas de texto para a digitação de dados, três botões de comando e mais duas caixas de texto complementares. As primeiras caixas de texto são DataInicial (txtDataIni), DataFinal (txtDataFim) e Número de Dias (txtNumDias). Elas são, na verdade, controles de entrada e saída dos dados, pois qualquer uma pode ser a caixa que apresenta o resultado da operação. As duas caixas auxiliares são txtNomeDiaI e txtNomeDiaF. Elas simplesmente exibem o nome do dia da semana das datas inicial e final. Por fim, vêm os botões de comando. Calcular (cmdCalcular) executa a operação; Fechar (cmdFechar) encerra o programa; e Limpar (cmdLimpar) apaga o conteúdo de todas as caixas de texto, preparando o programa para outro cálculo. Vejamos, agora, o que acontece “embaixo do capô” desse aplicativo. Comecemos do início. Quando o formulário se abre, o valor da caixa txtDataIni é definido como a data atual. Em seguida, põe-se o foco da ação na próxima caixa, txtNumDias: txtDataIni = Date txtNumDias.SetFocus
A primeira linha, acima, está no procedimento UserForm_Initialize. A outra se encontra em UserForm_Activate. Se o usuário não deseja fazer um cálculo com a data de hoje, pode voltar ao campo txtDataIni e digitar nova data. Depois, ele deve clicar no botão Calcular. Então, o processamento começa. Em pri-
81
meiro lugar, a rotina cmdCalcular_Click verifica o estado de preenchimento das três caixas de texto principais. Juntas, elas podem assumir oito situações: 2 estados (cheio, vazio) elevados à terceira potência (3 caixas). Para cobrir essas oito situações, são definidos seis status, identificados pela variável intStatus, que assume valores de 0 a 5: 0 – Dois campos quaisquer estão em branco 1 – Os três campos estão em branco 2 – Somente txtDataFim está em branco 3 – Somente txtNumDias está em branco 4 – Somente txtDataIni está em branco 5 – Os três campos estão preenchidos Nos casos 0, 1 e 5, o usuário recebe uma mensagem de erro. Nos demais, o preenchimento, em princípio, parece correto. Mas é preciso verificar se a data digitada é realmente uma data válida. Isso se faz com a função intrínseca IsDate (umaData), que retorna um valor booleano: True ou False. Se os valores digitados passam no teste, então executa-se o cálculo adequado a cada caso. Se Data Final é o campo em branco, o resultado é determinado com a seguinte linha: txtDataFim =
DateValue(txtDataIni) + Val(txtNumDias)
Para a Data Inicial: txtDataIni = DateValue(txtDataFim) - Val(txtNumDias)
Para o Número de Dias: txtNumDias = DateValue(txtDataFim) - DateValue(txtDataIni)
Todas essas operações baseiam-se na possibilidade de somar ou subtrair datas e números de dias, obtendo datas ou dias. Em cada caso, o resultado no campo-resposta é apresentado em letras de cor azul (vbBlue) e em estilo negrito (Bold). Isso facilita a visualização: o usuário logo vê que o resultado está em destaque. Como se obtém essa cor? Basta chamar a rotina MudaCor, que recebe como parâmetro um número (1, 2 ou 3) que identifica o campo-resposta. Private Sub MudaCor(num As Integer) ‘ Muda ForeColor (cor da letra) do campo-resposta Dim ctl As control Dim ctlDestaque As control 82
‘ Define qual é o campo-resposta Select Case num Case 1: Set ctlDestaque = txtDataIni Case 2: Set ctlDestaque = txtNumDias Case 3: Set ctlDestaque = txtDataFim End Select ‘ Fonte azul, em negrito, no campo-resposta ctlDestaque.ForeColor = vbBlue ctlDestaque.Font.Bold = True ‘ Fonte normal nos outros campos For Each ctl In Me.Controls If Left$(ctl.Name, 3) = “txt” And _ ctl < > ctlDestaque Then ctl.ForeColor = vbBlack ctl.Font.Bold = False End If Next ctl End Sub
Nesta rotina, destacam-se duas variáveis do tipo Control (controle): ctl e ctlDestaque. Elas representam, respectivamente, um controle qualquer do formulário e o controle correspondente ao campo-resposta. Pelo índice recebido, a rotina determina qual dos três controles é ctlDestaque. Para isso usa as instruções Select Case e Set. Em seguida, definem-se as cores. As letras, no campo-resposta, assumem a cor azul e o estilo negrito. Nos demais controles, a cor é o preto (vbBlack) e o estilo, normal (Bold = False). Atenção ao detalhe: para localizar os controles não destacados, procura-se pelos controles cujos nomes comecem com “txt” (as caixas de texto) e que não sejam iguais a ctlDestaque. Falta-nos comentar duas rotinas interessantes: txtDataIni_Change e txtDataFim_Change. Esses procedimentos, como os nomes indicam, definem o que o programa deve fazer quando ocorre uma mudança nas caixas de texto correspondentes. Vejamos o caso de txtDataIni: Private Sub txtDataIni_Change() If IsDate(txtDataIni) Then txtNomeDiaI = Format$(txtDataIni, “ddd”) Else txtNomeDiaI = “” End If txtNomeDiaI.ForeColor = txtDataIni.ForeColor txtNomeDiaI.Font.Bold = txtDataIni.Font.Bold End Sub
83
Enquanto o usuário digita em txtDataIni, essa rotina é acionada a cada nova tecla. Quando o conjunto de caracteres começa a fazer sentido como data, a rotina imediatamente escreve o dia da semana correspondente na caixa de texto à direita: txtNomeDiaI. Se o texto digitado não corresponder a uma data, txtNomeDiaI permanece vazio. O evento Change da caixa de texto também é disparado quando o próprio programa escreve nela um resultado. Nesse caso, a caixa de texto é o campo-resposta e suas letras recebem a cor azul. Para que o nome do dia fique da mesma cor, a propriedade ForeColor de txtDataIni é copiada para txtNomeDiaI. Processo idêntico é feito com a propriedade Font.Bold. Assim, enquanto se digita, os dois controles têm letras pretas. Quando é o programa que escreve, as letras se tornam azuis. O procedimento associado às mudanças em txtDataFim é, absolutamente, idêntico. Quando o usuário aciona o botão Limpar, apaga-se o conteúdo de todas as caixas de texto. Ao mesmo tempo, todas as letras voltam a ser pretas e em estilo normal. Isso prepara o programa para iniciar novo cálculo.
Para ir mais além 1. O principal recurso usado neste projeto para calcular datas foi somar
uma data a um número (de dias). Você pode fazer essa mesma operação com outra função do Visual Basic: DateAdd. A sintaxe de DateAdd é a seguinte: DataFinal = DateAdd (intervalo as string, _ número as Double, DataInicial)
Intervalo é uma string que determina a unidade de tempo considerada. Assim, “d” é dia; “ww”, semana; “m”, mês. O número indica a quantidade de intervalos. Portanto, para adicionar 65 dias à data 15/02/2000, procede-se da seguinte maneira: DataFinal = DateAdd (“d”, 65, #15/02/2000#)
O resultado é DataFinal = 20/04/2000 Observe as cercas (#) em torno do número: elas indicam data. Você também poderia passar esse valor como texto: “15/02/2000”. O resultado seria o mesmo.
2. Consulte na ajuda do Visual Basic a sintaxe de duas funções similares a
DateAdd: DateDiff (diferença entre duas datas) e DatePart, que extrai uma parte (dia, mês ou ano) de uma data.
84
Olho no formato da data Observe as variações dos formatos de datas em algumas funções do Visual Basic. Em DateSerial, por exemplo, a ordem dos componentes de uma data deve ser apresentada da seguinte maneira: DateSerial (ano, mês, dia)
No entanto, quando se faz referência a uma data dentro de uma declaração SQL, ela deve ser sempre apresentada no formato #mm/dd/yyyy#. Por exemplo: SELECT * FROM tabVendas WHERE [DataVencimento] > #07/31/2000#
85
9 Seu Word salva arquivos XMW? Descubra quais arquivos externos seu Word sabe ler ou escrever Ficha do projeto Projeto: Tabela de conversores de arquivos disponíveis no Word O que faz: Prepara uma tabela, mostrando todos os tipos de arquivos que seu Word é capaz de abrir e escrever. Arquivos e requisitos do projeto: Sub-rotina ConversoresDeArquivos. Conhecimento técnico: Manipulação básica da coleção FileConverters (conversores de arquivos) e suas propriedades. Nível de programação: Intermediário
Os conversores de arquivos do Word estão reunidos numa coleção de objetos chamada FileConverters. Dessa coleção fazem parte tanto os filtros usados pelo programa para ler, como para escrever arquivos diferentes de seu padrão (DOC). Alguns arquivos, como os do tipo TXT, podem ser lidos e gravados pelo Word. Outros têm conversão exclusiva para o lado da leitura ou da gravação. Há ainda aqueles que não têm nenhum tipo de conversão. Entre esses, encontram-se os arquivos para os quais não existem filtros e também os arquivos cujos filtros não estão instalados no sistema. Em outras palavras, duas máquinas com a mesma versão do Word podem ter coleções de conversores diferentes. Isso decorre do fato de que os filtros não são todos instalados, automaticamente. A instalação de boa parte deles depende de decisão explícita do usuário. A coleção FileConverters é um objeto hierarquicamente subordinado ao objeto Application, ou seja, ao próprio Word. As referências a objetos da coleção são sempre feitas no padrão: FileConverters(índice)
O índice pode ser uma string identificadora de um tipo de conversor. Exemplo: FileConverters(“MSWordMac”).OpenFormat
Pode, também, ser um número que representa a posição do conversor na coleção. Exemplo: FileConverters(3).FormatName
Os objetos da coleção FileConverters têm uma série de propriedades, das quais as mais importantes estão listadas a seguir:
PROPRIEDADE
TIPO DE VARIÁVEL
O QUE FAZ
CanOpen
Booleano
Indica se o conversor especificado é capaz de abrir arquivos.
CanSave
Booleano
Indica se o conversor foi desenvolvido para salvar arquivos no formato especificado.
ClassName
String
Retorna um nome único que identifica o conversor de arquivos. Exemplos: MSWord6, Lotus123.
Extensions
String
Fornece as extensões de nome de arquivo associadas ao conversor especificado. 87
PROPRIEDADE
TIPO DE VARIÁVEL
O QUE FAZ
FormatName
String
Fornece o nome amigável do conversor. O FormatName corresponde à expressão apresentada na caixa de diálogo Salvar Como, item Salvar Como Tipo.
Name
String
Retorna o nome do arquivo que contém o conversor.
OpenFormat
Long
Número que identifica o tipo de arquivo a ser aberto.
Parent
Objeto
Retorna o nome do objeto-pai do conversor indicado. Esse objeto, no caso, é o Microsoft Word.
Path
String
Fornece a pasta na qual está armazenado o arquivo do conversor especificado.
SaveFormat
Long
Número que identifica o tipo de arquivo a salvar.
O objetivo, agora, é usar a coleção FileConverters e algumas das propriedades acima para construir uma tabela que mostre, em detalhes, todos os conversores instalados no Word. Para isso, será necessário usar apenas um procedimento, a sub-rotina ConversoresDeArquivos. Ela começa abrindo um novo documento e definindo a orientação da página para Paisagem. Isso é feito porque já sabemos que a tabela terá nove colunas, número difícil de acomodar na orientação Retrato: Documents.Add ActiveDocument.PageSetup.Orientation = wdOrientLandscape
As nove colunas vão listar as principais propriedades de cada objeto FileConverter. Elas aparecerão na seguinte ordem: Índice, ClassName, FormatName, Name, CanSave, SaveFormat, CanOpen, OpenFormat e Extensions. Os títulos foram mantidos em inglês porque correspondem, exatamente, ao nome das propriedades. O próximo passo é dar um título para o documento (“Conversores de Arquivos Disponíveis”) e definir os títulos de cada campo da tabela. Vamos usar o método TypeText e inserir os títulos seguidos de uma string de separação. No exemplo, a string usada é a constante sSep, definida como o caractere #: No final, insere-se um parágrafo (a constante vbCrLf):
88
Selection.TypeText “Índice” & sSep & “ClassName” & sSep & _ “FormatName” & sSep & “Name (Arquivo)” & sSep & _ “CanSave” & sSep & “SaveFormat” & sSep & “CanOpen” & _ sSep & “OpenFormat” & sSep & “Extensions” & vbCrLf
Definido o título da tabela e os nomes dos campos, passemos à montagem do conteúdo das linhas. Para isso, vamos usar um loop para percorrer toda a coleção de conversores instalados. Esse loop For/Next vai girar de 1 até o total de conversores da coleção. Cada linha é montada com a mesma técnica utilizada para a linha de títulos: For i = 1 To FileConverters.Count sSaveFormat = “-” sOpenFormat = “-” With FileConverters(i) If .CanSave Then sSaveFormat = Str(.SaveFormat) If .CanOpen Then sOpenFormat = Str(.OpenFormat) Selection.TypeText i & sSep & .ClassName & sSep & _ .FormatName & sSep & .Name & sSep & _ .CanSave & sSep & sSaveFormat & sSep & _ .CanOpen & sSep & sOpenFormat & sSep & _ .Extensions & vbCrLf End With
A única diferença das linhas do corpo da tabela para a linha de títulos é imposta pelas propriedades SaveFormat e OpenFormat. Esses números só existem quando o conversor é capaz de salvar ou abrir um tipo de arquivo. Se, por exemplo, você pedir a propriedade SaveFormat de um conversor que não salva, provoca um erro. Situação idêntica ocorre com OpenFormat. Para evitar o problema, antes de solicitar o valor dessas propriedades, é obrigatório verificar as propriedades CanSave e CanOpen. No loop, criamos duas variáveis string, sSaveFormat e sOpenFormat, ambas inicialmente com o valor “-”. Então, para cada objeto FileConverter, consulta-se o valor de CanSave e CanOpen. Se for possível salvar, sSaveFormat será igual a SaveFormat. Se for possível abrir, sOpenFormat será igual a OpenFormat. Nas outras situações, as duas strings irão para a tabela como um traço, indicando que o número não existe para aquele conversor. Quanto às propriedades CanOpen e CanSave, também incluídas na tabela, o próprio Word encarrega-se de apresentá-las como “Verdadeiro” ou “Falso”. Fim do loop. Todo o texto está num documento Word. São linhas contendo uma série de caracteres #. Falta transformar essa algaravia numa tabela. Então, seleciona-se todo o documento, menos a linha do título geral, que não fará parte da tabela. Depois, aplica-se o comando ConvertToTable na região selecionada, indicando o separador (caractere # ) e o número de colunas (9). A última linha da rotina Selection.Cells.AutoFit
força o redimensionamento das colunas. Quando o Word constrói uma tabela, inicialmente todas as colunas têm a mesma largura. Assim, em algumas delas so-
89
bra espaço e, em outras, as palavras ficam quebradas de forma irregular. O método AutoFit tenta ajustar essa situação. Missão cumprida. Execute a macro e imprima a tabela de conversores. Parte dela, com informações de minha máquina e do Word 2000, está mostrada na Figura 9.1. Outras aplicações, usando dados dessa tabela, aparecerão no próximo capítulo.
F I G U R A 9 . 1 Visão parcial da tabela gerada pela rotina
Conversores de Arquivos
Atenção: o tipo de arquivo citado no título – XMW – resultou de pura invenção. Não sei se existe algum arquivo que use essa extensão esquisita. Eu apenas quis sugerir que o Word pode ser capaz de lidar com arquivos diferentes dos tradicionais DOC, TXT e RTF. Conforme você pôde observar, este projeto não está amparado no suporte visual de um formulário. Seu único ponto visível, antes de deflagrar a preparação da tabela de conversores, é o pedido de confirmação ao usuário:
F I G U R A 9 . 2 Mensagem antes de construir a tabela 90
Para ir mais além 1. Mesmo com o papel na orientação Paisagem e a ajuda do método Auto-
Fit, as colunas ficam mal distribuídas numa folha A4. Uma alternativa é modificar as margens do papel. Isso deve ser feito logo no início da rotina, mais ou menos no mesmo lugar em que foi definida a orientação Paisagem. Aliás, as margens também são propriedades de PageSetup. Com margens menores, a tabela terá mais campo para se expandir, oferecendo mais espaço para cada coluna.
2. Observe as linhas de código a seguir, retiradas da parte final da sub ConversoresDeArquivos (os comentários foram eliminados): Selection.HomeKey Unit:=wdStory, Extend:=True Selection.MoveDown Unit:=wdLine, count:=2, Extend:=True Selection.ConvertToTable Separator:="#", NumColumns:=9 Selection.Cells.AutoFit
O termo Selection não lhe parece muito repetitivo? Pois é, os caras que fazem o Visual Basic também acharam isso. Então, eles criaram uma forma de evitar essas repetições. Trata-se da instrução With/End With. Com ela, você pode transformar o trecho de código acima em algo muito mais econômico: With Selection .HomeKey Unit:=wdStory, Extend:=True .MoveDown Unit:=wdLine, count:=2, Extend:=True .ConvertToTable Separator:="#", NumColumns:=9 .Cells.AutoFit End With
Legal, não? O nome do objeto fica em cima, junto com With, e seus métodos ou propriedades são referidos, precedidos por um pontinho, no espaço antes de End With.
3. O método ConvertToTable, mostrado acima, pode ser usado sem os
parâmetros Separator e NumColumns (separador e número de colunas). Para isso, em lugar de usar o separador #, como fizemos neste projeto, use o caractere Tab (constante vbTab). Desse modo, basta você aplicar o método ConvertToTable, sem complementos. O Word descobre sozinho qual o separador e qual o número de colunas. Há exemplos disso em outros projetos deste livro, entre os quais os que estão nos Capítulos 30 e 34.
91
Hora, minuto, segundo Muitas vezes, no meio de um trabalho de programação, você se depara com um valor de tempo decorrido em milhares de segundos. Que tal apresentar esse número no formato hh:mm:ss? Aí está uma função que faz isso. Basta passar o número e ela devolve uma string formatada. Function FormataHoras(vHoraSeg As Variant) As String ‘ Recebe um valor em segundos e ‘ devolve-o no formato hh:mm:ss Dim Dim Dim Dim Dim
iHora As Integer sngHoraResto As Single iMinuto As Integer iSegundo As Integer sTmp As String
iHora = vHoraSeg \ 3600 sngHoraResto = vHoraSeg Mod 3600 iMinuto = (sngHoraResto) \ 60 iSegundo = sngHoraResto Mod 60 sTmp = Format$(iHora, “0#”) & “:” sTmp = sTmp & Format$(iMinuto, “0#”) & “:” sTmp = sTmp & Format$(iSegundo, “0#”) FormataHoras = sTmp End Function
Da base 60 para a base 10 A próxima macro faz o trabalho inverso da anterior. Recebe um número no formato hh:mm:ss e devolve um número decimal. Public Function Hora6010(strHora As Variant) As Single On Error GoTo Hora6010_Err Dim x As Integer Dim strHoraAux As String Dim Horas As Integer Dim Mins As Integer Dim Segs As Integer Const Sep = “:”
‘ ‘ ‘ ‘ ‘ ‘
variável auxiliar var. auxiliar graus minutos segundos separador (hh:mm:ss)
(Continua) 92
(Continuação) ‘ Valor das horas em formato string strHora = Format$(strHora, “hh:mm:ss”) ‘ Separa horas x = InStr(strHora, Sep) Horas = IIf(x > 0, Val(Left$(strHora, x - 1)), 0) If Horas > 24 Then MsgBox Str$(Horas) & “ - Valor incompatível com horas.” Hora6010 = 0 Exit Function End If ‘ Separa minutos strHoraAux = Right$(strHora, Len(strHora) - x) x = InStr(strHoraAux, Sep) Mins = IIf(x > 0, Val(Left$(strHoraAux, x - 1)), 0) If Mins > 60 Then MsgBox Str$(Mins) & “ - Valor incompatível com minutos.” Hora6010 = 0 Exit Function End If ‘ Separa segundos Segs = IIf(x > 0, Val(Right$(strHoraAux, Len(strHoraAux) - x)), 0) If Segs > 60 Then MsgBox Str$(Segs) & “ - Valor incompatível com segundos.” Hora6010 = 0 Exit Function End If ‘ Valor final da função Hora6010 = Horas + (Mins / 60) + (Segs / 3600) Hora6010_Fim: Exit Function Hora6010_Err: MsgBox Err.Description Resume Hora6010_Fim End Function
93
10 Muito além do velho e conhecido DOC Crie um conversor de arquivos do Word para vários outros tipos de documentos Ficha do projeto Projeto: Conversor de arquivos DOC O que faz: Converte arquivos de documentos do Word para uma série de outros arquivos de texto: DOC do Word 6.0/95; DOC do Word 2.0; TXT com quebra de linhas; RTF (Rich Text Format); e documento HTML. Usuários potenciais: Este projeto é útil para qualquer usuário que trabalhe com diferentes tipos de documentos de texto no dia-a-dia do escritório. O conversor é especialmente útil para empresas que operam com mais de uma versão do Word – por exemplo, Word 97 ou 2000, e Word 6.0 ou 95. Outra utilidade está na conversão em lote de arquivos DOC já existentes em arquivos HTML para uma intranet ou um site na Web.
Arquivos e requisitos do projeto: Formulário frmConverteDOC e código interno. Conhecimento técnico: Manipulação básica de dois objetos do Word: a coleção FileConverters (conversores de arquivos) e o objeto FileSearch (pesquisador de arquivos). Nível de programação: Intermediário
O Conversor de Arquivos DOC é um projeto relativamente simples. Contém apenas um formulário, frmConverteDOC, cujos controles e rotinas associadas dão conta de tudo que o programa se propõe a fazer. O objetivo do conversor é transformar lotes de arquivos DOC em outros tipos de arquivos, a saber: 1. DOC do Word 2.0 2. DOC do Word 6.0/95 3. TXT com quebras de linhas 4. RTF (Rich Text Format) 5. HTML O tipo de arquivo de partida será o da versão do Word com que você trabalha – ou seja, 97 ou 2000. No entanto, nada impede que você use o programa, no Word 2000, por exemplo, com a finalidade de converter arquivos de outras versões do Word – como a 2.0 – para HTML ou TXT. O Conversor de Arquivos DOC repousa, tecnicamente, em dois recursos do VBA, ambos associados a arquivos: a coleção FileConverters e o objeto FileSearch. A coleção FileConverters reúne todos os conversores de arquivos instalados no Word, tanto os de leitura como os de escrita. Como você sabe, o Word – ou outro aplicativo qualquer – precisa usar módulos de conversão, seja para abrir e apresentar arquivos de outros programas, seja para salvar arquivos em formatos externos. O conjunto de conversores disponíveis no Word varia de máquina para máquina. Depende, em primeiro lugar, da versão do processador de texto. Depende, ainda, do tipo de instalação que você fez, já que boa parte dos conversores só é instalada por solicitação do usuário. O objeto FileSearch, ao contrário, está disponível em qualquer instalação do Word 97 ou do 2000. Com seus métodos e propriedades, ele facilita o trabalho de localizar, contar e classificar arquivos. A aplicação Conversor de Arquivos DOC usa os recursos de FileSearch para encontrar os arquivos do Word, e os FileConverters para transformá-los em documentos de outro padrão, conforme indicação do usuário. 95
Se você tem dois ou três documentos do Word e pretende convertê-los para TXT ou HTML, então basta abrir o primeiro documento, salvá-lo no formato desejado, fechá-lo e, depois, repetir a operação para os outros arquivos DOC. Nesse caso, não é necessário ter ou desenvolver uma aplicação como o Conversor de Arquivos. Agora, considere as seguintes situações:
1. Você tem centenas ou milhares de arquivos para converter; ou 2. Você converte apenas alguns arquivos, mas essa tarefa tende a fazer parte de seu dia-a-dia.
Aqui, o uso do conversor se justifica plenamente. De fato, esta aplicação constitui um exemplo típico de automação de escritórios. Seu objetivo é deixar a cargo do computador a execução de tarefas repetitivas. Na verdade, o programa faz a mesma coisa que você faria: abrir os arquivos e salvá-los em outro formato. Mas é preciso ter paciência infinita para repetir essa seqüência de operações dezenas, centenas de vezes – sem errar. Mas, chega de conversa; vamos à prática.
A lógica do conversor O formulário frmConverteDOC, único módulo do conversor, contém os seguintes controles fundamentais: n
duas caixas de texto, txtPastaDOC e txtPastaFinal;
n
dois botões de comando, cmdProcurarDOC e cmdProcurarFinal;
n
uma moldura com cinco botões de opção; e
n
botões Converter (cmdConverter) e Fechar (cmdFechar).
Para organizar a conversão dos documentos, entendemos que os arquivos originais deveriam estar em diretórios diferentes dos arquivos convertidos. Aliás, isso se torna obrigatório quando os grupos de documentos de origem e destino são ambos do Word, em diferentes versões. Na caixa de texto txtPastaDOC, o usuário deve indicar o diretório onde se encontram os arquivos originais. Na outra caixa, txtPastaFinal, ele deve digitar a pasta de destino. Os botões cmdProcurarDOC e cmdProcurarFinal oferecem uma alternativa à digitação dessas duas pastas. Se o usuário clicar neles, vai poder navegar pela árvore de diretórios e indicar as duas pastas. O uso dos botões Procurar, em lugar da digitação é sempre recomendado porque evita erros. Cada um dos cinco botões de opção corresponde a um dos tipos de arquivos de saída (TXT, RTF, HTML etc.). O padrão de saída é o documento do Word 6.0/95, que tende a ser a alternativa mais usada. O botão Fechar, fazendo jus ao nome, encerra a aplicação. E, por fim, o botão Converter dá início ao tra96 balho de conversão dos arquivos.
F I G U R A 1 0 . 1 A tela do Conversor: cinco formatos de destino
Quanto ao desenho do formulário, não há segredos. Passemos, então, ao código. Comecemos pelos dois botões Procurar (cmdProcurarDOC e cmdProcurarFinal). O evento Click desses dois objetos tem a mesmíssima estrutura. Ele chama a caixa de diálogo Cópia, captura o diretório escolhido pelo usuário nessa caixa de diálogo e, por fim, escreve esse diretório na caixa de texto correspondente. No código, essa tarefa é atribuída à função EscolherPasta. Além de capturar uma string com a pasta indicada pelo usuário, essa função executa ainda dois trabalhos secundários, mas fundamentais para evitar erro no programa. O primeiro deles é a extração de aspas no início e no fim da string. Quando trabalha com caminhos que contêm espaço entre palavras (exemplo: “c:\meus documentos”), o Word acrescenta aspas no início e no fim da expressão. No entanto, se você tentar usar a informação com as aspas, provocará um erro. Por isso, a função verifica se existem as aspas – o caractere Chr$(34) – e, em caso positivo, elimina-as: If Right$(strPasta, 1) = Chr$(34) Then strPasta = Mid(strPasta, 2, Len(strPasta) - 2) End If
A outra pequena tarefa realizada pela função EscolherPasta é eliminar a barra invertida (\) que pode vir no final do nome de uma pasta, especialmente no caso de um diretório-raiz, como c:\ ou d:\. Se você não tiver esse cuidado ao lidar com diretórios, corre o risco de provocar um erro ao fazer concatenações do tipo: diretório & “\arquivo” 97
Como a variável diretório já contém a barra, isso resulta em: diretório\\arquivo
Já vimos como indicar os diretórios. Passemos, agora, para a escolha do tipo de arquivo final. Quando o usuário clica num dos cinco botões de opção existentes, define o valor da variável intTipoDocFinal, válida em todas as rotinas do formulário. Se, por exemplo, ele escolhe a opção DOC Winword 6.0/95, dispara o evento Click do botão correspondente: Private Sub optDoc95_Click() intTipoDocFinal = DOC_WORD6 End Sub
Inicialmente, a idéia era estabelecer constantes para os valores DOC_Word6, DOC_WORD2, TXT e os demais. No entanto, para garantir a compatibilidade do conversor com o Word 97 e o Word 2000, todos esses valores passaram a ser variáveis (inteiros longos). Das cinco alternativas de conversão oferecidas pelo programa três têm constantes predefinidas no Word: texto com quebra de linhas, RTF e HTML. Essas constantes são, respectivamente, wdFormatTextLineBreaks, wdFormatRTF e wdFormatHTML (este último apenas no Word 2000). Isso significa que os conversores desses formatos estão embutidos no Word e são instalados com ele. No entanto, quando se deseja exportar textos no formato do Word 2.0 ou do Word 6.0/95, é preciso antes verificar se os conversores estão instalados. Isso é feito mediante a propriedade CanSave do objeto FileConverters. Se CanSave=True, o Word pode salvar para um formato indicado. No Conversor de Arquivos DOC, o teste da possibilidade de conversão é feito no evento UserForm_Initialize. Para saber se é possível salvar no formato do Word 6.0, por exemplo, o teste é o seguinte: If FileConverters(“MSWord6Exp”).CanSave Then DOC_Word6 = FileConverters(“MSWord6Exp”).SaveFormat Else DOC_Word6 = NÃO_EXISTE ‘Conversor não instalado End If
A string “MSWord6Exp”, que identifica a classe do conversor de arquivos (propriedade ClassName) para o formato Word 6.0/95, não aparece por milagre. Ela resulta de uma consulta que foi mostrada no projeto anterior (verifique). Essa consulta também revela que a string de identificação do conversor do Word 2.0 é “MSWordWin2”. Quando o usuário opera com o processador de texto em modo interativo, não precisa saber de nada disso. Mas o desenvolvedor, que vai revolver as entranhas do programa, tem de travar conhecimento 98 com objetos como os FileConverters e suas strings e constantes quase secretas.
Mas vamos em frente. O valor das variáveis DOC_Word6, DOC_Word2, TXT e afins é definido durante a inicialização do formulário. Como o formato de destino padrão é o documento do Word 6.0/95, também no evento de inicialização provoca-se um clique na opção correspondente. Portanto, quando o form se abre, o valor de intTipoDocFinal está associado ao formato de gravação (propriedade SaveFormat) de arquivos Word 6.0. Indicados os diretórios de origem e de destino e o tipo de documento final, o usuário clica no botão Converter (frmConverter). A rotina Click desse botão faz dois testes importantes. Primeiro, ela checa se as caixas de texto que indicam os diretórios foram preenchidas. Basta que uma delas esteja vazia para que o programa seja interrompido. O outro teste consiste em verificar se os dois diretórios indicados de fato existem. Esse passo seria desnecessário se o usuário sempre indicasse os diretórios, mediante o clique nos botões Procurar. Mas, como ele também pode digitar, a porta está aberta para o erro. Portanto, se, pelo menos, um dos diretórios não existir, é preciso – mais uma vez – interromper o programa. Quem diz se o diretório é verdadeiro ou falso é a função PastaExiste: Private Function PastaExiste(strPasta As String) As Boolean ’ Testa se o diretório strPasta existe On Error GoTo PastaExiste_Err Dim strDirAtual As String strDirAtual = CurDir$ ChDir strPasta PastaExiste = True ChDir strDirAtual PastaExiste_Fim: Exit Function PastaExiste_Err: PastaExiste = False Resume PastaExiste_Fim End Function
A função PastaExiste recebe o nome de uma pasta e responde se essa pasta existe ou não. Inicialmente, ela guarda o diretório corrente (CurDir$) na variável dirAtual. Em seguida, tenta passar para o diretório em teste, com a instrução ChDir. Se tudo correr bem, então a pasta existe e passa a ser o novo diretório atual. Como é apenas um teste, usa-se outra vez a instrução ChDir para voltar ao antigo diretório corrente. Se o diretório não existe, ocorre um erro, que é tratado na rotina PastaExiste_Err. Lá, a função é declarada falsa. A função PastaExiste apresenta um interessante algoritmo de programação. No entanto, você pode substituí-la por outra rotina, PastaExiste2, também incluída no projeto. PastaExiste vale para qualquer versão do Visual Basic. PastaExiste2 usa recursos mais novos introduzidos pelo VBA: 99
PastaExiste2 = True strTemp = Dir$(strPasta, vbDirectory) If Len(strTemp) = 0 Then PastaExiste2 = False End If
PastaExiste2 parte da hipótese de que o diretório existe. Depois, usa a função Dir$, com a constante vbDirectory, para fazer a verificação. Se Dir$ retorna uma string vazia, então o diretório não existe e a função é falsa. Se você quiser, pode resumir ainda mais essa rotina, deixando-a com apenas uma linha: PastaExiste2 = Dir$(strPasta, vbDirectory) < >“”
Aqui, você indica que a função é igual a Dir$(), que é diferente de uma string vazia. Se Dir$ confirmar isso, a função será verdadeira. Se, no entanto, Dir$ for igual a uma string vazia, então PastaExiste2 é falsa. Se você não se sentir à vontade com essas duas igualdades – no caso, uma igualdade e uma desigualdade – encadeadas, teste as duas opções de PastaExiste2 isoladamente e conclua. Observe: quando se usou a variável strTemp para armazenar o valor da função Dir$, a verificação foi feita checando se o comprimento dessa string era zero. No outro caso, o teste procura saber se a string é vazia. Os dois processos se equivalem. No entanto, segundo os teóricos, o teste do comprimento zero é mais rápido. Agora, a reta final. Se os diretórios existem, a rotina cmdConverter_Click chama o procedimento ConverteDOC que, de fato, cuida da conversão dos arquivos. Entra em ação o objeto FileSearch. Sua propriedade LookIn é ajustada para procurar no diretório de origem. Já a propriedade FileName é ajustada para a string “*.doc”, a fim de localizar todos os arquivos do Word no diretório. Se nenhum arquivo for encontrado, encerra-se o procedimento. Caso contrário, o programa entra num loop For/Next para determinar, em cada passo, o nome de um arquivo. Este é aberto, salvo na pasta de destino com o mesmo nome (muda apenas a extensão, se necessário) e, depois, fechado. O loop continua, até o último arquivo. O presente projeto é interessante para você conhecer os procedimentos usados pelo Word na conversão de um tipo de arquivo em outro. No entanto, se você usa o Word 2000, essa versão do programa já traz um aplicativo chamado Assistente de Conversão em Lote, que permite converter arquivos do Word para vários outros formatos, e vice-versa. Para abrir esse assistente, acione Arquivo/Novo e, na janela Novo, dê um duplo clique na opção Assistente de Conversão em Lote. O programa vai guiar você, passo a passo, na conversão dos arquivos desejados. Além da interface, organizada na forma de assistente, uma das vantagens dessa aplicação sobre nosso conversor é que ele opera em duas direções: de, e para arquivos DOC do Word 2000. 100
F I G U R A 1 0 . 2 Assistente de Conversão: de DOC (Word 2000)
para outros formatos, e vice-versa
Para ir mais além 1. Além dos cinco tipos de conversões previstos no Conversor de Arquivos
DOC, você pode incluir outros, ampliando a abrangência do programa. Para isso, use as informações discutidas no capítulo anterior.
2. Modifique o Conversor de arquivos DOC para que ele apresente ao
usuário uma caixa de listagem com os arquivos do Word, existentes no diretório de origem. Assim, em lugar de converter todos os arquivos existentes nesse diretório, o programa converteria apenas aqueles escolhidos pelo usuário.
3. Outra ampliação possível é permitir que o programa aceite outros forma-
tos de arquivo na entrada do processo, e não somente na saída. Uma sugestão para a escolha do formato de entrada seria agregar ao formulário outra moldura, com botões de opções. Outra seria adotar a solução empregada pelo Assistente de Conversão do Word 2000 – caixas de combinação para os formatos de entrada e de saída. Lembre-se de interromper o fluxo do programa se o formato de entrada for igual ao de saída.
4. Durante a conversão, você vai ver o Word abrindo, salvando e fechando
cada arquivo encontrado no diretório de origem. Se você achar mais conveniente fazer essa operação de forma mais discreta, retire as aspas de comentário das seguintes linhas, que vêm antes e imediatamente depois do trecho de código que representa o loop de conversão: ‘Application.WindowState = wdWindowStateMinimize ‘Application.WindowState = wdWindowStateMaximize
101
A primeira linha minimiza a tela do Word. Assim, a operação ocorre de forma semi-oculta. Terminada a conversão, a outra linha retorna o programa ao modo de tela cheia.
5. Observe a declaração da função EscolherPasta. Na lista dos parâmetros que deve receber, ela traz um elemento novo: um parâmetro opcional:
Private Function EscolherPasta(Optional strDir As String = “c:\”) As String
O que é isso? Quando um parâmetro é declarado como Optional, isso significa que ele não precisa ser incluído na chamada da função. No caso acima, a chamada pode ser feita de duas maneiras. Exemplo 1: txtPastaDoc = EscolherPasta()
Nesse caso, a caixa de diálogo é exibida mostrando o diretório “c:\”, indicado como padrão. Ou seja, se a chamada não inclui um diretório, a caixa de diálogo mostra a pasta c:\. A outra alternativa força a exibição da pasta desejada: txtPastaDoc = EscolherPasta(“c:\meus documentos”)
Janela imediata Admita que você quer usar a janela Imediata (ou Verificação Imediata) para testar a função PastaExiste, que faz parte do projeto Conversor de Arquivos DOC. Então você digita na janela Imediata: ? PastaExiste(“c:\meus documentos”)
O Word avisa que aquela função não existe. Como? Ela está ali mesmo, à sua frente! Calma: acontece que PastaExiste é declarada como uma função do tipo Private – quer dizer, só atua no âmbito do formulário a que pertence. Portanto, ela não pode ser vista “de fora”. A saída é “passar para dentro”, localizando a função no objeto que a contém, o formulário. Assim: ? frmConverteDOC.PastaExiste(“c:\meus documentos”)
Agora a função responde: Verdadeiro
102
Esse procedimento vale tanto para os módulos de formulários – caso de frmConverteDoc – quanto para os módulos de puro código.
Formato HTML Se você precisa criar aplicações compatíveis com mais de uma versão do Word – por extensão, mais de uma versão do VBA –, cuidado com os detalhes. Um exemplo: tanto o Word 97 como o 2000 convertem arquivos para o formato HTML. Só que, quando a versão mais nova foi lançada, o padrão HTML (leia-se Internet) já se tornara muito mais importante. Por isso, nos conversores de arquivos, o formato HTML já tem uma constante intrínseca: wdFormatHTML (que é igual a 8). Só que essa constante só existe no Word 2000. Portanto, não pode ser usada se você quer que sua aplicação também rode na versão anterior. No lugar de wdFormatHTML, use 8.
103
11 Na tabuada do 1 Crie documentos com numeração seqüencial automática Ficha do projeto Projeto: Memorando Numerado Simples e Memorando Numerado Interno O que faz: Abre um modelo de documento e numera-o, conforme uma seqüência controlada. O projeto Memorando Numerado Interno é idêntico ao primeiro. A única diferença é que incorpora a lógica de numeração no próprio modelo de documento. Arquivos e requisitos do projeto: Para o Memorando Numerado Simples: – Módulo modMemoNumerado e rotinas internas. – Modelo Memonum0.dot Para o Memorando Numerado Interno: – Modelo Memonum1.dot e código interno. Conhecimento técnico: Criação e leitura de arquivos INI. Controle do documento de dentro ou de fora do modelo que o contém. Nível de programação: Iniciante
Em numerosas atividades do dia-a-dia dos escritórios, sempre se necessita gerar documentos com numeração seqüencial. São memorandos, cartas, ofícios, relatórios e outros documentos que precisam ser marcados com números do tipo 001/00, 002/00, 003/00, e assim por diante. Você certamente gostaria de ter uma ferramenta em seu Word que garantisse a seqüência dos números sem que você precisasse anotá-los para controle. Não é difícil desenvolver essa ferramenta. Para simplificar, vamos trabalhar com a idéia do memorando. Lógico, o que se disser aqui em relação ao memorando valerá também para carta, ofício, relatório, recibo ou qualquer outro documento que requeira numeração seqüencial. Raciocinemos. O que é necessário? Primeiro, um modelo – o documento-padrão para seus memorandos. Esse modelo deve ter um espaço para o número do documento. Mas como saber qual foi o número do último memorando emitido, a fim de dar ao próximo o número seqüencial correto? Aqui entra um pequeno macete. Há várias formas de fazer isso, mas todas passam por guardar o número num registro permanente. E o melhor lugar, no caso, é um arquivo no disco. No Word, a forma mais fácil de guardar um número é usar um arquivo INI. Com essa idéia, esbocemos as vigas mestras do projeto:
1. Temos um modelo de memorando, ao qual deve ser anexado, automaticamente, o número do documento.
2. Cada número utilizado será armazenado num arquivo INI. Isso implica
que, no memorando seguinte, esse número deverá ser lido, acrescentado de uma unidade e estampado no novo documento. E outra vez armazenado etc. etc. Isso lembra a escola primária antiga, quando a criançada recitava em coro a “tabuada do 2”: “2 vezes 1, dois; 2 vezes 2, quatro”...
Passemos ao código. O primeiro passo é criar um novo módulo, ao qual chamaremos de modMemoNumerado. Esse módulo conterá todas as rotinas necessárias ao projeto. No módulo, vamos trabalhar com a rotina principal, que receberá o nome de Sub MemoNumerado Simples. Observe o detalhe: precisaremos, mais tarde, executar essa rotina de fora desse módulo. Por isso, essa sub deve ser marcada como um procedimento público. Assim sendo, deve ser declarada da seguinte forma: Public Sub MemoNumeradoSimples()
Sigamos, passo a passo, as ações desse procedimento. Em primeiro lugar, ele define o diretório onde será gravado o documento final. Define, também, o arquivo que contém o modelo de memorando, e o arquivo INI que armazena o último número seqüencial utilizado. sDocSubDir = “c:\solucoes\memos” sArqModelo = “c:\solucoes\memonum0.dot” sArqIni = “c:\solucoes\dados\memo.ini”
105
Em seguida, a rotina lê o ano atual no relógio do sistema. Esse valor deverá compor parte da data na numeração do documento. sAnoAtual = CStr(Year(Now))
Segue-se um teste de existência do arquivo INI, usando a função ArquivoExiste, outra rotina do módulo modMemoNumerado. ArquivoExiste simplesmente responde se Memo.ini existe ou não. Em caso negativo – que pode corresponder, por exemplo, à primeira vez que a rotina é executada –, recorre-se à sub ZeraContagem, que cria Memo.ini, e anota ali o primeiro número de documento: 0. Na primeira consulta ao arquivo, o número se tornará 0 + 1. Veja o código completo de ZeraContagem: Private Sub ZeraContagem(strAno As String, strIni As String) ‘ Grava o ano atual, zera a contagem System.PrivateProfileString(strIni, “Contador”, “Ano”) = strAno System.PrivateProfileString(strIni, “Contador”, “MemoNum”) = “0" End Sub
ZeraContagem recebe, como string, o ano lido no relógio do PC e o caminho completo do arquivo INI. Com esses elementos, usa a propriedade PrivateProfileString, do objeto System, para criar o arquivo Memo.ini e inserir nele uma seção chamada Contador, a qual tem duas entradas: Ano e MemoNum. Memo.ini, um arquivo texto, fica inicialmente com o seguinte conteúdo: [Contador] Ano=2000 MemoNum=0
Continuemos. E o que acontece se o arquivo INI existe? Nesse caso, a rotina recorre, ela mesma, à propriedade PrivateProfileString, desta vez para ler o valor armazenado. Lido o valor, entra em ação outro teste. Se o ano lido no relógio do micro for maior que o registrado no arquivo INI, então iniciou-se um ano novo desde a última vez que a rotina foi utilizada. Se temos um ano novo, então é necessário recorrer outra vez aos serviços de ZeraContagem: o número do memorando volta a zero e o ano é atualizado no arquivo. E se o ano do relógio for menor que o já registrado no INI? Nesse caso, ou o relógio do PC andou para trás ou o arquivo INI foi adulterado. Então, a rotina avisa ao usuário que aconteceu algo estranho – e pára. Passados os testes, a rotina segue em frente. Agora, ela já tem todos os dados e pode abrir um documento novo, baseado no modelo do memorando: Documents.Add Template:=sArqModelo 106
No lugar em que deverá entrar o número seqüencial, o modelo traz a expressão NúmeroDoDocumento, que deverá ser substituída pelo valor adequado. A rotina lê o valor no arquivo INI, soma 1 e compõe o texto que, no fim, resulta em algo como: Memorando 036/00
Agora, usa-se o objeto Selection.Find para encontrar, no documento, a expressão NúmeroDoDocumento e substituí-la por “Memorando 036/00”. Então, monta-se um nome para o arquivo, também baseado no número do documento. O nome do arquivo será: mm036-00.doc
Naturalmente, como o Word suporta nomes longos desde a versão 95, você não precisa forçar a mão para manter o nome do arquivo com apenas oito caracteres. Antes de salvar o arquivo, outro teste: por acaso já existe algum arquivo com esse nome? Em caso negativo, salva-se o arquivo, e o memorando, pré-numerado, já está pronto para receber o texto. No entanto, se o arquivo existe, a operação de salvamento é cancelada. Trata-se de um procedimento de segurança. Sem ele, você correria o risco de sobregravar um documento existente (e já expedido) com outro, novo. Esse erro só seria possível se alguém modificasse, manualmente, o número registrado no arquivo INI. De todo modo, o programa não permite a perda de documentos. Tanto que, nesse caso, o arquivo não é salvo e o número a ele atribuído também não vai para o registro no arquivo INI. Para evitar que alguém – ou você mesmo – modifique o arquivo INI, o ideal é guardá-lo num diretório de pouco trânsito, como a pasta de modelos do Word. Fuja do diretório do Windows ou de Meus Documentos. Aqui, para manter juntos todos os dados referentes a projetos do livro, ele é armazenado em c:\solucoes\dados. O módulo modMemoNumerado pode residir em seu modelo Normal.dot ou em outro arquivo DOT qualquer. Para executar a rotina MemoNumeradoSimples, é necessário chamá-la a partir da janela de macros (Ferramentas/Macro/Macros/Executar), ou então instalar numa barra de ferramenta um botão que a dispare.
Memorando numerado interno Este projeto é absolutamente idêntico ao anterior: abre um documento baseado num modelo específico e numera-o. Armazena o número num arquivo INI para leitura posterior, durante a emissão do próximo documento. O único ponto destoante entre os dois projetos é a sua estrutura.
107
F I G U R A 1 1 . 1 O memorado, pré-numerado, pronto para
receber um texto final
O projeto Memorando Numerado Simples envolve dois itens: o modelo Memonum0.dot, que contém a matriz para os documentos, e o módulo modMemoNumerado. Este último, contido em outro modelo – que pode ser o Normal ou outro qualquer –, engloba toda a lógica do programa. Memonum0.dot serve apenas de suporte para o design do documento. Portanto, você pode criar novo modelo com esse nome, reservando nele um lugar para a palavra-chave (NúmeroDoDocumento) que será localizada e substituída pelo número do documento. Este projeto, Memorando Numerado Interno, contém o mesmo código de programação e a mesma lógica de numeração dos documentos. Tem também um modelo, Memonum1.dot. A diferença fundamental é que o código – uma cópia do módulo modMemoNumerado –, está embutido no próprio modelo. Em outras palavras, a “cara” do documento e o esquema de automação residem no próprio documento. Aí, em vez de estar num módulo, o programa fica no próprio módulo ThisDocument, existente em qualquer documento ou modelo. Agora você deve ter percebido o porquê da palavra “interno” no nome do projeto. As diferenças de funcionamento determinadas por essas duas abordagens – a lógica dentro ou fora do modelo – estão mostradas na tabela a seguir.
108
ITEM
MEMO NUMERADO SIMPLES
MEMO NUMERADO INTERNO
Papel do modelo
Serve apenas de suporte ao Fornece o formato do formato básico do documento. documento e também a A lógica de automação está lógica de automação. fora.
Reação do modelo diante de um duplo clique no Windows Explorer
Abre um documento baseado em Memonum0.dot, mas não ocorre a numeração automática.
Abre um documento baseado em Memonum1.dot e executa a numeração automática.
Percepção do usuário, se a solução for disparada a partir de um botão na barra de ferramentas
Idêntica
Idêntica
Espaço ocupado em disco pelo documento final
Apenas o tamanho normal do documento
O tamanho normal do documento mais o tamanho do código embutido
Como você pode ver pelo quadro acima, há vantagens e prejuízos em reunir toda a solução dentro de um modelo ou deixá-la em compartimentos separados – documento de um lado e código do outro. A principal vantagem da separação está mostrada na última linha do quadro acima: o modelo que contém apenas o documento ocupa menos espaço em disco. Se, por exemplo, o código embutido acrescenta 10 KB em cada documento, ao final de cem memorandos (ou cartas, ofícios etc.) você já terá perdido cerca de 1 MB de espaço. Em contrapartida, reunir tudo num mesmo arquivo é mais prático e interessante. Para evitar o desperdício, tenha em conta este pequeno truque. Sempre que possível, use a lógica embutida no modelo. Mas, se for necessário gerar um documento genérico – não baseado no modelo –, aplique essa mesma lógica sobre um documento derivado do modelo normal. Isso pode ser resolvido pelo seguinte trecho de código: Dim strModelo As String strModelo = NormalTemplate Documents.Add Template:=strTemplate
Se você tentar incluir o valor NormalTemplate diretamente na linha Documents..., não vai funcionar. Na sintaxe do método Add, para Documents, o valor esperado deve ser do tipo string, o que está garantido pela declaração da variável strModelo. Agora, observe: obviamente, este truque não se aplica ao caso do modelo Memonum1.dot. Serve, no entanto, para casos em que o programa 109
faz uma pesquisa e gera um documento a partir de uma página totalmente em branco – o que não é o caso de nossos memorandos. Outra conclusão que se pode extrair dessa discussão é a seguinte. Se o documento vai ser produzido em escala, considere a possibilidade de separar modelo e programa. No extremo oposto, se a aplicação não gera documento, mas apenas executa um cálculo ou consulta informações, coloque-a toda dentro de um modelo. Naturalmente, essa conclusão não deve ser encarada como um dogma religioso – é apenas um princípio, bem genérico, para orientar sua decisão. Daqui partimos para outro ponto, igualmente importante. Quando o programa e o modelo são separados, onde deve ser armazenado o programa? Se o programa está no modelo Normal, ou em qualquer outro modelo global, ele se torna disponível no Word em todos os momentos. Esse aspecto é o lado positivo. Em compensação, se você armazena todas as aplicações – módulos e formulários – no modelo Normal.dot, transformará seu Word numa imensa carroça. Porque ele estará sempre carregando na memória o peso extra desses objetos. Como regra geral, você pode adotar alguns critérios. Dê preferência a colocar no Normal.dot os pequenos programas de uso muito freqüente. Aplicações muito grandes ou de uso esporádico podem residir em modelos próprios. Elas só serão trazidas para a memória quando estiverem em uso.
Para ir mais além 1. Se, por acaso, você decidir utilizar esta solução para numerar documen-
tos e está começando no meio do ano, com – digamos – 45 documentos já emitidos, rode o programa uma vez e coloque 45 como o número registrado em Memo.ini.
2. Em lugar de usar um arquivo INI, poderíamos ter registrado as informa-
ções no Registro do Windows. Contudo, achamos desaconselhável. Primeiro, porque experimentações com o Registro podem resultar em desastre. Depois, até por causa da necessidade de facilitar a manipulação expressa no item 1, acima.
3. Nos capítulos seguintes, você verá como aumentar o nível de automação no preenchimento de documentos. Observe que, neste capítulo, por exemplo, as macros poderiam envolver o nome do remetente e do destinatário do memorando.
4. Se quiser, você pode modificar o programa para que o documento seja
numerado com um ano de quatro dígitos. Em lugar de “Memorando 036/00”, você pode querer “Memorando 036/2000”.
5. O arquivo Memonum0.dot foi criado sob a inspiração de um modelo de 110
memorandos que acompanha o Word. No original, o campo Data era preenchido automaticamente, graças a um campo de data no formato
“25 de fevereiro de 2000”. Esse campo, no entanto, apresentava um problema. Auto-atualizável, ele sempre exibia a data em que você abrisse o documento. Assim, o memorando não guardava a data de sua emissão. Para corrigir isso, apagamos o campo e forçamos a inserção da data no lugar dele, via código. Para isso, na construção do modelo, marcamos a posição onde estava o campo com um indicador (Inserir/Indicador) chamado DataDoDocumento. O trecho de código que garante a inserção da data é o seguinte: strData = Day(Now) & “ de ” & _ Format$(Now, “mmmm”) & “ de ” & Year(Now) Selection.GoTo What:=wdGoToBookmark, _ Name:="DataDoDocumento" Selection.TypeText strData
O código localiza o indicador (bookmark) com o método Selection. GoTo e, com o cursor posicionado nele, insere a data, contida na variável strData.
6. No código embutido em Memonum1.dot há duas linhas comentadas. São elas:
’ Documents.Add Template:=sArqModelo ’ ActiveWindow.View.Type = wdPageView
A primeira abre um novo documento baseado no modelo (Memonum1.dot). A outra exibe a janela do Word no formato Layout de Página. Elas são linhas válidas no projeto Memorando Numerado Simples. No entanto, geram erro se forem deixadas ativas no presente projeto. Por quê?
7. Observe um detalhe: todos os procedimentos embutidos em Memo-
num1.dot são do tipo Private. A razão é simples: eles são disparados quando se abre um novo documento baseado no modelo. Nesse momento, o procedimento New_Document, entra em ação, chamando a sub MemoNumeradoInterno. Tudo ocorre dentro do próprio documento. Não há, portanto, necessidade de algum objeto ficar acessível para fora do módulo.
111
Versões diferentes, claras ou ocultas Se você desenvolver para um público que vai usar seu produto em plataformas diferentes, tome cuidado com as “pegadinhas” impostas pelos ambientes de programação. Isso vale para as diferentes versões do Word. No VBA do Word 2000, para obter a data atual no formato “25 de setembro de 2000”, você pode usar: strData = Format (Now, “d \de mmmm \de yyyy”)
Se você colocar as partículas “de”sem as barras que as precedem, obtém algo assim: 25 25e setembro 25e 2000
Isso ocorre porque o interpretador entende a primeira letra do “de” como um sinal de dia, na data. A barra invertida modifica o valor da letra. Ou seja, a letra, ali, tem o valor de letra, mesmo. Descobri por acaso, tentando assimilar o Format aos padrões das expressões regulares, já existentes no VBScript. Deu certo – e gostei. Esse foi o lado bom. Só depois é que percebi a má notícia: isso só funciona no VBA do Office 2000. No 97, o caminho tem de ser outro, um pouco menos simpático: strData = Day (Now) & “ de ” & _ Format$(Now, “mmmm”) & “ de ” & Year(Now)
Portanto, cuidado com esses detalhes. Aliás, às vezes nem é necessário haver duas versões diferentes do programa. Dois Words da mesma versão, com uma DLL mais recente numa das máquinas, podem produzir informações díspares. Se você desenvolveu uma aplicação no Word 2000, não se arrisque a pôr a mão no fogo jurando que ela funciona no Word 97.
Atualização de modelos A partir de qualquer documento baseado num modelo, você pode abrir o código embutido nesse modelo. Aí, o que for atualizado no documento, também o será no modelo. Isso vale apenas para o VBA: formulários e módulos de programação. Para atualizar o texto ou o layout do documento (texto, tabelas etc.) contido no modelo, é necessário abrir o próprio modelo – ou seja, o arquivo DOT.
112
12 De um em um, para vários documentos Crie documentos com numeração seqüencial automática Ficha do projeto Projeto: Numera automaticamente e controla a emissão de diferentes documentos: memorandos, cartas, ofícios, relatórios etc. O que faz: Abre um modelo de documento e numera-o, conforme uma seqüência controlada. Arquivos e requisitos do projeto: Formulário frmDocsNumerados e rotinas internas. Arquivos DOT com os modelos de cada documento. Arquivo Docs.ini. Conhecimento técnico: Criação e leitura de arquivos INI Nível de programação: Iniciante
Este capítulo constitui um prolongamento lógico dos dois anteriores. Naqueles, você viu como programar uma aplicação que numera automaticamente um tipo de documento: memorando, carta, ofício, requerimento etc. Neste, vamos dar um passo à frente, expandindo a solução, para que ela controle, ao mesmo tempo, diferentes tipos de documentos. Para que o usuário diga à aplicação com qual documento deseja trabalhar, é necessário oferecer a ele um painel de escolha. Portanto, ao contrário da solução anterior, precisamos criar um formulário (frmDocsNumerados) com as opções de documentos. No exemplo deste projeto, usamos cinco tipos de documentos: memorandos, carta tipo 1, carta tipo 2, ofício e relatório. Essencialmente, portanto, o formulário (frmDocsNumerados) contém uma moldura com cinco botões de opção (OptionButton1 a OptionButton5) e dois botões de comando: cmdOK e cmdCancelar. Para operar o aplicativo, o usuário escolhe um tipo de documento e aciona o botão OK. A ação básica deste projeto é executada pela rotina associada a esse botão. A primeira tarefa da rotina cmdOK_Click consiste em determinar qual foi o botão de opção escolhido pelo usuário: For i = 1 To 5 If Controls(“OptionButton” & i).Value = True Then n = i Exit For End If Next i
F I G U R A 1 2 . 1 O formulário frmDocsNumerados
Conforme o índice do botão escolhido (1 a 5), define-se qual modelo de documento deverá ser usado. Naturalmente, para cada opção existente no programa você precisa ter um modelo. A caracterização do tipo de documento que será usado no processamento posterior é feita mediante a instrução Select Case: 114
Select Case n Case 1 ‘ memorando strDocDir = “c:\solucoes\memos\” strArqModelo = “c:\solucoes\memonum1.dot” strChave = “MemoNum” strTipoDoc = “Memorando ” strArqPrefixo = “mm” Case 2 (...)
Para cada valor de n, atribuem-se diferentes valores a cinco variáveis:
1. strDocDir – Diretório onde serão gravados os documentos do tipo n. Se
você quiser organizar bem os documentos, uma boa idéia é criar um subdiretório para cada tipo de documento.
2. strArqModelo – Caminho completo do arquivo com o modelo do tipo de documento.
3. strChave – Nome da chave que controla a numeração do documento no arquivo INI. Como há, no exemplo, cinco tipos de documentos, deve haver igual número de chaves no arquivo INI.
4. strTipoDoc – Expressão que indica o tipo de documento: “Memorando”, “carta” etc.
5. strArqPrefixo – Prefixo associado ao tipo de documento que servirá para
identificar os arquivos. Para memorandos, “mm”; para cartas, “cr”, “ct”; para ofícios, “of”. Concluídas essas definições, o restante do processamento é similar ao já visto nos dois projetos anteriores. A principal diferença está em dois pontos. Primeiro, a necessidade de criar um modelo para cada tipo de documento desejado. Depois, essa multiplicidade de documentos se reflete também no arquivo Docs.ini, que tem o seguinte perfil: [Contador] Ano=2000 MemoNum=0 Carta1Num=0 Carta2Num=0 OficNum=0 RelatNum=0
Na virada do ano, o programa recorre à rotina ZeraContagem, que atualiza o ano e zera os números de todos os documentos. 115
Para ir mais além 1. No exemplo mostrado neste projeto, todos os documentos usam o mesmo modelo. Além disso, todos os arquivos são armazenados no mesmo diretório. Para transformar este aplicativo num projeto utilizável, você precisa criar os modelos e ajustar a eles as opções do formulário.
2. Com pequenas adaptações, você pode ajustar o projeto para controlar
menos – ou mais – de cinco opções de documentos. Basta mexer no formulário e incluir (ou subtrair) um ou mais Cases na instrução Select Case.
Diretório mutante O VBA/Word é, sem dúvida, uma ferramenta excelente para a automação de escritórios. Isso não significa que seja um produto isento de imperfeições. Aqui está uma delas. A propriedade DefaultFilePath, do objeto Options, dá acesso a uma série de definições do Word. Exemplo: Options.DefaultFilePath(wdDocumentsPath)
A linha acima fornece o diretório-padrão no qual o Word guarda os documentos. Mas cuidado: não confie na informação fornecida por essa propriedade – ela é mutante. Ao contrário do diretório mostrado em Ferramentas/Opções/Arquivos, que é fixo, esse diretório é, na verdade, o diretório corrente, que pode ser obtido por vários outros caminhos. Um deles é a função CurDir, do Visual Basic. Outro é dado pela propriedade DefautFilePath, com o parâmetro wdCurrentFolderPath: Options.DefaultFilePath(wdCurrentFolderPath)
Cabe, então, a pergunta: se wdCurrentFolderPath fornece o diretório corrente, por que wdDocumentsPath não retorna o valor, firme, do diretório-padrão para gravar documentos? O resultado desse comportamento inconsistente é que, via Options, não há como obter esse diretório de forma confiável. Em minha opinião, wdDocumentsPath deveria informar um diretório fixo – aquele mostrado na caixa de diálogo Opções – e wdCurrentFolderPath forneceria o diretório corrente, como já faz hoje. Fique atento.
116
13 Palavras que valem dinheiro Duas soluções para escrever, automaticamente, valores monetários por extenso Ficha do projeto Projeto: Macro EscreveExtenso e aplicativo Valor por Extenso. O que faz: A macro EscreveExtenso converte números em valores por extenso, em reais. Valor por Extenso presta o mesmo serviço, mas abre uma caixa de diálogo na qual o usuário pode escolher a apresentação do extenso em outras moedas, além do real. Arquivos e requisitos do projeto: Biblioteca Extens32.dll, que fornece os números por extenso. Procedimento EscreveExtenso. Formulário frmExtenso. Conhecimento técnico: Movimentação do cursor num documento Word e manipulação de strings. Nível de programação: Intermediário
No dia-a-dia dos escritórios comerciais, se existe uma tarefa maçante é escrever valores monetários por extenso. Além de exigir atenção do operador, impõe-lhe a digitação de longas fileiras de palavras para descrever os números. É exatamente por causa dessa trabalheira que qualquer mecanismo para escrever valores por extenso constitui um recurso bem-vindo para quem os enfrenta no cotidiano. O projeto deste capítulo apresenta duas soluções para esse problema. A primeira é uma macro específica para o Word, enquanto a outra, com a alteração de apenas uma linha de código, pode ser usada em qualquer outro programa que suporte a linguagem VBA.
A macro EscreveExtenso A macro EscreveExtenso foi o primeiro programa de algum interesse que escrevi no VBA/Word, logo após o lançamento do Office 97. Na verdade, ela consistia na “tradução” de outra macro, criada ainda nos tempos do Word 6.0. O que faz esse código? O acesso à macro é feito através de um botão de comando instalado numa barra de ferramentas. Para o usuário, as coisas funcionam da seguinte maneira. Ele vai redigindo o documento e, num dado instante, surge a necessidade de escrever um valor por extenso. Então, ele digita o valor, por exemplo, R$ 1.389,67 – e clica no botão de comando. A macro responde, transformando o trecho do texto em algo assim: R$ 1.389,67 (um mil, trezentos e oitenta e nove reais e sessenta e sete centavos) O usuário, agradecido, continua a redação. O projeto de EscreveExtenso envolve duas partes: um procedimento escrito em VBA, no ambiente do Word, e uma biblioteca externa, o arquivo Extens32.dll, que é na verdade quem faz o trabalho pesado de converter números em palavras. A idéia do projeto e a macro em VBA são minhas, mas a DLL foi elaborada pelo analista de sistemas Antonio Augusto Ferreira, exímio desbravador das obscuridades da linguagem C++. Vamos, agora, conhecer o funcionamento interno da macro. Como EscreveExtenso vai usar os serviços da biblioteca Extens32.dll, o primeiro passo consiste em declarar a função extenso, residente nessa DLL. Essa declaração deve ser feita num módulo (não pode ser num formulário), da seguinte maneira: Public Declare Function Extenso Lib “Extens32.dll” _ Alias “extenso” (ByVal Valor As String, _ ByVal Retorno As String) As Integer 118
Essencialmente, a macro executa três operações: n
ler o número;
n
enviá-lo à DLL, que devolve o valor por extenso; e
n
escrever esse valor no documento.
A parte mais interessante – e específica do Word – é a primeira. Quando a macro é chamada pelo usuário, o cursor está à direita do número que ele acabou de digitar. Para reconhecer o número, ela desloca o cursor da direita para a esquerda até encontrar o primeiro caractere. Nesse momento, liga o modo de seleção (ExtendMode) e continua o deslocamento para a esquerda, agora em busca de um espaço vazio. Esse espaço é o sinal de que o número começa ali. Como o número está selecionado, armazena-se o seu valor numa variável e o cursor é devolvido para o lado direito do número. O espaço vazio, como se pode perceber, é fundamental. Se ele não existir, o cursor seguirá em marcha à ré, indefinidamente. O usuário precisa observar essa regra. Todo esse trabalho para capturar o número é feito no seguinte trecho do código: Selection.MoveLeft Unit:=wdCharacter, count:=1, _ Extend:=wdExtend While Selection.Text = “ ” If (Selection.Type = wdSelectionIP) And _ (Selection.Start = 0) Then Exit Sub Selection.ExtendMode = False Selection.MoveLeft Unit:=wdCharacter, count:=1, _ Extend:=wdMove Wend Selection.MoveRight Unit:=wdCharacter, count:=1, _ Extend:=wdMove Selection.ExtendMode = True With Selection.Find .Forward = False .Wrap = wdFindStop .Execute FindText:=" “ End With strValor = Selection.Text Selection.ExtendMode = False Selection.MoveRight Unit:=wdCharacter, count:=1, _ Extend:=wdMove 119
Daí para a frente, os procedimentos são mais simples. O valor strValor é enviado à função Extenso, na DLL, e esta devolve as palavras correspondentes, por meio da variável strRetorno (o método é meio tortuoso, mas tem de ser assim, porque funções da API do Windows não retornam texto de forma direta). strRetorno = String$(512, 32) ‘512 espaços x = extenso(strValor, strRetorno) strTmp = “ (” & Trim$(strRetorno) & “)”
O valor direto fornecido pela função é um número inteiro que indica o total de caracteres das palavras do extenso. Como strRetorno, por padrão, tem 512 caracteres, basta pegar os x primeiros – o resto está em branco. No trecho acima, essa tarefa de eliminação de espaços vazios é feita pela função Trim$. Por fim, a terceira parte: as palavras são colocadas entre parênteses e inseridas no texto pelo método Selection.TypeText. Toda essa trabalheira é necessária porque não há uma forma rápida de identificar o número no texto. Isso não ocorreria, por exemplo, no Excel, porque os valores estão contidos em células, que são locais bem determinados. É por isso que o código da macro tem um jeito de poucos amigos. Mas, se considerarmos apenas a parte do extenso, a coisa é simples e rápida. Eis aqui uma função de extenso que faz o mesmo papel, sem a localização do número: Function ValorExtenso (strValor As String) As String Dim strRetorno As String Dim x As Integer, strTmp As String strRetorno = String$(512, 32) ‘512 espaços x = Extenso(strValor, strRetorno) strTmp = Trim$(strRetorno) If x > 0 And strTmp “ real” Then ValorExtenso = strTmp End If End Function
Toda a função está aí. E dessa forma pode ser usada no Excel, no Access, no PowerPoint etc. E até mesmo no Word – desde que se adote outra forma de capturar o número. Outra forma de resolver a questão seria com uma caixa de diálogo (formulário). Em lugar de escrever o número no texto, o usuário o escreveria nessa caixa e acionaria OK. O formulário faria a comunicação com a DLL e se encarregaria de “digitar”, no texto, o valor retornado. O projeto Valor por Extenso baseia-se exatamente nessa segunda possibilidade. 120
O aplicativo Valor por Extenso Valor por Extenso amplia as possibilidades de EscreveExtenso. O projeto se propõe a escrever extenso e, além disso, permitir que o usuário escolha uma moeda – além do real. Valor por Extenso tem um único formulário, frmExtenso, com os seguintes controles: uma caixa de texto para a digitação do número (txtValor), os clássicos botões OK e Cancelar, e uma moldura, Moeda, com quatro botões de opção (opt1 a opt4). Estes últimos correspondem às moedas: real, dólar, franco e iene. Neste projeto, o número por extenso fornecido pela DLL (em reais) é modificado para ajustar-se à moeda escolhida.
F I G U R A 1 3 . 1 O formulário: moedas diferentes
Para identificar a moeda escolhida, o programa conta com a ajuda da variável m_intMoeda, com validade geral no formulário. No evento de inicialização, o valor dessa variável é definido como 1, correspondente ao padrão, que é real. No evento Click de cada botão de opção, m_intMoeda assume novo valor: 2 para dólar, 3 para franco e 4 para iene. A ação começa quando o usuário clica no botão OK. Se a caixa txtValor estiver preenchida, o programa segue em frente. Uma instrução Select Case distribui o trabalho conforme a moeda escolhida: Select Case m_intMoeda Case 1 ‘ real s = “R$ ” strTmp = ExtensoMoedas(strValor, “real”, “reais”) Case 2 ‘ dólar s = “US$ ” strTmp = ExtensoMoedas(strValor, “dólar”, “dólares”) Case 3 ‘ franco s = “F ” strTmp = ExtensoMoedas(strValor, “franco”, “francos”) Case 4 ‘ iene s = “¥ ”
121
strTmp = ExtensoMoedas(strValor, “iene”, “ienes”) End Select Selection.TypeText s & strValor & “ (” & strTmp & “)”
Quem entra em cena, agora, é a função ExtensoMoedas, que recebe o valor e os nomes da moeda no singular e no plural. O que faz essa rotina? Ela chama outra função, ValorExtenso, já mostrada acima, para obter a string fornecida pela DLL. Essa string em ExtensoMoedas, é chamada de ValorComReal, que só serve se a moeda é o real. Para qualquer outra, a string tem de ser modificada. A estratégia para fazer a alteração é a seguinte. Com a função InStr, descobre-se na string onde está a palavra “real” ou “reais”. A partir dessa posição e do comprimento dessas duas palavras, divide-se a string em duas partes, uma antes e outra depois da denominação da moeda. Por fim, recompõem-se as partes, colocando no meio o nome da nova moeda: ExtensoMoedas = sLeft & sMoeda & sRight
No Word 2000, essa rotina seria bem mais simples e menos trabalhosa, graças a uma nova função, Replace, compatível com o Visual Basic 6.0 (o VBA 97 é compatível com o VB 5.0). Com Replace, basta mandar trocar uma palavra pela outra: ExtensoMoedas = Replace (ValorComReal, “reais”, “dólares”)
Entretanto, no projeto, você vai encontrar a forma mais complicada, para garantir a compatibilidade com o Word 97. Voltando ao código. Depois de receber o valor de ExtensoMoedas, a rotina ligada ao clique no botão OK coloca-a entre parênteses, antecede-a com o símbolo da moeda e insere no texto: Nesse caso, portanto, o usuário só precisa escrever “a quantia de”, chamar a caixa de diálogo, digitar o valor e escolher a moeda (se for o caso). Tarefa cumprida.
Para ir mais além 1. A macro EscreveExtenso lida com o objeto Selection um dos mais recorrentes na programação em Word. Analise direitinho como foi feita a captura do número. É importante entender o uso do modo de seleção.
2. Embora estenda as possibilidades da macro EscreveExtenso, o projeto
122
Valor por Extenso também tem seus problemas. Um deles é que algumas moedas são “femininas”, como a libra e a lira. Nesses casos, a string que vem da DLL não vai fazer a concordância de gênero, como em “uma
lira”, “duas libras”, “trezentas rúpias”. Outra limitação é que todos os valores divisionários são centavos.
3. Agora, um limite da própria DLL. A biblioteca foi construída para trabalhar com números no formato usado no Brasil: 12.345,67. Ou seja, o separador de milhares é o ponto e o separador de decimais, a vírgula.
4. Faça uma experiência: antes de clicar no botão OK, acione Tab para fazer
o foco sair da caixa de texto txtValor. Isso lhe mostrará o verdadeiro número enviado à DLL. Verdadeiro porque o aplicativo, antes de enviar o número, ajusta-o para o padrão brasileiro de escrever valores monetários.
5. Mesmo com esses limites em mente, você pode ampliar o programa, incluindo novas moedas. Basta aumentar o número de botões de opção e fazer os ajustes correspondentes.
Nome do documento no rodapé Esta macro é bastante útil: escreve o nome do documento e o número da página no canto direito do rodapé, em letra pequena, corpo 9. Sub InsereNomeNoRodape() With ActiveWindow .View.Type = wdPageView .ActivePane.View.SeekView = wdSeekCurrentPageFooter With Selection .Font.Size = 9 .Fields.Add Range:=Selection.Range, _ Type:=wdFieldEmpty, Text:= _ “FILENAME ” & “ - Pág. ” & “PAGE ”, _ PreserveFormatting:=True .TypeText Text:=" - Pág. “ .Fields.Add Range:=Selection.Range, _ Type:=wdFieldEmpty, Text:= _ “PAGE ”, PreserveFormatting:=True .ParagraphFormat.Alignment = wdAlignParagraphRight End With .ActivePane.View.SeekView = wdSeekMainDocument End With End Sub
Se você preferir o cabeçalho em vez de rodapé, troque a constante wdSeekCurrentPageFooter por wdSeekCurrentPageHeader. Você também pode mudar o tamanho da letra, alinhar o parágrafo à esquerda, definir um nome de fonte. Esteja à vontade. 123
14 Documentos à la carte Torne, realmente, automática a emissão de memorandos e outros papéis Ficha do projeto Projeto: Memorando Numerado 2 O que faz: Esta aplicação automatiza a emissão de memorandos. Preenche todo o cabeçalho, numera e salva o documento. Arquivos e requisitos do projeto: Memonum2.dot, modelo que contém a base do memorando. Formulário frmMemorandos e código interno. Conhecimento técnico: Programação com indicadores (bookmarks) e o objeto Selection.Find. Escrita e recuperação de informações em arquivos INI. Nível de programação: Iniciante
Em dois capítulos anteriores, mostramos como automatizar o controle de documentos numerados. Naquelas soluções, no entanto, o único item de automação estava na numeração do documento. Agora, vamos dar um passo à frente, incluindo outras variáveis no processo de preenchimento automático. Para facilitar a comparação, vamos usar como modelo o mesmo documento básico, um memorando. Além do número, esse documento tem cinco campos cujo preenchimento pode ser automatizado: Nome do destinatário Nome do remetente Assunto do memorando Data Outros destinatários (pessoas a quem será enviada uma cópia do memorando) Para ajustar o modelo do documento a essa nova situação, vamos transformar todos esses campos, que antes estavam destinados à digitação direta, em palavras-chave que devem ser substituídas pelas variáveis correspondentes. Assim, em lugar dos campos, teremos quatro palavras-chave: NomeDoDestinatário, NomeDoRemetente, AssuntoDoMemorando e ListaDeOutrosDestinatários. No campo Data, em vez de usar a técnica de substituição, vamos trabalhar com um indicador — ou seja, um marcador de posição invisível, inserido no texto. Na hora do preenchimento, basta deslocar o cursor para essa posição e lá incluir, via código, a data corrente. Na elaboração do documento, o indicador, chamado DataDoDocumento, é inserido mediante o comando Inserir/Indicador. O modelo modificado de memorando deve ser salvo com o nome de Memonum2.dot. Quando se analisa o “funcionamento” desse memorando, percebe-se que essas cinco variáveis apresentam natureza distinta. A data pode ser fornecida pelo sistema. O assunto do memorando deve ser digitado em cada caso. Deve haver um espaço fixo para armazenar o nome do remetente, porque este certamente não mudará a cada documento. Ao mesmo tempo, é preciso oferecer ao usuário a oportunidade de organizar uma lista de nomes, na qual ele possa escolher o destinatário principal e os destinatários secundários do documento. Tudo indica que precisamos de um formulário para abrigar esses itens. O operador fará todas as definições nesse formulário e, em seguida, dará um comando para que a aplicação se encarregue de preencher os cinco campos automatizáveis. Ao usuário restará, apenas, escrever o corpo do memorando.
Construção do formulário Vamos criar um formulário chamado frmMemorandos. Esse objeto terá os seguintes controles principais:
125
n
um controle multipage (Multipage1) com duas orelhas (Page1 e Page2);
n
dois botões de comando, OK (cmdOK) e Cancelar (cmdCancelar);
n
rótulos Próximo Memo (labMemoNúmero) e PróximoArquivo (labArquivo).
F I G U R A 1 4 . 1 O formulário, destacando a orelha Destinatários
(Page1)
As duas orelhas de Multipage1 também incorporam objetos. Em Page1 (rótulo: Destinatários), existem os seguintes controles: n
caixa de combinação Para (cboPara), com a lista para a escolha do destinatário principal;
n
labTotalNomes, sem rótulo, para indicar o total de nomes disponíveis na caixa Para;
n
caixa de listagem Com Cópia Para (lstComCópia), para abrigar a lista dos demais destinatários;
n
caixa de texto Assunto (txtAssunto).
Em Page2 (rótulo: Configuração), encontram-se os seguintes controles:
126
n
caixa de texto Remetente (txtRemetente);
n
botão de comando Salvar (cmdSalvarNome);
n
botões de comando Editar Lista (cmdEditarLista) e Atualizar Lista (cmdAtualizarLista);
F I G U R A 1 4 . 2 Os objetos da orelha Configuração (Page2) n
rótulo labExplica;
n
rótulos labListaMemo, labMemoIni; labModelo; e labDirDocs.
Os controles não citados – como algumas molduras e rótulos – desempenham papel neutro no projeto, e figuram apenas como elementos de indicação ou de realce visual. Assim como nos projetos anteriores, neste também vamos precisar de um arquivo INI (Memo.ini) para armazenar o contador de números dos documentos e alguns itens de configuração. O conteúdo total de Memo.ini será algo como o seguinte: [Contador] Ano=2000 MemoNum=32 Remetente=Arthur Rimbaud
Precisaremos, também, de um arquivo texto, Listamemo.txt, no qual ficará guardada a lista dos destinatários. Esse arquivo poderia fazer parte de um banco de dados. No entanto, normalmente, a lista de pessoas a quem se envia memorandos não passa de umas poucas dezenas de nomes. Por isso, um arquivo texto resolve bem a questão, com muito menos trabalho.
O código Passemos ao código. A primeira tarefa, ao inicializar o formulário, será ler o arquivo Listamemo.txt e transportar para a caixa de combinação cboPara todos os nomes de destinatários cadastrados. Para essa tarefa é convocada a rotina Preen- 127
cheCombo. Esta, depois de incluir os nomes na lista de cboPara, exibe o total deles na etiqueta labTotalNomes. Outra tarefa executada durante a inicialização do formulário é ler o arquivo Memo.ini e, com base nas informações registradas nele, definir o número do próximo memorando a ser emitido e o nome do arquivo que o conterá. Se, por exemplo, o número no arquivo INI é 32, o número do memorando será 032/00 – fórmula: número/ano – e o arquivo assumirá o nome mm032-00.doc.
F I G U R A 1 4 . 3 O formulário, logo após a inicialização
Outra informação lida no arquivo INI deve ser o nome do remetente, que é transferido para a caixa de texto txtRemetente, na orelha Configuração (Page2). Após a inicialização, nenhuma informação é exibida nas caixas Para e Com Cópia Para e somente a primeira delas contém uma lista de nomes. A pessoa que emite o memorando deve escolher o destinatário na lista da caixa Para. Ao fazer isso, ele provoca a execução da rotina cboPara_Click, que copia a lista de cboPara para cboComCopia. Todos os itens são copiados, menos um – aquele que foi selecionado como destinatário principal. Isso evita que uma mesma pessoa seja incluída como receptor primário e secundário de um memorando. A exclusão do nome já escolhido em cboPara é garantida pelo seguinte trecho de código: intX = cboPara.ListIndex ‘ Pega o num. do item selecionado lstComCopia.Clear For i = 0 To cboPara.ListCount - 1 ‘Copia todos os itens, menos o selecionado em cboPara If i intX Then lstComCopia.AddItem cboPara.List(i) Next i
Primeiro, pega-se o índice do item selecionado (intX), que corresponde ao 128 valor da propriedade ListIndex de cboPara. Em seguida, percorre-se com um
loop todos os itens de cboPara, copiando-os para lstComCopia. Exclui-se apenas aquele cujo índice seja igual ao do valor escolhido em cboPara. A operação fundamental da aplicação ocorre a partir do momento em que o usuário clica no botão OK. Em primeiro lugar, a rotina cmdOK_Click chama a função TestaPreenchimento para saber se todos os itens fundamentais foram preenchidos: destinatário, assunto e remetente. A escolha de itens na caixa de listagem lstComCopia é opcional. Vale observar que esta última tem a propriedade MultiSelect ajustada para fmMultiSelectMulti (=1). Essa definição permite que cada clique num item o selecione automaticamente. Em sentido inverso, um clique num item já escolhido o “desseleciona”. Se todos os preenchimentos estão corretos, passa-se à outra fase. Oculta-se o formulário e cria-se um documento novo, baseado no modelo Memonum2.dot. Chegamos ao momento de preencher os campos automatizáveis do memorando. Primeiro, vamos posicionar o cursor no indicador DataDoDocumento e, lá, inserir a data: strData = Day(Now) & “ de ” & _ Format$(Now, “mmmm”) & “ de ” & Year(Now) Selection.GoTo What:=wdGoToBookmark, Name:="DataDoDocumento" Selection.TypeText strData
Depois, é preciso determinar se o usuário escolheu um ou mais nomes como destinatários de cópias do memorando. A propriedade Selected do objeto lstComCopia indica quais itens estão selecionados. Portanto, com um loop que percorre todos os itens, fica fácil acumular uma string com todos os nomes selecionados, separando-os com ponto-e-vírgula. A próxima etapa consiste em definir MatrizTexto, uma matriz de duas dimensões – uma para o valor da variável indicado pelo usuário e a outra para a palavra-chave que deve ser substituída no documento. Feito isso, usa-se nosso já conhecido objeto Selection.Find para executar a operação de busca e substituição. As tarefas finais são salvar o arquivo com um nome já definido desde a abertura do formulário e armazenar, no arquivo INI, o número usado no documento atual. Além da operação básica, este projeto envolve outros aspectos que estão todos na orelha Configuração. Quando o usuário clica no botão Salvar Nome, dispara uma rotina que salva no arquivo INI o nome digitado na caixa de texto txtRemetente. O botão cmdEditarLista abre o Bloco de Notas (Notepad.exe) e carrega o arquivo Listamemo.txt, que contém, na lista de destinatários, um nome em cada linha. O usuário pode incluir, apagar ou editar nomes. Para que as mudanças introduzidas na lista tenham efeito imediato no aplicativo, é necessário que ele, após a edição, clique no botão Atualizar Lista. Este invoca, outra vez, a rotina PreencheCombo que limpa a caixa de combinação e a caixa de lista com os nomes de destinatários, e renova o conteúdo da primeira. 129
F I G U R A 1 4 . 4 A lista de destinatários
Na área de declarações do formulário, são definidos como constantes do tipo String os valores dos arquivos e diretórios envolvidos no programa: sDIR_LISTAMEMO
Diretório do arquivo Listamemo.txt, que contém o cadastro de destinatários.
sDIR_MEMOINI
Diretório do arquivo Memo.ini, que armazena as configurações do programa.
sDIR_SALVAMEMO
Diretório-padrão em que os novos memorandos devem ser salvos.
sDIR_MODELO
Diretório onde fica o modelo que serve de base para os memorandos (no exemplo, Memonum2.dot).
Para que o programa funcione corretamente com outros diretórios, modifique essas constantes. Os valores definidos por elas estão mostrados no painel Arquivos e Diretórios da orelha Configuração.
Para ir mais além 1. Torne o gerador de memorandos mais amigável para o usuário, armaze-
130
nando, no arquivo INI, os diretórios que estão definidos como constantes. Para isso, é preciso eliminar as definições de constante e ler as informações no INI como variáveis. Isso exigirá uma contrapartida: deverá haver um lugar para o usuário indicar novos diretórios. Você pode, por
F I G U R A 1 4 . 5 O resultado final: resta apenas escrever o texto
principal do documento
exemplo, criar uma terceira orelha no objeto Multipage apenas para a configuração de Arquivos e Diretórios. Outra opção é incluir pequenos botões ao lado dos rótulos que exibem os arquivos e diretórios. Esses botões abririam uma caixa de diálogo para o usuário escolher, em cada caso, o novo diretório.
2. O gerador de memorandos não permite que você envie memorandos
com cópia para um número muito grande de destinatários. Isso porque ele usa a técnica de acumular os nomes numa string e depois inserir essa string no documento. Você pode contornar essa limitação inserindo os nomes um a um no documento. Experimente.
Caixa sempre aberta Uma desvantagem das caixas de combinação é que exigem, pelo menos, dois cliques para a escolha de uma opção: o primeiro abre a lista, e o outro seleciona uma alternativa. Se você achar adequado, pode economizar um clique para o usuário, fazendo o seguinte. Na rotina correspondente ao evento em que o controle recebe o foco (Enter), aplique o método DropDown. Exemplo, para uma caixa chamada cboTratamento: Private Sub cboTratamento_Enter() cboTratamento.DropDown End Sub
131
15 Para não repetir o repetido Ensine o Word a contar e marcar palavras já usadas dentro de um documento Ficha do projeto Projeto: Contador de Expressões O que faz: Conta o número de vezes que uma palavra ou expressão aparece num texto do Word. Opcionalmente, marca as expressões encontradas. Usuários potenciais: Escritores, revisores e qualquer pessoa preocupada com o excesso de repetições de palavras ou expressões. Arquivos e requisitos do projeto: Formulário frmContaExpressao e código interno. Conhecimento técnico: Operações com o objeto de busca do Word (Selection.Find) do VBA, além da manipulação de arquivos texto. Nível de programação: Intermediário
Se você quisesse saber quantas vezes aparece uma determinada palavra ou expressão num texto do Word 97 ou do 2000, qual comando usaria? Antes que você perca tempo vasculhando menus e barras de ferramentas, aí vai a resposta: nenhum. Não existe no Word um recurso para contar palavras dessa forma. Mas, com uma pequena ajuda de nosso amigo VBA, podemos adicionar esse comando ao processador de texto. É disso que trata o projeto atual. Pensemos um pouco. Precisamos de um dispositivo que percorra um documento de alto a baixo e, durante essa varredura, identifique uma palavra, ou seqüência de palavras, e conte quantas vezes ela aparece. No final, esse recurso deve informar: a expressão foi encontrada X vezes. Por onde começar? A opção Editar/Localizar (ou Selection.Find, no VBA) desenvolve uma tarefa bem próxima. Você fornece uma palavra, ela varre o texto e seleciona a primeira ocorrência. Com o comando Localizar Próxima, nova ocorrência é selecionada – e assim por diante, até o final do documento. Podemos partir dessa característica já existente e modificá-la para atingir nosso propósito. Observe que o comando Localizar, ao encontrar uma palavra, pára. Precisamos que ele siga em frente e, além disso, conte o número de ocorrências encontradas. Com essas idéias em mente, armemos a estrutura de nossa solução. É preciso oferecer ao usuário uma caixa de diálogo na qual ele indique qual palavra deve ser procurada. Vamos usar um formulário (frmContaExpressao) que contém, basicamente: uma caixa de texto, para a digitação da expressão a ser buscada e contada; um botão de comando, Contar (cmdContar), que dispara a ação; e outro, Cancelar (cmdCancelar), que encerra o aplicativo. Com os elementos acima, podemos montar a interface de um programa para contar o número de vezes que uma palavra ou expressão aparece num texto. Mas que tal incluir outras funções nesse aplicativo? Com um pouco mais de esforço, ele poderá executar também as seguintes tarefas adicionais: n
Localizar somente palavras ou expressões inteiras. Ou seja, quando a expressão desejada é “para”, só vale a palavra correspondente. Expressões como “paralelas” e “comparar” não serão contadas.
n
Coincidir maiúscula e minúscula – Nesse caso, a busca leva em conta o tipo de letra. Se o argumento é “lua”, “Lua” (com L maiúsculo) não será considerada.
n
Marcar palavra encontrada – Com essa opção, a expressão encontrada será destacada com um pincel marcador, em todas as suas ocorrências.
n
Desmarcar palavra encontrada – Operação inversa da anterior.
Estas duas últimas opções são úteis para quem escreve ou revisa textos. Ao reler um documento que acabou de escrever, a pessoa nota que repetiu muitas vezes uma determinada palavra. Então, usa a opção Marcar. Assim, a expressão fica destacada no texto, dando-lhe a oportunidade de empregar sinônimos ou mudar a estrutura de alguns trechos para eliminar as repetições, principalmente as muito próximas. No final, a palavra permanece destacada 133
em alguns lugares. Usa-se a outra opção, Desmarcar Palavra Encontrada, para desfazer os destaques. No processo de revisão, é possível que o usuário busque mais de uma vez a mesma expressão. Isso traz à mente uma idéia: manter uma espécie de histórico das expressões procuradas anteriormente. Dessa forma, ele pode reaproveitar palavras já digitadas. Com essas novas características, o formulário que apenas esboçamos mais acima precisa ser revisado. Em lugar de uma caixa de texto para receber a palavra procurada, vamos usar uma caixa de combinação (cboExpressão). Essa caixa, além da função de receber o texto digitado, armazenará a lista das últimas palavras procuradas. Haverá também quatro caixas de verificação para as opções de busca: Encontrar Palavra Inteira (chkPalavraInteira); Coincidir Maiúscula/Minúscula (chkCoincidirLetra); Marcar Palavra Encontrada (chkMarcar); e Desmarcar Palavra Encontrada (chkDesmarcar). Por último, há o botão Limpar Histórico, que elimina a lista de palavras procuradas anteriormente exibidas na caixa cboExpressão. Portanto, nosso aplicativo vai encontrar e contar palavras, localizá-las, segundo critérios de busca, e manter um histórico das últimas expressões procuradas. Para isso, o formulário de suporte terá o aspecto exibido na Figura 15.1.
F I G U R A 1 5 . 1 Contador de expressões: com busca e histórico
O desenho do formulário, em si, não apresenta nenhuma dificuldade. Passemos ao código. O fundamental de todo o funcionamento do aplicativo vai acontecer quando o usuário clicar no botão Contar. Vamos pensar, portanto, na rotina associada ao acionamento desse botão.
O mecanismo de busca e contagem 134
A rotina de localização e contagem de expressões pode ser delineada nos seguintes passos:
1. Para começar, uma providência administrativa. Se o usuário clicar no
botão Contar sem ter digitado nada na caixa de texto, usa-se um Exit Sub para encerrar, aí mesmo, a execução do código. O comando Localizar só é capaz de operar usando, como argumento de busca, uma variável do tipo String – que aceita, no máximo, 255 caracteres. Então, aproveitamos a mesma seqüência de Ifs para verificar essa limitação e avisar o usuário. Detalhe sutil: esse bloco é também um recurso disfarçado de tratar erros, já que evita a procura de um valor vazio ou de tamanho superior ao limite máximo.
2. Para garantir que a contagem varrerá o texto completamente, vamos mover o cursor para o início do documento: Selection.MoveStart Unit:=wdStory
3. Iniciemos o processo de busca, definindo alguns parâmetros importantes: Selection.Find.Text = sExpressão Selection.Find.Forward = True
Essas linhas indicam que: a) o texto a ser procurado é sExpressão, string que corresponde ao que foi digitado na caixa combinação do formulário; e b) o processo deve ser feito do início para o fim do documento.
4. Por enquanto, ainda não começamos a operação de busca. Ela só é ativada, de fato, depois do comando Selection.Find.Execute
Como já vimos, o comando Localizar apenas encontra a expressão procurada e pára. Portanto, a linha de código acima põe em ação o mecanismo de busca uma única vez. Mas será que ele encontrou alguma coisa? Se encontrou, a propriedade Found (encontrado), do objeto Selection.Find é verdadeira. O trecho completo fica sendo o seguinte: Selection.Find.Execute While Selection.Find.Found = True n = n + 1 Selection.Find.Execute Wend
5. A primeira linha, já vimos, executa a primeira busca. Em seguida, vem
um loop do tipo While/Wend. Se uma palavra foi encontrada, o fluxo do programa entra no loop e soma 1 ao valor de n, um número inteiro, antes zerado. A busca é executada mais uma vez. De novo, pela ação do loop, testa-se se a expressão foi encontrada e acumula-se mais 1 no contador n. O processo se repete até o final do documento. 135
6. Agora, uma caixa de mensagem apresenta o resultado do trabalho com uma frase do tipo: “A expressão ‘pôr-do-sol’ foi encontrada 5 vezes.”
Com esses seis passos, descrevemos o funcionamento do mecanismo de busca e contagem de palavras usado no projeto. Mas – você deve ter notado – não falamos nas opções de busca. Como fica a localização das expressões se o usuário escolher uma ou mais dessas opções? Voltemos ao código. As alternativas Encontrar Palavra Inteira e Coincidir Maiúscula/ Minúscula estão previstas no objeto Find, respectivamente, com as propriedades MatchWholeWord e MatchCase. Como as duas assumem valores booleanos (True/False), basta transferir para elas, na hora da busca, o valor da caixa de verificação correspondente no formulário: With Selection.Find .MatchCase = chkCoincidirLetra.Value .MatchWholeWord = chkPalavraInteira.Value
Falta, agora, a questão marcar/desmarcar palavra encontrada. Ela se resolve no loop While/Wend, já citado acima. Veja como: While Selection.Find.Found = True If chkMarcar = True Then Selection.Range.HighlightColorIndex = wdBrightGreen ElseIf chkDesmarcar = True Then Selection.Range.HighlightColorIndex = wdWhite End If n = n + 1 Selection.Find.Execute Wend
Sempre que uma palavra é encontrada, verifica-se o valor da caixa chkMarcar. Se é verdadeiro, aplica-se à expressão (que está selecionada) a cor verde-claro, correspondente à constante intrínseca do Word wdBrightGreen. Se, ao contrário, o usuário escolheu a caixa Desmarcar, aplica-se à seleção a cor branca (wdWhite). Se nem uma caixa nem a outra estão ativadas, a rotina faz apenas a localização e a contagem das expressões repetidas. Para que as opções Marcar/Desmarcar não firam a lógica, as rotinas chkMarcar_AfterUpdate e chkDesmarcar_AfterUpdate garantem que se uma estiver ativa, a outra não deve estar.
Histórico de busca Até aqui, resolvemos tudo o que se refere à localização, opções de busca e contagem. Resta-nos, ainda pendente, o histórico de palavras procuradas. Para manter os argumentos de busca entre um e outro acionamento do Contador de 136 Expressões, temos de armazenar em algum lugar esses argumentos.
A forma mais simples de fazer isso é recorrer a um arquivo texto. Cada busca acrescenta nova palavra à lista existente na caixa cboExpressão. Quando o aplicativo se fecha, o conteúdo dessa lista deve ser transferido para o arquivo. E cada vez que o programa for aberto, a lista do arquivo deve ser copiada para cboExpressão. Vamos ao código. A acumulação da última palavra na caixa de combinação é automática, desde que a caixa tenha como propriedade Style o valor fmStyleDropDownCombo (=0), que é o padrão. Então, uma parte do problema se resolve sozinha. A outra, armazenar a lista num arquivo, deve ser executada no fechamento do formulário. Escolhemos o evento UserForm_QueryClose e, nele, fazemos a seguinte operação. Decidimos que o histórico de palavras procuradas não deveria ter mais que vinte expressões. Trata-se de um número razoável, acima do qual não faz muito sentido armazenar palavras. Por causa disso, verificamos quantos itens existem na lista de cboExpressão. Se mais de vinte, trabalhamos somente com os vinte primeiros, que são gravados, cada um, como uma linha de um arquivo texto. Esse arquivo se chama Historia.plv e é gravado no diretório de modelos do Word – que varia conforme a versão do programa. Poderíamos tê-lo chamado Historia.txt, mas achamos que a estranha extensão PLV (de palavra) e o diretório de modelos deixam o histórico mais a salvo de apagamentos involuntários. Um detalhe: a cada fechamento, o arquivo é apagado e recriado com o novo conteúdo da caixa de combinação. Portanto, fechado o formulário, as informações da caixa de combinação cboExpressão ficam armazenadas no arquivo Historia.plv. Agora, vejamos o que acontece quando se abre o formulário. A primeira ação da rotina UserForm_Initialize é verificar se o arquivo de histórico existe. Em caso positivo, ele é lido, linha a linha, para a caixa cboExpressão. Caso contrário, a caixa fica vazia: não existe histórico, o qual será criado no próximo fechamento do formulário, se o usuário tiver realizado alguma busca. O botão Limpar Histórico, simplesmente, emprega o método Clear para limpar a caixa cboExpressão: cboExpressão.Clear
Com a caixa vazia, a rotina de fechamento do form elimina o arquivo existente, e não tem o que salvar. Assim, na próxima abertura do formulário, a caixa cboExpressão continuará vazia. Fim de projeto.
Para ir mais além 1. O Contador de Expressões centra sua atividade na busca de termos.
Você pode ampliá-lo, incluindo a opção de substituir a expressão en- 137
contrada por outra. Isso exigirá algum esforço de design, a fim de exibir o espaço para a nova expressão quando o usuário escolher Substituir. Uma fonte de inspiração pode ser a própria caixa de diálogo Localizar/Substituir do Word.
2. Se achar conveniente, substitua o número máximo de expressões armazenadas no histórico (20). Basta trocar, na rotina UserForm_QueryClose, o valor da constante cnsMAX_PAL.
Objetos azul-piscina Ao criar formulários com muitos campos de texto, você pode adicionar um efeito visual para dar ao usuário uma indicação de onde se encontra o foco. O truque consiste em trocar a cor de fundo do controle, quando ele recebe o foco, e retorná-la ao padrão, branco, quando a ação passa a outro objeto. A operação é simples. Crie duas subs, uma chamada FundoCor e outra FundoBranco: Private Sub FundoCor() ActiveControl.BackColor = RGB(192, 255, 255) End Sub Private Sub FundoBranco() ActiveControl.BackColor = wdColorWhite End Sub
A cor RGB, acima, é o azul-piscina. Aos meus olhos, é a mais agradável para a leitura. Mas você pode escolher outra qualquer. No evento Enter do controle (correspondente ao GotFocus do Visual Basic), chame FundoCor. No evento Exit (LostFocus, no VB), chame FundoBranco. Aviso prévio: não funciona se o objeto estiver dentro de uma moldura.
138
16 Todas as fontes, na tela e no papel Um aplicativo para visualizar e imprimir as fontes instaladas no sistema Ficha do projeto Projeto: Lista de Fontes O que faz: Relaciona, numa caixa de diálogo, todas as fontes instaladas no sistema e exibe amostras de cada uma delas, em diferentes tamanhos e estilos. Permite, ainda, imprimir um catálogo personalizado das fontes instaladas. Arquivos e requisitos do projeto: Formulário frmFontes e código interno. Formulário frmFontesCatálogo e código interno. Conhecimento técnico: Trabalho com coleções de objetos e matrizes. Rotina de classificação. Nível de programação: Intermediário
Neste projeto, o objetivo básico é criar um mostruário de fontes. Com ele, o usuário poderá visualizar todos os recursos de letras disponíveis em seu sistema e experimentar, interativamente, tamanhos e estilos. Poderá ainda, se quiser, imprimir um catálogo das fontes, exibindo-as no tamanho que achar mais conveniente às suas necessidades. Para levar à frente esta idéia, precisamos construir um formulário (frmFontes) com o seguinte conjunto de controles ativos: a) uma caixa de listagem (lstFontes), que receberá a lista com os nomes das fontes instaladas no sistema; b) uma caixa de texto (txtAmostra), destinada a exibir a visualização de qualquer fonte escolhida na caixa lstFontes; c) dois botões de comando, cmdMais e cmdMenos – o primeiro para aumentar, e o outro para reduzir o tamanho da fonte visualizada na caixa de texto. d) dois botões de comutação (toggle buttons, que o VBA em português chama de botões de ativação), tmdItálico e tmdNegrito, que aplicarão os estilos itálico e negrito ao texto exibido em txtAmostra; e) um botão de comando Aplicar (cmdAplicar), para transferir a um texto selecionado num documento a formatação de fonte mostrada na caixa txtAmostra; f) uma caixa de texto comum (txtTamanho), que exibirá o tamanho da fonte, em pontos, e também aceitará a digitação de valores por parte do usuário; g) um botão Fechar (cmdFechar), para encerrar a aplicação; h) um rótulo, labNumFontes, que indicará ao usuário o total de fontes listadas; i) um botão Catálogo (cmdCatálogo), porta de acesso a outro formulário, a partir do qual se poderá imprimir um mostruário de fontes; e j) duas etiquetas auxiliares, uma para identificar a caixa com a lista de fontes e a outra para a caixa texto, com a amostra de texto. Para que você possa fazer uma idéia concreta de seu trabalho final, veja na Figura 16.1 como ficará essa caixa de diálogo.
140
Não há dificuldade no traçado desses objetos. Destaco, apenas, que a caixa txtAmostra deve ter a propriedade MultiLine ajustada para True. Se isso não for feito, não será possível visualizar o texto em mais de uma linha. No mais, o trabalho essencial consiste em manter o equilíbrio e o alinhamento dos objetos – coisa que se aprende e se aperfeiçoa com a experiência.
F I G U R A 1 6 . 1 Visualização do projeto concluído
Muito bem: está pronta a interface do aplicativo. Mas, por enquanto, ele é como um aparelho de som que tem apenas o gabinete. Não há, dentro dele, nenhum circuito que o faça funcionar. Nossa próxima tarefa é, justamente, adicionar os circuitos – ou seja, o código de programação. Comecemos pela caixa de listagem lstFontes. Ela deve entrar em funcionamento tão logo o formulário seja exibido. É preciso, portanto, traçar para ela algum circuito que a preencha com o rol das fontes instaladas no sistema. O local mais adequado para colocar os fios necessários é a rotina UserForm_Inialize, que é uma das primeiras a ser executadas quando se abre um formulário. Para preencher a caixa de listagem, bastaria percorrer a coleção de fontes instaladas. Assim: Dim fName For Each fName In FontNames lstFontes.AddItem fName Next fName lstFontes.ListIndex = 0
A primeira linha declara a variável fName, um objeto. Depois, um loop For/Next varre a coleção FontNames e, com o método AddItem, vai-se adicionando, um a um, os nomes das fontes à caixa de listagem. Por fim, coloca-se a barra de seleção no primeiro item da lista (ListIndex = 0). Pronto, a caixa está preenchida com todos os nomes das fontes existentes no sistema. Mas há um problema: se você fizer como mostrado acima, obterá os nomes de fontes desordenados, não em ordem alfabética, o que dificulta a localização. Se estivéssemos trabalhando com a linguagem Visual Basic, bastaria ligar propriedade Sorted (classificada) da caixa de lista. Infelizmente, não existe tal recurso nas caixas de lista do VBA. Então, se quisermos organizar os nomes das fontes em ordem alfa, será necessário fazer isso “no braço”.
141
O primeiro passo é criar uma matriz, sFontes, com tantos elementos quantos sejam os nomes de fontes da coleção FontNames: Dim i As Integer Dim fCount As Integer fCount = FontNames.Count ReDim sFontes(fCount) As String ‘ Preenche matriz com os nomes, desordenados For i = 1 To fCount sFontes(i) = FontNames(i) Next i ‘ Ordena os nomes de fontes OrdemAlfa sFontes(), 1, fCount ‘ Preenche a caixa lstFontes For i = 1 To fCount lstFontes.AddItem sFontes(i) Next i
Em seguida, lançamos mão do procedimento chamado OrdemAlfa, que recebe toda a matriz sFontes e a classifica em ordem alfabética. Por fim, varremos a matriz e preenchemos a caixa de listagem com os nomes das fontes. A rotina OrdemAlfa usa um algoritmo de classificação conhecido como Bubble Sort, o “método da bolha”. A etiqueta labNumFontes, como vimos, deve exibir o número de fontes listadas. Para isso, basta recorrer à propriedade ListCount da caixa de listagem: labNumFontes = lstFontes.ListCount
Uma alternativa, para não usar o procedimento OrdemAlfa, é recorrer ao método SortArray, do WordBasic, a linguagem de macros do Word que foi substituída pelo Visual Basic. No caso, em lugar de chamar OrdemAlfa, usaríamos: WordBasic.SortArray sFontes()
Funciona perfeitamente, no Word 97 e no 2000. O problema dessa alternativa é que a Microsoft não estimula o uso do WordBasic. Isso porque, numa próxima versão do Word, o objeto WordBasic pode desaparecer. Então, de uma hora para outra, seu programa, baseado nele, passaria a não funcionar. Ainda na rotina UserForm_Initialize, é interessante preencher a caixa de texto txtAmostra, para que o usuário visualize a primeira fonte da lista logo na abertura do formulário. Aqui é simples: projetamos nessa caixa de texto o alfa142 beto completo, em maiúsculas e minúsculas, mais os dez algarismos. Quando a
primeira fonte for selecionada, o quadro de amostra exibirá suas letras conforme essa fonte. O tamanho inicial é fixado em 8 pontos. Assim, a caixa txtTamanho exibe o número 8. Para os botões de comutação tmdItálico e tmdNegrito, definiu-se que sua propriedade TripleState é falsa. Essa propriedade permite que esses objetos tenham três estados: verdadeiro, falso e nulo (ou seja, sem valor). Em nosso caso, interessa que eles tenham apenas um comportamento binário: negrito (sim ou não) e itálico (sim ou não). Até aqui, preenchemos a lista de fontes e a caixa de texto. Mas, quando o usuário clicar numa opção de lstFontes, é necessário que essa opção seja aplicada à caixa txtAmostra. Para isso, basta incluir uma linha na rotina lstFontes_Click: txtAmostra.Font.Name = lstFontes.Value
Isso estabelece uma vinculação entre a listagem e a amostra. Agora, avancemos para os controles que ficam logo abaixo da caixa txtAmostra. O botão tmdItálico liga ou desliga o estilo itálico nas letras do quadro de amostra. Ao receber um clique, ele permanece pressionado (posição ligado) ou retorna ao normal (desligado). Apenas uma linha na rotina tmdItálico_Click resolve essa questão: Private Sub tmdItálico_Click() txtAmostra.Font.Italic = tmdItálico.Value End Sub
Linha idêntica é usada para responder ao evento Click do botão tmdNegrito. Em seguida, vêm os botões cmdMais e cmdMenos. O primeiro adiciona 1 ponto ao tamanho da letra exibida em txtAmostra; o outro subtrai um ponto. Vejamos o exemplo de cmdMais: txtAmostra.Font.Size = txtAmostra.Font.Size + 1
Fácil, não? Sim, mas não basta. Naturalmente, não será possível aumentar (ou diminuir), indefinidamente, o tamanho da letra. Há, então, os limites – que, em nosso caso, foram definidos em 6, o inferior, e 128, o superior. Isso significa que, se o usuário tentar extrapolar essas balizas, o programa não deve permitir. Por isso, antes de somar (ou subtrair, no caso de cmdMenos) um ponto ao tamanho da fonte, é preciso verificar se o novo número não cairá na zona proibida. Além disso, o número exibido em txtTamanho também deve ser atualizado. O código completo associado ao evento Click do botão cmdMais fica assim: If Not TamanhoAceitável(MAIS) Then Exit Sub txtAmostra.Font.Size = txtAmostra.Font.Size + 1 txtTamanho = CInt(txtAmostra.Font.Size)
143
A constante MAIS (=1) é enviada a uma função, TamanhoAceitável, que responde se o novo número está ou não dentro do campo permitido. Em caso negativo, a rotina é abortada (Exit Sub), evitando que o valor da fonte extrapole os limites definidos. Para o botão cmdMenos, a rotina é similar: troca-se o mais pelo menos e envia-se à função TamanhoAceitável a constante MENOS (=2). Agora, vamos à caixa de texto txtTamanho. Além de receber o número atribuído ao tamanho da fonte, ela também aceita digitação. Para reduzir a possibilidade de erros, vamos proibir que o usuário digite nela qualquer coisa que não seja número. Para isso, usamos o seguinte código no evento txtTamanho_KeyPress: Select Case KeyAscii Case 8, 48 To 57 ‘OK Case Else ‘ anula a digitação KeyAscii = 0 End Select
Com as linhas acima, a caixa aceita somente as teclas de código KeyAscii igual a 8 (retrocesso) ou de 48 a 57 (0 a 9). Todo o resto é anulado. Mas isso ainda não impede que o usuário digite números inaceitáveis: 1000, 5500, 99999. É preciso, portanto, evitar que o erro aconteça. Vamos controlar o que foi digitado por meio do evento Exit, que ocorre quando a caixa de texto perde o foco. A rotina txtTamanho_Exit ganha o seguinte código: Dim intTam As Integer intTam = Val(txtTamanho) If intTam > 128 Or intTam < 6 Then MsgBox “Tamanho de fonte muito grande ou muito pequeno.”, vbOKOnly, “Fontes” ‘ retorna o valor anterior txtTamanho = Int(txtAmostra.Font.Size) Exit Sub End If ‘ modifica o tamanho txtAmostra.Font.Size = intTam
Se o valor ultrapassa os limites estabelecidos, o número em txtTamanho é redefinido com o tamanho da fonte ainda ativo na caixa txtAmostra. Caso contrário, a fonte em txtAmostra assume o tamanho digitado. Em frente, para o botão Aplicar. Esse controle transfere a formatação existente na caixa de texto txtAmostra para uma área selecionada ou todo um documento. Ao ser acionado, ele apresenta uma caixa de diálogo avisando ao usuário a operação que será feita: 144
F I G U R A 1 6 . 2 Mensagem aberta pelo botão Aplicar
Se a resposta for sim, o seguinte código é executado, copiando todas as características da fonte em txtAmostra para o trecho selecionado no documento. With Selection.Font .Name = txtAmostra.Font.Name .Italic = txtAmostra.Font.Italic .Bold = txtAmostra.Font.Bold .Size = txtAmostra.Font.Size End With Application.ScreenRefresh
Observe a última linha acima. O papel dela é forçar a atualização do texto no documento. Sem ela, a formatação ocorre, mas você só vai vê-la depois de fechar o formulário da aplicação Lista de Fontes. A primeira parte do projeto está concluída. Você pode ver, na tela, todas as fontes instaladas no seu micro, em diferentes tamanhos e estilos. Resta somente o botão Catálogo. Ele abre outro formulário, a partir do qual se poderá imprimir uma amostra das fontes existentes no computador. Isso será resolvido no bloco, a seguir.
Catálogo de fontes Nesta parte do projeto, vamos construir um pequeno aplicativo que permita ao usuário comandar a impressão de um catálogo com amostras de todas as fontes existentes em seu computador. Para isso, precisamos desenhar outro formulário, frmFontesCatálogo. Um clique no botão Catálogo (formulário frmFontes) abre esse novo formulário. Na verdade, frmFontesCatálogo funciona como um projeto independente. Simples, este novo projeto só oferece como opção a possibilidade de escolha do tamanho da letra que será usada na confecção do catálogo. Primeiro, tracemos o formulário frmFontesCatálogo. Trata-se de um pequeno form com uma caixa de combinação (cboTamanho) que oferece tamanhos de fontes de 8 a 36 pontos. Além dela, há apenas os botões Criar Catálogo (cmdOK) e Cancelar (cmdCancelar). 145
F I G U R A 1 6 . 3 O formulário frmFontesCatálogo
Na rotina UserForm_Initialize encontra-se o processo de inicialização da caixa cboTamanho. Ela é preenchida com números de 8 a 36, sendo que o valor 14 é assentado como padrão. A criação do catálogo fica a cargo da rotina CatálogoDeFontes, que recebe, como parâmetro, o tamanho da fonte escolhido na caixa Tamanho. Vejamos como trabalha essa rotina. Em primeiro lugar, ela abre novo documento para receber o catálogo. Esse procedimento constitui também uma precaução contra erros. Evita, por exemplo, que a rotina escreva em outro documento. Em seguida, ela escreve um título para o documento, no qual é indicado o tamanho da fonte escolhido pelo usuário: Por fim, num loop, são percorridos todos os nomes de fontes disponíveis no sistema. À medida que surge novo nome, o texto básico (todas as letras, maiúsculas e minúsculas do alfabeto) vai assumindo essa fonte, com o tamanho indicado. O resultado é um documento como o mostrado na Figura 16.4.
146
F I G U R A 1 6 . 4 O catálogo: pronto para ser impresso
Para completar o projeto, vamos definir o código do botão Criar Catálogo (cmdOK). Primeiro, ele oculta o formulário. Depois, chama o procedimento CatálogoDeFontes, passando o valor escolhido pelo usuário na caixa cboTamanho. Por fim, fecha o formulário. Por que esconder o formulário? Para que a rotina CatálogoDeFontes funcione, o form precisa estar ativo. Então, ele é ocultado e só se fecha depois da criação do catálogo. A rotina completa do botão é: Private Sub cmdOK_Click() Me.Hide CatálogoDeFontes cboTamanho Unload Me End Sub
Este projeto resultou da união de dois aplicativos separados, um para exibir as fontes na tela e o outro para produzir um catálogo imprimível. Assim, o botão Catálogo passou a ser o eixo de ligação entre os dois formulários. Mas, se você analisar direito, vai notar que o formulário frmFontesCatálogo tem apenas um item que o justifica: a caixa de combinação na qual o usuário deve escolher o tamanho da fonte. Por causa disso, decidi retrabalhar o layout do formulário principal, frmFontes, e eliminar o segundo formulário. O resultado visual está na figura a seguir.
F I G U R A 1 6 . 5 O projeto, reestruturado para um único
formulário
O que mudou? Todas as funções de frmFontesCatálogo foram absorvidas por frmFontes. Para o usuário, é bem mais cômodo, já que tudo está na mesma tela. Aliás, o programador também ganha, porque se livra da necessidade de bolar artimanhas para coordenar o fechamento dos dois formulários. O projeto, refeito, está disponível no disco que acompanha este volume, no modelo ListFont2.dot. 147
Por fim, vale discutir um aspecto interessante, relacionado ao encerramento dos aplicativos VBA. Projetos contidos dentro de um modelo são abertos, obrigatoriamente, com a apresentação de um documento baseado nesse modelo.Quando se trata de uma aplicação que se esgota em si mesma – ou seja, não gera documentos –, ao fechar o formulário, pode-se fechar também o documento ativo. Assim, todos os traços do programa são varridos da memória. Em casos como o deste Catálogo de Fontes, a coisa é um pouco mais complicada. Quando se produz um catálogo para impressão, é recomendável que o programa seja encerrado e o documento-catálogo se torne o documento ativo. Ao mesmo tempo, para limpar completamente o programa da memória, deve-se fechar o documento que lhe serve de suporte (no caso de um projeto residente num modelo diferente do Normal). Aqui você tem de tomar cuidado para fechar o documento certo. Um roteiro para isso é o seguinte. Assim que o programa entrar em ação, capture o nome do documento que o contém. Declare uma variável com validade em todo o formulário e armazene nela o nome desse documento. O melhor lugar para fazer isso é a rotina UserForm_Initialize: sNomeDocOriginal = ActiveDocument.Name
Se, por exemplo,você vai emitir um catálogo, outro documento entra em cena. Logo que ele for criado (Documents.Add), capture também o nome dele, que será o novo documento ativo: sNomeDocCatálogo = ActiveDocument.Name
No final da montagem do catálogo, feche o form com a instrução Unload. Por fim, no evento QueryUnload, acrescente as seguintes linhas: If sNomeDocCatálogo < > “” Then Documents(sNomeDocCatálogo).Activate Documents(sNomeDocOriginal).Activate End If Documents(sNomeDocOriginal).Close wdDoNotSaveChanges
Se não há um documento com o catálogo, fecha-se o documento-suporte (última linha acima). Se, ao contrário, houve emissão do catálogo, coloca-se o documento correspondente em primeiro plano. Em seguida, esse mesmo plano é ocupado pelo documento original, depois fechado. Isso garante que o catálogo fique visível para o usuário.
Para ir mais além 148
1. Experimente o preenchimento da caixa de listagem lstFontes sem usar o
recurso de ordenação alfabética. Observe como a abertura do formulário fica bem mais rápida. A maior parte do tempo consumido nessa operação deve-se à classificação alfabética. Se seu micro tem poucas fontes instaladas, você nem vai notar a diferença.
2. A rotina OrdemAlfa pode ser usada em qualquer outro programa para
organizar os elementos de uma matriz, em ordem alfabética. Ela deve receber a matriz, além dos índices do primeiro e do último elemento da matriz.
3. Você pode usar a rotina OrdemAlfa ou o método SortArray, do WordBasic, para ordenar, alfabeticamente, os nomes das fontes no catálogo para impressão. Em lugar de listar as fontes via coleção FontNames, crie uma matriz com elas, classifique a matriz, e passe para o documento os nomes da fonte a partir dos elementos da matriz. Essa solução está pronta na segunda versão do projeto, com apenas um formulário.
4. Algumas fontes não são exibidas corretamente na caixa txtAmostra. Entre elas estão Wingdings, Webdings, Marlett e Symbol – todas elas fontes figurativas. Curiosamente, se você predefinir a fonte para esses nomes, funciona. Todavia, se elas forem definidas com o programa já rodando, as imagens recusam-se a aparecer. Creio que temos aí um pequeno bug. Fiz um teste com caixas de texto do Visual Basic 6.0 e o funcionamento foi normal com essas fontes. O mesmo problema existe com o objeto Label (rótulo). Esse comportamento estranho pode ser constatado tanto no Office 97 como no Office 2000, em português ou no original,
Fontes demais Alguns programas gráficos, como o CorelDraw, vêm acompanhados de milhares de fontes. Não caia na tentação de instalar todas. Mesmo revistas e outras publicações profissionais não usam mais que um punhado de fontes. Instalar um número excessivo de conjuntos de letras não traz nenhum benefício concreto. Ao contrário, consome recursos do sistema. Se for necessário, use um gerenciador de fontes. Trata-se de um tipo de programa que mantém as fontes instaladas, mas inativas. Assim, elas não desperdiçam recursos do sistema. Aliás, o próprio Windows tem um limite de fontes, que fica em torno de 1000.
em inglês. É o tipo do bug que passa batido, versão após versão.
149
17 Um projeto cheio de estilo Organize catálogos de todos os estilos disponíveis no Word Ficha do projeto Projeto: Catálogo de Estilos O que faz: Apresenta, numa caixa de diálogo ou num documento, os nomes e características dos estilos disponíveis no Word. Arquivos e requisitos do projeto: Formulário frmEstilos e código interno. Conhecimento técnico: Manipulação das coleções de estilos do Word. Nível de programação: Intermediário
As coleções de objetos do Word – e de outros aplicativos do Office – constituem uma chave mestra para a programação de aplicativos nessa plataforma. Itens da interface do programa (como barras de ferramentas, botões e comandos de menu) formam coleções. Partes do documento (parágrafos, tabelas, fontes) também estão reunidas, cada tipo em sua própria coleção. Os conversores de arquivos disponíveis no ambiente são outra coleção. Então, não é preciso adivinhar: os estilos embutidos no sistema, ao lado dos estilos criados pelo usuário, também formam uma coleção. Neste projeto, nosso intuito é elaborar um aplicativo que permita ao usuário visualizar uma lista com a descrição de todos os estilos disponíveis e, se quiser, produzir um documento-catálogo com os nomes e descrições dos estilos. Esse documento pode ser armazenado ou impresso, conforme a necessidade. O aplicativo baseia-se num formulário (título: Lista de Estilos; nome: frmEstilos) bem simples. Inclui apenas uma caixa de listagem (lstEstilos) e três botões de comando: Aplicar (cmdAplicar), Catálogo (cmdCatálogo) e Cancelar (cmdCancelar).
F I G U R A 1 7 . 1 O formulário: estilos residentes e personalizados
No desenho do formulário não há nada de excepcional. Passemos, então, ao código. Mas, antes de tudo, é importante fazer duas observações. A primeira: no Word, há dois tipos de estilos – os personalizados, que são criados pelo usuário, e os residentes, que vêm de fábrica, no programa. A outra observação é que os estilos personalizados variam de documento para documento. Os estilos personalizados podem ser armazenados em diferentes modelos. Assim, quando você pede que o Word liste esses estilos do usuário, ele vai apresentar uma relação dos estilos existentes no documento ativo. Nossa aplicação, portanto, apresentará uma listagem dos estilos personalizados (se houver algum no documento ativo) e dos estilos residentes. 151
Para gerar a lista de estilos, a principal rotina do projeto é a sub UserForm_Initialize. Ela contém todos os ingredientes necessários para o que o formulário Lista de Estilos já se apresente, exibindo a lista completa dos estilos. A grande vantagem das coleções de objetos do Office está na facilidade de listá-los e, com isso, tomar uma série de iniciativas de programação. O centro da rotina de inicialização do formulário está em dois loops. O primeiro percorre o conjunto de estilos personalizados, ou melhor, não-residentes. O segundo, lista os estilos residentes. O arcabouço desses loops é o seguinte: For Each est In ActiveDocument.Styles If Not est.BuiltIn Then (...) For Each est In ActiveDocument.Styles If est.BuiltIn Then (...)
Fica claro que a propriedade BuiltIn (embutido) indica o estilo residente. O resto são caracterizações dos itens que definem um estilo: nome da fonte, tamanho, estilo (negrito, itálico), parágrafo (recuo à esquerda, recuo à direita, espaço entre linhas), alinhamento do parágrafo (à esquerda, à direita, centrado) etc. Cada um desses itens é acrescentado, com o método AddItem, à caixa de listagem lstEstilos. Acima deles, naturalmente, figura o nome do estilo (propriedade NameLocal). O botão Aplicar transfere o estilo indicado na caixa lstEstilos para uma seleção qualquer do documento ativo. Em outras palavras, você pode usar esta aplicação para definir estilos no documento em que está trabalhando. No entanto, a caixa de lista lstEstilos contém, além de estilos, títulos e elementos de estilo como Fonte, Recuo de parágrafo etc. Para identificar exatamente nessa caixa o que é um estilo, usamos o seguinte truque: Dim sEstilo As String sEstilo = lstEstilos.Value If Left$(sEstilo, 3) “--” And _ Left$(sEstilo, 3) Space(3) Then Selection.Style = sEstilo End If
152
O botão Catálogo chama a rotina CatálogoDeEstilos, cujo código é idêntico ao de UserForm_Initialize. A diferença básica está no lugar em que a lista de estilos é apresentada. Ao abrir o formulário, a lista é mostrada na caixa lstEstilos. Agora, a mesma lista vai para um novo documento do Word (e daí, se o leitor quiser, para a impressora). Portanto, em lugar do método AddItem, usamos agora TypeText para escrever linhas de informações num documento Word. Por exemplo:
With Selection .TypeText “ Fonte: ” & est.Font.Name & “ ” & _ est.Font.Size & vbCrLf .TypeText “ Negrito: ” & IIf(est.Font.Bold = True, _ “Sim”, “Não”) & vbCrLf .TypeText “ Itálico: ” & IIf(est.Font.Italic = True, _ “Sim”, “Não”) & vbCrLf (...)
Estas linhas produzem algo como: Fonte: Times New Roman 10 Negrito: Não Itálico: Sim Observe que estas linhas de código terminam sempre com a constante vbCrLf. Essa constante equivale à combinação das teclas Enter (Cr, ou carriage return) e Linefeed (alimentador de linha). Juntas, elas definem, no Windows, um novo parágrafo. Você poderia também usar, em lugar da constante, outro método do Word: Selection.TypeParagraph
O documento produzido pelo pressionamento do botão Catálogo é dividido em duas partes. A primeira, com os estilos personalizados, se houver. E a outra com os estilos residentes. O layout do catálogo é mostrado na Figura 17.2.
F I G U R A 1 7 . 2 Um catálogo, destacando a parte dos estilos
personalizados
153
Para ir mais além 1. Um detalhe interessante. Assim como os estilos, as barras de ferramentas também têm a propriedade NameLocal (em português). Só que as barras têm ainda a propriedade Name (em inglês), que não existe para os estilos.
2. As medidas nos documentos do Word são dadas em pontos. Este é o padrão.
Então, para mostrar dimensões em centímetros, você precisa fazer sempre a transformação de unidades. Neste projeto, há exemplos dessa transformação nas rotinas UserForm_Initialize e CatálogoDeEstilos. Um exemplo: PointsToCentimeters(est.ParagraphFormat.LeftIndent) & “ cm”
Existem também funções para executar outras transformações de medidas: PointsToInches (de pontos para polegadas); PointsToLines (de pontos para linhas); PointsToMilimeters (de pontos para milímetros); PointsToPicas (de pontos para paicas); PointsToPixels (de pontos para pixels). As operações inversas também estão disponíveis: MilimetersToPoints, LinesToPoints, CentimetersToPoints.
3. Neste projeto, estão cobertas apenas as formatações de estilo mais óbvias – aquelas que se referem a parágrafos e fontes. No entanto, as formatações de estilo envolvem ainda outros itens de que não se pode esquecer: tabulação, borda, idioma, moldura e numeração.
Yo no creo en fantasmas, pero... Muitas vezes quem desenvolve em ambiente Windows se vê assombrado por fantasmas digitais. Eles aparecem, por exemplo, quando um comando trivial, sobre o qual não há dúvida possível, começa a se comportar de forma esquisita. Eis um exemplo que aconteceu comigo. Durante a produção deste livro, eu precisava usar a função Now para obter o nome do mês corrente. Fiz o seguinte: Format$(Now, “mmmm”)
154
Pois bem, a expressão acima fornecia “janeiro”— só que o mês atual era outubro. Testei-a na janela Imediata, no VBA/Word, e a esquisitice continuava. Como alternativa, passei para a expressão equivalente, Format$(Date, “mmmm”), que fornecia uma resposta correta. Voltei para Now, e nada. A solução foi simples e desconcertante: salvar todos os documentos e reiniciar o computador. Aí tudo voltou a se comportar como esperado. Conclusão: você pode não acreditar em fantasmas, mas eles existem — inclusive os digitais. Para livrar-se deles, zerar tudo e recomeçar talvez seja a saída. Dê um boot no fantasma.
18 No paraíso das secretárias Um programa que demonstra recursos do VBA aplicados a tabelas do Word Ficha do projeto Projeto: VBA com tabelas O que faz: Demonstra recursos do VBA aplicados às tabelas do Word. Arquivos e requisitos do projeto: Modelo Tabelas.dot, que inclui documento e formulário VBA com código interno. Conhecimento técnico: Funções do VBA para criar, modificar, apagar e formatar tabelas. Nível de programação: Iniciante
As tabelas, a meu ver, formam um capítulo à parte no Word. Quando tive a oportunidade de experimentá-las, pela primeira vez, no Word 2.0, pensei comigo: é o paraíso das secretárias! Claro, as tabelas são um recurso fantástico para qualquer usuário. Pensei, imediatamente, nas secretárias porque sabia o quanto era penoso para elas datilografar (epa, aí está uma palavra do século passado!) documentos que incluíam tabelas. Sabia também que, mesmo nos processadores de texto para DOS, construir uma tabela requeria engenho, arte – e muita paciência. Este capítulo apresenta uma coleção de itens que demonstram algumas das possibilidades do VBA aplicado a tabelas do Word. São dez pequenos exercícios que envolvem as principais operações com tabelas. O projeto está todo contido no modelo Tabelas.dot, o qual contém um documento com duas tabelas e, internamente, um formulário VBA com código. O documento é simples. Exibe uma tabela de quatro linhas e quatro colunas, e outra de seis linhas e três colunas. Essas tabelas dão suporte aos exercícios em VBA que serão implementados no projeto. O desenho do formulário (frmTabelas) também não apresenta mistérios. Trata-se de um formulário com uma moldura abrigando dez botões de opção (opt1 a opt10), um para cada operação demonstrada. Além disso, há dois botões de comando: OK (cmdOK), que dispara a operação selecionada, e Cancelar (cmdCancelar), que fecha a aplicação.
F I G U R A 1 8 . 1 O formulário: dez exercícios com tabelas
O eixo da aplicação é o código associado ao evento clique do botão OK. Ali encontra-se uma instrução Select Case que distribui a ação para diferentes rotinas. Vamos percorrer as dez operações, comentando os detalhes principais. 156
F I G U R A 1 8 . 2 O documento embutido no modelo: duas tabelas
simples
1. Multiplicar uma célula de Tab 1 por uma célula de Tab 2 Nesse caso, a operação é feita na sub MultiplicarCélulas, mas estriba-se na função PegaValorCélula. MultiplicarCélulas pede os valores à função e, em seguida, calcula o produto: v1 = PegaValorCélula(1, 3, 2) v2 = PegaValorCélula(2, 1, 2) v = v1 * v2
Portanto, o esforço fundamental é realizado pela função PegaValorCélula. Esta tem o seguinte cabeçalho: Private Function PegaValorCélula(nTab As Integer, _ nLin As Integer, nCol As Integer) As Variant
Os três parâmetros que ela recebe são exatamente os itens que localizam a célula desejada. Primeiro, nTab, o índice da tabela. Depois, o número da linha (nLin) e o número da coluna (nCol). A rigor, a localização da célula demandaria mais uma informação: o nome ou o índice do documento. Sim, porque você pode ter vários documentos abertos, cada qual contendo uma ou mais tabelas. No entanto, para simplificar, trabalharemos aqui sempre com o documento ativo. Não há perigo de erro: como você vai notar, o próprio acesso ao formulário garante que o documento ativo seja o que nos interessa. No Word, cada tabela inserida num documento ganha um número seqüencial, a partir de 1. Essa numeração envolve uma pegadinha. Se um documento 157
contém três tabelas, seus índices são 1, 2 e 3. Se você eliminar a tabela 2 e voltar a fazer referência ao índice 3, provocará um erro. Por quê? Simplesmente porque o índice 3 deixou de existir. A ex-tabela 3 tem agora o índice 2. Portanto, fique esperto. Mas voltemos à função PegaValorCélula. Suas linhas ativas são as seguintes: Dim varValor As Variant varValor = _ ActiveDocument.Tables(nTab).Cell(nLin, nCol).Range.Text varValor = Left$(varValor, Len(varValor) - 2) PegaValorCélula = varValor
A segunda linha captura o valor da célula indicada. Na seguinte, são retirados dois caracteres do final desse valor. Isso é feito porque o conteúdo de cada célula contém dois caracteres, além dos visíveis: o caractere 13 (correspondente ao Enter, ou vbCr) e o 7, que não tem representação externa. Juntos, eles indicam o final de uma célula. Quando o Word está configurado para exibir todos os caracteres, inclusive os ocultos, esses caracteres são representados na tabela por um pequeno círculo com quatro raios, como você pode ver na Figura 18.2.
F I G U R A 1 8 . 3 Cada célula tem um caractere especial
Observe que a função PegaValorCélula retorna um valor do tipo variant. Como a intenção era usar valores mistos (texto ou números), decidimos trabalhar com esse tipo de variável. Nada impede, no entanto, que você faça a função fornecer um resultado do tipo string, que depois, se for o caso, deve ser convertido para um formato numérico adequado.
2. Capturar e exibir uma linha de tabela A operação 2 consiste em ler as informações de uma linha de tabela e exibi-las numa caixa de mensagem. Aqui, a rotina responsável pelo trabalho básico é a função GetLinha. Ela recebe o índice da tabela (nTab) e o número da linha. Sua missão é ler, um a um, os valores das células dessa linha. Como primeira ação, GetLinha verifica o número total de colunas, usando o método Count: 158
iTotColunas = ActiveDocument.Tables(nTab).Columns.Count
Em seguida, faz um loop do tipo For/Next, com o contador variando de 1 ao total de colunas, já calculado. Assim, ao ler cada célula, acumula seus valores numa string, separando-os com uma vírgula. O resultado final da função é essa string.
3. Capturar e exibir uma coluna de tabela Esta operação é bastante similar à anterior. Basta fazer as adaptações necessárias. Onde lá se lia coluna, aqui se deve ler linha, e vice-versa. A base é a função GetColuna, cuja estrutura é idêntica à de GetLinha. Há apenas uma diferença sutil. Como já vimos, o valor de cada célula é sempre expurgado de seus dois últimos caracteres, Chr(13) e Chr(7). Em GetColuna, somente o último caractere é retirado. Mantém-se o outro, porque ele determina uma quebra de linha. Como a intenção é mostrar uma coluna, o resultado, na caixa de mensagem, fica assim:
F I G U R A 1 8 . 4 Coluna da tabela
4. Selecionar a coluna 1 da tabela 2 A solução, neste caso, é direta. Basta usar o método Select: ActiveDocument.Tables(2).Columns(1).Select
159
5. Apagar a coluna 4 da tabela 1 Para apagar linhas ou colunas, usa-se o método Delete – aliás, o mesmo método utilizado em numerosas situações em que é preciso eliminar objetos: ActiveDocument.Tables(nTab).Columns(nCol).Delete
6. Formatar a coluna 1 da tabela 1 Para esta operação, entra em cena o procedimento FormatarColuna, que executa as seguintes configurações: n
define como verde a cor de fundo da coluna;
n
muda a fonte para negrito e itálico;
n
muda a cor da fonte para azul.
7. Apagar a tabela 2 inteira Mais uma vez, o método usado é Delete, e a aplicação é direta. Sendo nTab o índice da tabela, basta indicar: ActiveDocument.Tables(nTab).Delete
8. Trocar o valor de Tab2 (2, 2) para 22 Neste caso, basta selecionar a célula desejada – (2, 2) na tabela 2 – e inserir nela o novo valor: 22. A inserção é feita com o método TypeText. Assim: ActiveDocument.Tables(2).Cell(2, 2).Select Selection.TypeText “22"
Outra forma de obter o mesmo resultado seria usar a propriedade Text do objeto Range: ActiveDocument.Tables(2).Cell(2,2).Range.Text= “22"
Essa segunda alternativa é também mais econômica, porque dispensa a seleção prévia da célula. Como você vê, diferentes caminhos conduzem ao mesmo objetivo. Situações assim são comuns no VBA.
9. Classificar a tabela 2 pela coluna 1 160
Um dos recursos disponíveis para o trabalho com tabelas é a possibilidade de classificação segundo vários campos. O método responsável por isso é Sort.
Abaixo, você o vê, aplicado à tabela 2, num comando definido por parâmetros nomeados: ActiveDocument.Tables(2).Sort _ ExcludeHeader:=False, FieldNumber:=1, _ sortFieldType:=wdSortFieldAlphanumeric, _ SortOrder:=wdSortOrderAscending
O que significam esses parâmetros? n
ExcludeHeader (excluir cabeçalho) indica se a primeira linha da tabela deve ou não entrar no processo de classificação.
n
FieldNumber é número da coluna que servirá de base para a classificação.
n
SortFieldType indica o tipo de coluna a ser ordenada. O valor wdSortFieldAlphanumeric aplica-se, corretamente, a números ou texto.
n
SortOrder, a ordem de classificação deve ser crescente ou decrescente.
10. Adicionar uma tabela 3x7 ao documento Até aqui, já fizemos vários exercícios com a eliminação de partes de uma tabela ou de uma tabela inteira. Façamos, agora, o contrário: criar uma tabela. Para isso, vamos recorrer à função AdicionarTabela. Esta recebe os parâmetros nLins e nCols, inteiros, que indicam, respectivamente, o número de linhas e colunas da tabela a ser criada. O método usado para inserir uma tabela é Add, que pede como referência uma área de ação (range). Por isso, antes de recorrer a esse comando, é necessário definir essa área. Na função, ela correponde ao documento inteiro, o que é mostrado por: Dim Intervalo As Range Set Intervalo = ActiveDocument.Content
Agora, usamos o método Collapse, que recolhe um intervalo ou seleção para a posição inicial ou final. No caso, o que nos interessa é incluir a tabela no final do documento: Intervalo.Collapse Direction:=wdCollapseEnd
Podemos, então, inserir a tabela: ActiveDocument.Tables.Add Range:=Intervalo, _ NumRows:=nLins, NumColumns:=nCols Application.ScreenRefresh 161
Observe que o valor do parâmetro Range é o próprio intervalo, antes definido. A última linha acima força a atualização da tela do Word para que o usuário veja, imediatamente, a tabela recém-criada. Sem ela, a tabela será criada corretamente, mas talvez não seja exibida logo. Para o caso de dúvida, atualiza-se a tela. Há dezenas de outras possibilidades de trabalho com tabelas. Ao longo do tempo, e com o surgimento das necessidades, você vai descobri-las. Em caso de dúvida, recorra sempre ao help do VBA. Outra forma bastante produtiva de pesquisar é gravar uma macro (Ferramentas/Macro/Gravar Nova Macro) com as operações que você deseja realizar em código. O próprio Word ensina o que fazer.
Para ir mais além 1. Um bom exercício é descobrir, por exemplo, como selecionar duas ou
mais linhas ou colunas contíguas numa tabela. Uma pista: selecione a linha (ou coluna) e depois aplique o método MoveDown ou MoveRight para estender a seleção às linhas ou colunas vizinhas.
2. Dê uma boa olhada nos comandos de formatação de tabelas. Por exemplo, como definir as cores das bordas.
3. Dica: durante a gravação de macros, não é possível selecionar linhas ou colunas de uma tabela com o mouse. Faça essas operações com o teclado.
162
Para colorir as tabelas Você está elaborando uma tabela e quer destacá-la, adicionando uma cor de fundo. No modo interativo, é simples. Basta selecionar a área desejada, acionar Formatar/Bordas e Sombreamento e escolher a cor na orelha Sombreamento. E no VBA, como se faz isso? Primeiro é preciso selecionar a parte da tabela que deve ser pintada. A sintaxe você já viu neste capítulo. Por exemplo, para selecionar a linha 3 da tabela 2, no documento 1: Documents(1).Tables(2).Rows(3).Select
Para atribuir a cor de fundo, recorre-se à propriedade BackgroundPatternColor do objeto Selection.Shading. Se você escrever Selection.Shading.BackgroundPatternColor =
o VBA abrirá a ajuda pop-up com dezenas de cores à disposição. Há inclusive os meios-tons de cinza, cujas constantes são fáceis de memorizar: wdColorGray05,wdColorGray10 etc. Para ser melhor, falta apenas que, ao lado da constante, o VBA exiba uma amostra visual da cor.
F I G U R A 1 8 . 5 Constantes de cores
Não confunda a propriedade BackgroundPatternColor com BackgroundPatternColorIndex. A primeira, que usamos acima, corresponde ao padrão RGB: 16,7 milhões de cores. A outra, o Index, oferece menos de duas dezenas de tonalidades. Há apenas uma notícia ruim: BackgroundPatternColor só está disponível no Word 2000. Se você usa o 97, terá de se contentar com as poucas cores de BackgroundPatternIndex. 163
19 O Word também toca música! Uma aplicação para executar arquivos MP3, WAV e MID Ficha do projeto Projeto: Som no Word 1 O que faz: Organiza uma lista de arquivos de som nos formatos MP3, WAV e MID e permite executá-los, individualmente, usando o acessório Media Player, do Windows. Arquivos e requisitos do projeto: Formulário frmSom e código interno. Conhecimento técnico: Manipulação básica de um objeto do VBA (o objeto FileSearch, que localiza arquivos no disco), e da função Shell, tradicional do Basic, que executa outros programas externos ao projeto. Nível de programação: Iniciante
Neste projeto, Som no Word 1, vamos apresentar a primeira parte de uma série de três que envolvem a manipulação de recursos sonoros. Nossa intenção, aqui, é bem simples: criar uma caixa de diálogo do Word que organize uma lista de arquivos de som e permita que esses arquivos sejam executados. Para tal propósito, a caixa de diálogo – em nosso caso, o formulário frmSom – deverá conter apenas três controles fundamentais: uma caixa de lista (lstArquivos), um botão para tocar as músicas (cmdExecutar) e um botão de saída (cmdCancelar).
F I G U R A 1 9 . 1 Som no Word: para tocar arquivos de música
Não há, portanto, nenhum segredo no desenho do formulário. A imagem do alto-falante que você vê na Figura 19.1 tem apenas função decorativa. Passemos, então, ao código. Em primeiro lugar, é preciso organizar a lista dos arquivos. Quando o usuário abrir o formulário, ela já deve estar pronta. Por isso, o código que cria a lista e a coloca na caixa lstArquivos faz parte da rotina UserForm_Initialize, que é a primeira a ser executada em qualquer formulário. Essa rotina começa por definir um diretório básico, strNomeDir, no qual o programa irá procurar os arquivos de som. Esse diretório é c:\windows\media. A variável strNomeDir é declarada como geral para todo o formulário. Fazemos isso porque vamos precisar do valor dela em mais de uma rotina. Para localizar os arquivos, usamos os serviços do objeto FileSearch, aqui representado com o nome de Busca. Na verdade, para usar os termos técnicos, FileSearch é uma classe de objetos e Busca, uma instância dessa classe. Essa instância se define da seguinte maneira: Dim Busca As FileSearch Set Busca = Application.FileSearch
A partir daí, passa-se a trabalhar somente com o objeto Busca, que tem a vantagem do nome mais curto e mais amigável. Naturalmente, você não precisaria usar esse nome: poderia tratar diretamente com Application.FileSearch. Muito bem: agora, precisamos definir algumas propriedades do objeto Busca. 165
With Busca .NewSearch .LookIn = strNomeDir .SearchSubFolders = False .FileName = “*.wav;*.mid;*.mp3" .Execute (...) End With
Veja o que quer dizer cada uma dessas propriedades:
LookIn
Recebe o nome do diretório em que os arquivos devem ser procurados.
FileName
Armazena as extensões dos tipos de arquivos que devem ser localizados, no caso “*.wav;*.mid;*.mp3”.
SearchSubFolders
True ou False. Define se o objeto de busca deve procurar arquivos em subpastas do diretório indicado.
O método NewSearch avisa ao objeto Busca que deve fazer uma nova pesquisa com as condições (propriedades) indicadas. Já o método Execute, como o nome indica, dá o sinal de largada para que a busca seja levada a efeito. No final da rotina vem um If. Se a pesquisa encontrou algum arquivo (Busca.FoundFiles.Count > 0), então organiza-se a lista. Caso contrário, o programa emite um aviso ao usuário. Obviamente, o que nos interessa é a situação em que a contagem de arquivos é maior que zero. Aí, como todos os arquivos de som pertencem ao mesmo diretório, vamos listá-los sem o caminho de localização, somente com os nomes. Para isso, como já sabemos o comprimento do nome do diretório mais a barra invertida(lenStrNomeDir), ficamos apenas com o nome “líquido” do arquivo, que é adicionado como um item da caixa de listagem: For i = 1 To .FoundFiles.Count lstArquivos.AddItem Right$(.FoundFiles(i), _ Len(.FoundFiles(i)) - lenStrNomeDir) Next i
166
Pronto. A lista de arquivos – WAV, MP3 ou MID – está na caixa lstArquivos. Agora, falta garantir que o usuário escolha um arquivo e possa tocá-lo, clicando no botão Executar. Isso é resolvido na rotina cmdExecutar_Click, que determina o que o programa deve fazer quando o usuário clicar nesse botão. Agora chegamos à função Shell. Essa velha conhecida dos programadores Basic serve para chamar um executável externo. Aqui, ela é usada da seguinte forma:
Private Sub cmdExecutar_Click() Dim strArq As String ‘ caminho completo do arquivo: dir + arquivo strArq = strNomeDir & “\” & lstArquivos.Value ‘ A linha abaixo trabalha com o Media Player ‘ Vale para arquivos WAV, MID, MP3 (som) e AVI (vídeo); Shell “c:\windows\mplayer.exe ” & _ “/play /close ” & strArq, vbHide End Sub
Para que o programa possa encontrar o arquivo corretamente, é necessário apresentá-lo com o caminho completo. Então, faz-se agora a operação inversa daquela executada na rotina UserForm_Initialize: junta-se caminho e nome de arquivo na variável strArq. Em seguida, passa-se esse nome de arquivo à função Shell, que obedece ao seguinte esquema: Shell
“executável parâmetros arquivo”, estilo_da_janela
Em nosso caso, o executável é mplayer.exe, localizado no diretório c:\windows. Os parâmetros – específicos para o programa Media Player – são /play /close. Por fim, vem o nome do arquivo. Os parâmetros indicam que o programa deve tocar o arquivo (play) e fechar-se (close) após a execução. O estilo da janela, que é opcional, define se o executável deverá aparecer numa janela maximizada, minimizada, normal ou oculta. No exemplo, escolhemos a opção oculta (vbHide). O arquivo de som será tocado de forma independente pelo Media Player. Se você não usar a opção vbHide (e usar, em vez dela, vbNormalFocus, por exemplo), o Media Player vai abrir sua janela, normalmente, e mostrar na barra de título o arquivo em execução:
F I G U R A 1 9 . 2 Media Player com opção de janela normal
Projeto concluído. Uma observação: para garantir o estilo Windows, chame a rotina cmdExecutar_Click a partir da rotina do evento lstArquivos_DblClick. Isso significa que, se o usuário der um duplo clique numa linha da caixa de lista, o arquivo correspondente será tocado. 167
Para ir mais além 1. Como todos os arquivos de som envolvidos neste projeto localizam-se
num mesmo diretório, trabalhamos apenas com o “nome líquido”do arquivo. No entanto, o objeto FileSearch tem condições de localizar arquivos em qualquer diretório. Se você trabalhar com o nome completo (caminho + nome) do arquivo, pode listar todos os arquivos WAV, MID e MP3 do disco rígido. Para isso, defina as seguintes propriedades de FileSearch: .LookIn = “c:” .SearchSubFolders = True
2. Experimente com os parâmetros do Media Player. Use, por exemplo, somente o parâmetro /play e observe o que acontece.
3. O Media Player (mplayer.exe, localizado no diretório do Windows) só toca um arquivo de cada vez. Não há uma forma simples de fazê-lo tocar uma seqüência de arquivos musicais. Veja no próximo capítulo uma solução para isso.
VBA em vez do Explorer Se você precisa criar grande número de diretórios com numeração seqüencial (por exemplo, “Capítulo 01”, “Capítulo 02” etc.), use o seguinte programa VBA: Sub CriarDiretórios() ‘ Cria diretórios numerados Dim i As Integer Dim strCap As String Dim strDir As String strDir = “c:\meus documentos\Livro Final\” For i = 1 To 30 MkDir strDir & “Capítulo ” & Format (i, “0#") Next i MsgBox “Trabalho concluído.” End Sub
Esta dica resultou de minha experiência prática na produção deste livro. Em situações assim, é muito mais rápido usar o VBA que criar, manualmente, trinta pastas de arquivos com o Windows Explorer.
168
20 Os objetos comandam o ritmo Faça a programação com objetos soar como música aos seus ouvidos Ficha do projeto Projeto: Som no Word 2 O que faz: O programa gera um arquivo-texto M3U, padrão MP3, com a lista de todos os arquivos MP3, MID e WAV. Depois, usa um programa qualquer que suporte esse padrão para executar a seqüência de músicas. Aqui, essas características são apresentadas em duas visões: segundo a programação tradicional e com base na programação orientada a objeto. Arquivos e requisitos do projeto: Módulo modSomWord2 (programação clássica) Formulário frmSoundPlay e módulo de classe SoundPlayer (programação orientada a objeto)
Conhecimento técnico: Manipulação básica de um objeto do VBA (o objeto FileSearch, que localiza arquivos no disco) e da função Shell, tradicional do Basic, que executa outros programas externos ao projeto. Criação de uma classe de objetos, com definição de seus métodos e propriedades. Nível de programação: Intermediário
Você certamente terminou o capítulo anterior com uma pequena frustração: o projeto só permite tocar um arquivo de som de cada vez. Mas – por que não? – sua intenção é executar uma seqüência de músicas, bem no estilo dos MP3 players, como o WinAmp, o RealJukebox e o MusicMatch Jukebox e – surpresa! – o Windows Media Player. Isso também pode ser feito a partir do Word ou de qualquer outro aplicativo que trabalhe com o Visual Basic. Veja como.
A solução tradicional Para centrar os esforços nas questões fundamentais, desta vez não vamos criar um formulário. Tudo se resolverá apenas com uma sub-rotina chamada ListaArquivosDeSom. No início, o código é idêntico ao da rotina UserForm_Initialize, discutido no capítulo anterior. Basicamente, usa-se o objeto FileSearch para localizar, num dado diretório – aqui, strDirSom –, arquivos MP3, WAV e MID. A partir daí, as tarefas são as seguintes. Vamos criar, no diretório strDirSom, um arquivo de texto cujo conteúdo é a lista dos arquivos MP3, WAV e MID encontrados. Esse arquivo deve ter a extensão M3U, usada no padrão MP3 para armazenar listas de músicas (playlists). A estrutura de um documento M3U é muito simples e pode ser feita até à mão. Basta abrir o Bloco de Notas e escrever, em cada linha, o nome de um arquivo musical. O programa MP3 player toca as músicas na ordem indicada na lista. Observe que, para tornar as coisas mais simples, estamos admitindo que todas as músicas estão no mesmo diretório. Assim, ao salvar o arquivo M3U também nesse diretório, cada linha dele não precisa ter mais que o “nome líquido” do arquivo. Se a lista envolver arquivos de diferentes diretórios, então será necessário incluir o caminho completo do arquivo. Vejamos o principal trecho da rotina:
170
iArq = FreeFile Open strDirSom & “\” & “Listasom.m3u” For Output As #iArq For i = 1 To .FoundFiles.Count ‘ Isola nome do arquivo, sem diretório
strNomeArq = Right$(.FoundFiles(i), _ Len(.FoundFiles(i)) - (intLenArq + 1)) ‘ Letras minúsculas strNomeArq = LCase$(strNomeArq) Print #iArq, strNomeArq Next i Close #iArq Shell strPlayer & “ ” & strDirSom & “\” & _ “listasom.m3u”, vbMinimizedNoFocus
Primeiro, abre-se um arquivo chamado Listasom.m3u, no qual serão escritos os nomes dos arquivos de áudio. Em seguida, com um loop For/Next, percorre-se todos os nomes de arquivos encontrados. Um a um, eles vão sendo escritos no arquivo, com a linha Print #iArq, strNomeArq
Concluída a listagem, fecha-se o arquivo. Agora, com a função Shell, chama-se o player, e passa-se a lista para ele. Imediatamente, o programa começa a tocar a primeira música da lista. Você pode usar qualquer programa que suporte os três tipos de arquivos musicais usados neste projeto. Observe que aqui, diferentemente do que fizemos no capítulo anterior, usamos Windows Media Player 2 (mplayer2.exe). Trata-se de um programa mais novo que o Media Player tradicional do Windows. A primeira versão, que ainda vem em todas as instalações do Windows 95/98, toca arquivos MP3, mas não sabe ler a lista de músicas num arquivo M3U.
F I G U R A 2 0 . 1 Windows Media Player 2: executando um MP3
No exemplo, utilizamos o Media Player 2, tendo em vista que quase todo usuário de Windows deve tê-lo instalado. Ele tem pelo menos uma desvantagem: toca toda a seqüência de músicas mas não exibe a lista, o que qualquer MP3 player faz. Além disso, os arquivos MP3 têm um espaço para o armazenamento do nome da música, artista, título do disco, ano etc. O Media Player 2 não exibe essas informações. Se você não tem nenhum desses programas, todos eles são freeware ou shareware e podem ser obtidos na Internet. Eis os endereços: 171
PROGRAMA
FABRICANTE
ENDEREÇO WEB
Windows Media Player 2
Microsoft
www.windowsmedia.com
RealJukebox
RealNetworks
www.real.com
MusicMatch Jukebox
MusicMatch
www.musicmatch.com
WinAmp
NullSoft
www.winamp.com
Midisoft Internet Media Player
Midisoft
www.midisoft.com
Ao experimentar com a rotina ListaArquivos, não se esqueça de personalizá-la, indicando: a) um diretório em sua máquina que contenha arquivos de áudio. Isso corresponde à definição da variável strDirSom: strDirSom = “c:\mp3"
b) o endereço completo de um programa capaz de tocar esses arquivos orientado por uma lista M3U. Nesse caso, ajuste o valor de strPlayer: strPlayer = _ “c:\arquivos de programas\windows media player\mplayer2.exe”
172
F I G U R A 2 0 . 2 WinAmp: MP3 player com lista de arquivos
Para executar a rotina ListaArquivos, abra, em seu Word, o ambiente de desenvolvimento do Visual Basic e, na janela Imediata, digite: ListaArquivosDeSom
O resultado soará aos seus ouvidos.
Com orientação a objeto Este bloco é última parte de uma seqüência que mostra como executar arquivos de som WAV, MID e MP3 a partir do Word. Nesta parte nosso objetivo é bem claro: vamos repetir o que foi feito antes, só que, desta vez, empregando recursos de orientação a objeto. Quando se fala em objeto, logo nos vêm à mente coisas com que lidamos no dia-a-dia: carro, caneta, computador, relógio. De fato, todos esses são exemplos de objetos. Mas, em programação, é necessário fazer uma caracterização um pouco mais precisa. Observe que esses exemplos correspondem, na verdade, a categorias genéricas de objetos. Caneta é um objeto qualquer que desempenha as funções de caneta. Idem para carro, computador, relógio. Mas a minha caneta, esta que uso já faz alguns meses, é um caso particular da categoria genérica caneta. Primeiro, ela tem um dono, que sou eu. Depois, tem marca, modelo, cor externa, é de metal ou de plástico, escreve fino ou grosso, em preto, azul, vermelho, pode ser tinteiro ou esferográfica, pode ter aspecto de nova ou estar bastante arranhada... Enfim, um objeto concreto como esta caneta que tenho agora na mão reúne uma série de características próprias – ou seja, propriedades. Nos dois parágrafos acima, mostramos num exemplo sucinto as idéias de classe e objeto. A classe é uma representação abstrata. O objeto pertence à classe, mas passa a ser uma entidade concreta. Dito de outra forma, a classe é uma descrição ou a idéia genérica do objeto. Um exemplo simples. Ao projetar as casas para um conjunto habitacional do tipo Cohab, o arquiteto desenha uma unidade padrão. Com base nesse desenho, serão construídas centenas, milhares de casas idênticas. Todas elas podem ter as mesmas características. Mas pelo menos uma propriedade permitirá o reconhecimento individual de cada uma delas: o endereço. Para facilitar as coisas, normalmente não se faz essa distinção entre classe e objeto: ambos são considerados objetos. Mas em programação é importante saber que um objeto é uma instância de uma classe. Se, no exemplo do arquiteto, ele estivesse trabalhando com programação, poderíamos dizer que cada casa popular é uma instância do projeto. Ao lado de propriedades, como cor e tamanho, os objetos também executam ou sofrem ações. A casa, por exemplo, pode sofrer a operação de pintura. O
173
carro pode executar ações como mover-se, parar, dobrar à direita. Em programação, essas ações se chamam métodos. Embora vistos sem nenhuma profundidade teórica, esses conceitos mínimos – classe, objeto, propriedades e métodos – serão a base sobre a qual vamos desenvolver nosso exercício de orientação a objeto. No Visual Basic – e praticamente em todas as linguagens modernas –, as propriedades e os métodos de um objeto são apresentados com a seguinte sintaxe: Objeto.Propriedade Objeto.Método
Intuitivamente você já sabe disso. Considere, por exemplo, uma caixa de texto txtNome e uma caixa de imagem imgFoto. São objetos de classe diferentes, mas compartilham algumas propriedades como as que definem tamanho e posição: txtNome.Left = 75 txtNome.Top = 100 imgFoto.Width = 300 imgFoto.Height = 210 imgFoto.BackColor = vbYellow
Agora, vejamos alguns exemplos de ação (métodos) aplicados a esses dois objetos. Um método óbvio é o que transfere o objeto de lugar: Move. txtNome.Move 100, 150
Os dois números indicam as novas coordenadas Left e Top para as quais a caixa de texto deve se deslocar. Normalmente, a propriedade é representada por um substantivo ou um adjetivo (Height, altura; BackColor, cor de fundo; Enabled, ativo), enquanto o método envolve um verbo: Move, Delete, Close (mover, apagar, fechar). Algumas ações – assim como os verbos – não precisam de complemento. Exemplo: txtNome.SetFocus
O método SetFocus não pede nenhum complemento. Aplicado à caixa de texto txtNome, ele transfere o foco para esse objeto. No entanto, o método Move exige, por natureza, a informação de parâmetros. Mover para onde? Nesse caso, é preciso informar, no mínimo, as novas coordenadas do objeto. Mas o ambiente de trabalho do VBA facilita bastante as coisas. Assim que você escreve txtNome.Move e dá um espaço, uma etiqueta pop-up surge na tela,indicando os parâmetros que o método exige: 174
F I G U R A 2 0 . 3 Etiqueta com ajuda sobre a sintaxe do método
SoundPlayer: criação de uma classe de objetos Muito bem. Feita esta breve introdução ao mundo dos objetos, passemos à prática. Nosso objetivo, agora dito em termos de orientação a objetos, é criar uma classe que nos permita tocar arquivos de áudio a partir do Word (ou de outro aplicativo que dê suporte ao VBA). Reanalisemos o problema. Quais são os elementos envolvidos? n
O diretório com arquivos de áudio
n
O programa que será usado para tocar esses arquivos (player)
n
O nome do arquivo M3U com a lista de músicas
Além disso, quais ações devem ser executadas? n
Criar a lista de arquivos MP3, WAV e MID
n
Passar essa lista ao programa (player)
Tudo indica que os objetos de nossa classe terão três propriedades e dois métodos. Vamos em frente. Criaremos uma classe chamada SoundPlayer. Nela, implementaremos as propriedades FileDir, Player e FileName, e os métodos CreateList e Play. Se você não gostar dos nomes, use outros, como Pasta, Tocador, NomeDoArquivo, CriarLista e Tocar – ou outros que lhe pareçam mais convenientes. Preferimos nomes em inglês porque isso permite usar propriedades como FileName, que já são comuns para outros objetos. No ambiente de trabalho do Visual Basic, acione o comando Inserir/Módulo de Classe. O VBA cria um módulo chamado Classe1 (ou Classe2, se já existir outro com o nome Classe1) dentro da pasta Módulos de Classe. Na janela Propriedades, mude o nome Classe1 para SoundPlayer. Agora, entremos no código da classe. Na forma como a orientação a objeto foi implementada no Visual Basic, cada propriedade ou cada método corresponde a pelo menos uma rotina no interior da classe. Comecemos pelas propriedades. Cada uma delas exige a declaração de uma variável, válida apenas no âmbito do módulo de classe. Para o módulo SoundPlayer, temos três variáveis: Dim m_strPlayer As String Dim m_strFileName As String Dim m_strFileDir As String
‘ propriedade Player ‘ propriedade FileName ‘ propriedade FileDir
175
Observe que o usuário terá condições de definir o valor dessas propriedades e também consultar o valor ativo delas. Portanto, é necessário que a classe possua mecanismos para definir e para fornecer esses valores. A definição do valor de uma propriedade é feita com uma rotina Property Let, seguida do nome da propriedade. Exemplo: Property Let FileName(strFileName As String) ‘ Define o nome do arquivo m3u m_strFileName = strFileName End Property
Esta rotina recebe o nome do arquivo (strFileName) e armazena-o na variável local m_strFileName. O valor m_strFileName passa a corresponder à propriedade FileName de qualquer objeto pertencente à classe. Recapitulando: para definir uma propriedade, são necessários dois passos. O primeiro é declarar, no módulo de classe, uma variável interna a esse módulo. O outro consiste em criar uma rotina Property Let <nome da propriedade>, na qual aquela variável interna assume um valor externo, enviado por um programa que esteja usando os serviços da classe. Naturalmente, esse programa também pode querer consultar o valor atual da propriedade. Para permitir isso, é preciso criar, na classe, uma rotina Property Get <nome da propriedade>. A rotina Property Get é, por assim dizer, o inverso de Property Let: Property Get FileName() As String ‘ Fornece o nome do arquivo m3u FileName = m_strFileName End Property
176
Agora, a propriedade FileName, que se expõe para fora do módulo de classe, assume o valor da variável interna mstrFileName. Assim, se o programa perguntar, a classe responderá com o valor da propriedade. Nem sempre é possível ou necessário haver uma rotina Let e outra Get. Afinal, existem propriedades que só podem ser lidas – não admitem atribuição de valor. Você não pode, por exemplo, modificar o valor do sistema operacional ou do processador da máquina. Se essas variáveis fizessem parte de uma classe, teriam apenas a rotina Property Get. Muito bem, você já viu como criar as rotinas referentes a cada propriedade. Avancemos, agora, para as rotinas associadas aos dois métodos de nosso projeto: CreateList e Play. Como esses métodos podem ser chamados a partir de procedimentos fora do módulo de classe, então as rotinas correspondentes devem começar com a palavra-chave Public. Conseqüentemente, procedimentos auxiliares, internos à classe, devem ser marcados com a palavra-chave Private. Um exemplo de rotina auxiliar interna à classe é a sub TrataErros. Chamada por
outros procedimentos internos, ela cuida apenas de exibir uma mensagem quando ocorre um erro. Analisemos a rotina CreateList. Sua tarefa essencial é criar o arquivo M3U que contém a lista de arquivos de som. Essa rotina, na verdade, é uma variação do que já vimos neste capítulo, no formato de programação tradicional. Chamo a atenção apenas para uma novidade: em mais de um ponto do código aparece a palavra-chave Me. Ela é uma forma de referência implícita à própria classe. Quando se usa Me.FileDir, refere-se ao valor da propriedade FileDir. Lembre-se de que Me também é usada nos formulários. A instrução Unload Me, por exemplo, retira da memória o formulário dentro do qual ela é usada. Na classe SoundPlayer, em lugar de Me.FileDir, também seria correto utilizar a variável interna m_strFileDir. Passemos ao outro método, Play. Ele é mais simples que CreateList. Sua tarefa é pegar o arquivo M3U e apresentá-lo ao programa (player). Naturalmente, para que isso funcione, é necessário que, antes, as três propriedades já tenham sido definidas: o nome do player, o nome do arquivo M3U e o diretório onde estão os arquivos musicais. With Me If .Player = “” Or .FileDir = “” Or .FileName = “” Then s = “Antes de usar o método Play, é preciso ” s = s & “definir as propriedades FileDir e ” s = s & “Player do objeto SoundPlay.” MsgBox s, vbCritical, “Objeto SoundPlay” Exit Sub End If End With Shell Me.Player & “ ” & Me.FileDir & “\” & Me.FileName, _ vbMinimizedNoFocus
Em outras palavras, o método Play só pode ser chamado depois da definição das propriedades e da criação do arquivo M3U (método CreateList). No entanto, como o programador pode esquecer este detalhe, antes de tentar enviar informações ao player, testamos se os valores das propriedades Player, FileDir e FileName já foram definidos. É o que faz a instrução If, no trecho de código acima. Para encurtar caminho e reduzir a possibilidade de erro, usamos um pequeno truque em relação à propriedade FileName. Já vimos que ela deve ser definida de fora para dentro, o que é feito mediante a rotina Let FileName. Contudo, o nome do arquivo M3U não precisa variar. Então, adicionamos a seguinte linha de código à rotina Class_Initialize da classe SoundPlayer: Me.FileName = “listasom.m3u”
177
Assim como a rotina UserForm_Initialize, dos formulários, Class_Initialize deve conter todas as definições iniciais da classe. Em nosso exemplo, a propriedade FileName é definida, como padrão, para o valor “listasom.m3u”. Portanto, se não for indicado um nome para o arquivo, esse nome já estará definido.
Como usar a classe SoundPlayer A construção da classe SoundPlayer está terminada. Mas uma classe não é como uma função ou uma sub-rotina que você, simplesmente, chama quando necessário. Há uma forma específica de trabalhar com ela. Para demonstrar essa forma, vamos criar um formulário chamado frmSoundPlay (Figura 20.4).
F I G U R A 2 0 . 4 O formulário frmSoundPlay: uso da classe
SoundPlayer
Como controles fundamentais, esse formulário tem duas caixas de texto e dois botões, associados dois a dois. A primeira caixa de texto (txtDir) associa-se ao botão cmdProcurarDir. A outra (txtExe) trabalha em conjunto com o botão cmdProcurarExe. Você já percebeu: o usuário pode digitar nas caixas de texto o diretório dos arquivos de som e o caminho do programa player. Mas, se quiser, pode também clicar no botão de comando para localizar o diretório e o arquivo com uma caixa de diálogo do Word (janelas Cópia e Abrir). Restam os botões Cancelar, que não requer explicação, e OK, que dispara a execução dos arquivos musicais. Há ainda o botão Volume. Trata-se de um objeto acessório que abre o controle de Volume (Sndvol32.exe) do Windows. A parte externa do formulário está vista. Passemos para dentro. Vejamos as rotinas ligadas aos botões cmdProcurarDir e comdProcurarExe. O botão cmdProcurarDir abre a caixa de diálogo Cópia, que localiza diretórios:
178
Private Sub cmdProcurarDir_Click() ‘ Captura o diretório dos arquivos de som
Dim strPasta As String If Dialogs(wdDialogCopyFile).Display = -1 Then strPasta = Dialogs(wdDialogCopyFile).Directory If strPasta “” Then If Right$(strPasta, 1) = “\” Then strPasta = Left$(strPasta, Len(strPasta) - 1) End If txtDir = strPasta End If End If End Sub
Nesse caso, para abrir a caixa usa-se o método Display, e não o método Show. A diferença é que exibe a caixa de diálogo e não executa nada. Serve apenas como um objeto no qual o usuário escolhe um diretório. O valor fornecido pela caixa de diálogo Cópia é transferido para a caixa txtDir. As linhas adicionais no procedimento devem-se a um teste preventivo. Esse teste verifica se o nome do diretório termina em barra invertida e elimina-a. Evita-se, assim, que apareçam duas barras na hora de combinar diretório e nome do arquivo. O botão cmdProcurarExe exibe a caixa de diálogo Abrir, que lista arquivos. Set dlg = Dialogs(wdDialogFileOpen) dlg.Name = “*.exe” If dlg.Display = -1 Then txtExe = Options.DefaultFilePath(wdDocumentsPath) _ & “\” & dlg.Name End If
O código acima faz com que a caixa Abrir mostre apenas os arquivos do tipo EXE, já que o arquivo procurado é um executável. OK. O arquivo executável indicado pelo usuário é transferido para a caixa txtExe. Mas as coisas acontecem, mesmo, quando o usuário clica no botão OK. Vamos transcrever a rotina inteira: Private Sub cmdOK_Click() If Len(txtDir) 0 And Len(txtExe) 0 Then Dim sp As SoundPlayer Set sp = New SoundPlayer sp.FileDir = txtDir sp.Player = txtExe sp.CreateList
179
sp.Play Set sp = Nothing Unload Me Else MsgBox “Preencha os dois campos.”, vbCritical, _ “Objeto SoundPlayer” End If End Sub
Primeiro, a rotina verifica se as duas caixas de texto foram preenchidas. Em caso negativo, emite um aviso para o usuário e interrompe o programa. Em caso positivo, segue em frente. Aqui começamos a lidar com nossa classe SoundPlayer. Primeiro, é preciso declarar uma variável qualquer como sendo do tipo SoundPlayer. Ou seja, como a classe existe no ambiente do Word, ela passa a ser um tipo de variável-objeto. Dim sp As SoundPlayer
Depois, indica-se que a variável sp é uma instância da classe SoundPlayer. Assim: Set sp = New SoundPlayer
A partir de agora, você pode efetuar todas as operações suportadas pelo objeto sp, pertencente à classe SoundPlayer. Que operações são essas? Há apenas cinco, como você já sabe: três referem-se à definição de propriedades, e duas são métodos de ação. No caso das três propriedades há, de fato, seis operações: três definem valores e as outras três lêem esses valores. Como vimos no início deste capítulo, as referências a métodos e propriedades são sempre feitas com o nome do objeto e o nome do método ou propriedade unidos por um ponto. Então, para tocar a seqüência de músicas, basta escrever quatro linhas: sp.FileDir = txtDir sp.Player = txtExe sp.CreateList sp.Play
As duas primeiras definem propriedades. As duas últimas executam métodos. Lembre-se: usamos apenas duas das três propriedades. A terceira, FileName, foi deixada com o nome padrão: “listasom.m3u”. Preste atenção na linha a seguir: 180
Set sp = Nothing
Concluídas as tarefas, não há mais necessidade de lidar com o objeto sp, instância de SoundPlayer. Então, use a fórmula Set objeto = Nothing para desvincular a variável (no caso, sp) da classe (SoundPlayer). É importante fazer isso para liberar memória. Neste exemplo, chamamos o objeto de sp. Alguns autores recomendam que as variáveis de objeto sejam nomeadas com prefixos que facilitem a identificação. Em geral, aconselha-se que comecem com um “o” minúsculo ou com o prefixo “obj”. Por essa regra, você poderia chamar sp de objSP ou oSP, ou oSoundPlayer. Um detalhe interessante. Quando você digita, no código, uma chamada a funções ou sub-rotinas escritas por você mesmo, o ambiente do VBA apresenta a etiqueta de ajuda com os parâmetros esperados pela rotina. O mesmo ocorre com as classes. Depois que você cria nova instância de um objeto, o ambiente exibe todos os métodos e propriedades associados a ele:
F I G U R A 2 0 . 5 O VBA assume a classe que você cria
Outro detalhe: no formulário, para economizar digitação, defini o valor inicial das caixas de texto para o diretório e o player desejados. Isso está na rotina UserForm_Initialize. Assim, você só vai precisar digitar quando o diretório ou o player for diferente. Na maioria dos casos, basta clicar no botão OK. Agora, você deve estar pensando: mas qual a vantagem dessa história complicada de classes e objetos? As vantagens são muitas, mas fiquemos com as mais evidentes:
1. A facilidade para o programador que vai usar os serviços oferecidos pela
classe. Observe que, em nosso exemplo, todas as operações se resumiram em declarar o objeto e chamar métodos ou propriedades. Tudo em meia dúzia de linhas. Talvez o exemplo mostrado aqui não seja o mais adequado para dar idéia dessa facilidade. Afinal, construímos o projeto
181
com base num código que você já conhecia bem. Mas no interior de uma classe podem ocorrer processamentos supercomplexos que, no final, se transformam em métodos e propriedades. Pense de outro modo: você não precisa saber o que ocorre lá dentro de SoundPlayer. O certo é que, com ela, você toca música com algumas linhas de um código muitíssimo simples.
2. Reutilização de código. Você pode reutilizar funções e outras rotinas.
Basta copiá-las de um projeto e colá-las em outro. A utilização de objetos facilita esse processo. Salve a classe em disco: selecione o módulo e acione o comando Arquivo/Exportar Arquivo. Você terá um documento com extensão CLS. Para incluí-lo em outro projeto, importe a classe. Se o código estiver bem depurado e funcionando corretamente, você vai poder reutilizar a classe numerosas vezes, sem voltar a abri-lo. No início, pode dar trabalho. Depois, tudo fica mais fácil.
Para ir mais além 1. Testei a rotina ListaArquivosDeSom com os programas Media Player 2,
do Windows, WinAmp e MusicMatch Jukebox. Com este último não funcionou. O programa é aberto, mas não entende o nome do arquivo que lhe é passado. Então, nada acontece. Com os outros dois, tudo funciona corretamente. Um lembrete: se o Media Player já estiver aberto, você receberá uma mensagem de erro.
2. Modifique o formulário frmSoundPlay para que o usuário tenha condições de indicar, também, o nome do arquivo M3U. Assim, ele poderia ter diferentes programações musicais, cada uma contida num arquivo M3U.
3. No método CreateList, experimente trabalhar com o nome completo
do arquivo. Isso possibilitará criar listas com arquivos musicais localizados em diferentes diretórios.
4. Estude a possibilidade de carregar a lista M3U num controle ListBox e
reordenar a seqüência das músicas. Veja um exemplo de reordenação do conteúdo de listas no Capítulo 34, dedicado à criação de um assistente de banco de dados.
5. Como exercício de programação, pense em criar outras classes úteis. Dê
182
preferência a criar objetos que resumam códigos que, volta e meia, você precisa revisitar. Um exemplo que me ocorre é criar um objeto para exibir as janelas Abrir e Cópia (e talvez outras caixas de diálogo do Word) e retornar o arquivo ou o diretório selecionado pelo usuário. Esse assunto é um candidato óbvio. Basta você contar as vezes que essa operação se repete nos projetos deste livro.
Protetor de tela Quer abrir um protetor de tela do Windows a partir de um programa VBA? É fácil: use o seguinte procedimento, naturalmente adaptando o nome do arquivo SCR: Sub AcionaSCR() Dim n As Long n = Shell(“c:\windows\system\oceano.scr /s”, _ vbNormalFocus) End Sub
Botões OK e Cancelar No VBA, os formulários funcionam como caixas de diálogo. Portanto, para seguir o padrão, o botão OK (ou equivalente) deve ter sua propriedade Default ajustada para True. Também por causa do padrão, o botão Cancelar (ou Fechar) deve ter a propriedade Cancel = True. Com isso, se o usuário pressionar a tecla Enter, acionará OK. Se pressionar Esc, fechará o formulário pela ação do botão Cancelar. Esta não é uma regra absoluta, mas pode ser aplicada na maioria dos casos.
O público e o privado Você deve ter estranhado, neste projeto, o nome das variáveis m_strFileName, m_StringFileDir e m_strPlayer. Esse prefixo, m_, é usado para indicar que a abrangência da variável é interna ao módulo. Em qualquer situação, isso é importante, em especial quando se está trabalhando com projetos grandes e desenvolvidos por mais de um programador, onde é maior a possibilidade de confusão. Em programação orientada a objeto, identificar a abrangência das variáveis tem peso ainda maior. Constitui um princípio básico isolar os métodos e propriedades, que são públicos, dos valores internos de uma classe, que devem ser estritamente privados.
183
21 Mala direta num clique Produza cartas personalizadas com dados do Word, do Access ou do Excel Ficha do projeto Projeto: Mala Direta O que faz: Prepara documentos e etiquetas personalizadas com uma carta-modelo do Word e fontes de dados do Access, do Excel e do próprio Word. Arquivos e requisitos do projeto: Formulário frmMalaDireta e código interno. Conhecimento técnico: Uso do VBA/Word para abertura de fontes de dados para mala direta. Esquemas para manipulação gráfica de controles em formulários. Para experimentar o programa, você pode usar os seguintes arquivos: Documentos-base: Cartabd2.dot, Cracha3.dot Fontes de dados: Wdimport.doc, Clientes.mdb, Plclient.xls Nível de programação: Intermediário
Você sabe como emitir uma mala direta via VBA? Não é difícil. O projeto de que trata este capítulo demonstra como fazer isso a partir de três fontes de dados distintas: um documento do Word, um banco de dados Access e uma planilha Excel. Para utilizar este aplicativo, como em qualquer atividade de mala direta, você precisa ter a carta-modelo e a fonte de dados. A mesclagem das informações provenientes desses dois documentos é que vai produzir o resultado final da mala direta. A carta-modelo, você sabe, é o documento que contém os campos de mala direta, os quais serão substituídos, adequadamente, por informações extraídas da fonte de dados. Naturalmente, os campos nesse documento devem corresponder nominalmente aos campos da fonte de dados. A Figura 21.1 mostra um exemplo de como se apresentam os campos no documento-base para a mala direta.
F I G U R A 2 1 . 1 Na oval em destaque, os campos da
carta-modelo para mala direta
Para inserir os campos de dados num documento do Word, você precisa primeiro saber os nomes dos campos na fonte de dados. Com isso, aciona-se Inserir/Campo e, na tela seguinte, escolhe-se Mala Direta e MergeField. Na caixa Códigos de Campos, deve-se então digitar o nome do campo desejado (Figura 21.2). Essa operação repete-se para cada campo, durante a elaboração da carta-padrão. Concluída a carta modelo, pode-se salvá-la como um documento DOT (modelo) ou como um DOC. A decisão depende de alguns detalhes. Se você pretende usar continuadamente essa carta-padrão, o melhor é salvá-la como modelo. Se, no entanto, a intenção é utilizá-la uma única vez, o melhor é escolher o DOC. Modelo ou documento simples, ambos servem como base para a emissão de malas diretas. 185
E a fonte de dados? Esta, entre outras possibilidades, pode ser um documento Word, uma base de dados Access ou uma planilha Excel. Em geral, o documento Word consiste numa tabela cuja primeira linha indica os nomes de cada coluna. Passemos, então, ao projeto.
F I G U R A 2 1 . 2 Inserção de campos no documento-padrão
O aplicativo Mala Direta contém um único formulário, frmMalaDireta (Figura 21.3), com os seguintes controles:
186
n
os clássicos botões de comando OK (cmdOK) e Cancelar (cmdCancelar);
n
uma moldura com quatro botões de opção (opt1 a opt4), correspondentes às fontes de dados que o usuário pode escolher;
n
duas caixas de texto, txtDoc e txtFonteDados, com dois botões de comando associados: cmdProcurarDoc e cmdProcurarFonte. Nessas caixas o usuário deve indicar, respectivamente, o documento-base para a mala direta e a fonte de dados. Os botões de comando exibem a caixa de diálogo Abrir, para que o usuário possa navegar na árvore de diretórios e indicar os arquivos sem digitar; e
n
uma caixa de texto, txtComplemento, na qual deve ser digitado uma informação complementar, conforme a fonte de dados escolhida.
F I G U R A 2 1 . 3 O formulário frmMalaDireta, exibindo todos os
controles
Tratemos, primeiro, do funcionamento desse formulário. Cada fonte de dados escolhida implica um tipo de arquivo e, se for o caso, um tipo específico de complemento. A tabela abaixo lista todas as possibilidades cobertas pelo programa. FONTE DE DADOS
TIPO DE ARQUIVO
COMPLEMENTO
Documento do Word com tabela
DOC, com tabela na qual o primeira linha contém os nomes dos campos
Nenhum
Banco de dados Access – Tabela
MDB
Nome da tabela que é a verdadeira fonte de dados
Banco de dados Access – SQL
MDB
Declaração SQL para filtrar registros de uma ou mais tabelas do banco de dados
Pasta de trabalho XLS – Planilha
XLS
Nome da planilha (folha de dados) que contém as informações desejadas
O formulário frmMalaDireta tem algumas características que o tornam compatível com as diferentes características das fontes de dados. A fonte padrão é o documento do Word. Assim, quando o programa se inicia, o usuário não vê a caixa de texto txtComplemento, porque ela é desnecessária nesse caso. 187
F I G U R A 2 1 . 4 O formulário, com a opção-padrão: documento
do Word
Não é necessário exigir a caixa na qual o usuário deveria digitar o complemento, já que a opção Fonte de dados (arquivos DOC) dispensa esse complemento. No entanto, quando se clica em qualquer outra alternativa de fonte de dados, a caixa txtComplemento aparece, como na Figura 21.3. Os rótulos associados às caixas de texto txtFonteDados e txtComplemento também se modificam, conforme a fonte de dados escolhida. Essa movimentação é garantida pela rotina Click de cada botão de opção. Aqui, o exemplo da sub opt3_Click: Private Sub opt3_Click() m_intOpcao = 3 m_strCuringa = “*.mdb” If Right(txtFonteDados, 3) “mdb” Then _ txtFonteDados = “” labComplemento = “Sentença SQL:” labFonteDados = “Fonte de dados (arquivo MDB):” ExibeOcultaControles True End Sub
A rotina ExibeOcultaControles garante que a caixa de texto txtComplemento e o rótulo labComplemento fiquem ocultos quando a fonte de dados é um documento do Word (opção 1), e se tornem visíveis nas outras situações. Mas o que interessa, de fato, vem depois que o usuário clica no botão OK. Em primeiro lugar, o programa testa se as caixas de texto (duas ou três) estão devidamente preenchidas. No caso de preenchimento com nomes de arquivos, o 188 devidamente implica a existência do arquivo. As funções TestaPreenchimento,
ArquivoExiste e EscolherArquivo dão uma ajuda nesse processo. EscolherArquivo exibe a caixa de diálogo Abrir, do Word, para que o usuário possa indicar o arquivo sem digitá-lo. Se ele fizer isso, passará, tranquilamente, nos testes das outras duas funções. ArquivoExiste verifica se o conteúdo das caixas de texto txtDoc e txtFonteDados corresponde, realmente, a um arquivo. Por fim, TestaPreenchimento centraliza os testes, solicitando inclusive os serviços de ArquivoExiste. Se algo irregular é detectado, o programa avisa ao usuário e pára. Quando tudo está normal, o botão cmdOK chama a rotina ProduzDocMalaDireta. Esta é, na verdade, a parte do programa que faz acontecer. ProduzDocMalaDireta recebe quatro parâmetros: n
intFonte, o índice da fonte escolhida pelo usuário;
n
strTemplate, o caminho completo do arquivo com a carta-padrão;
n
strFonte, o caminho completo do arquivo da fonte de dados; e
n
varComplemento, tabela, planilha ou sentença SQL, conforme a fonte de dados.
Com relação a varComplemento, vale destacar dois aspectos. Primeiro: trata-se de um parâmetro opcional. Portanto, não precisa ser incluído, obrigatoriamente, na chamada da rotina. Isso permite que, quando a fonte de dados é um documento do Word, a chamada não inclua esse parâmetro. O segundo aspecto está na chamada: ProduzDocMalaDireta m_intOpcao, txtDoc, _ txtFonteDados, txtComplemento.Text
Oberve que o conteúdo de outras caixas de texto é passado com o nome do controle (txtDoc, txtFonteDados). Todavia, se você usar txtComplemento, vai provocar um erro. Isso porque o tipo de dados esperado por ProduzDocMalaDireta é variant. O nome do controle causa confusão porque a rotina (na verdade, o método OpenDataSource) fica sem saber se se trata do objeto ou do conteúdo dele. Para evitar dúvidas, use txtComplemento.Text. Outra alternativa seria criar uma variável de texto (string), associar a ela o valor do texto em txtComplemento e enviar essa variável. Concluída a mesclagem dos dados, ProduzDocMalaDireta fecha a carta-modelo, fecha o formulário e deixa na tela apenas o documento final: Você pode usar como carta-padrão não apenas cartas, no sentido estrito. Valem também documentos como etiquetas de endereçamento e crachás. Para esses últimos documentos, existem no mercado etiquetas pré-formatadas, algumas das quais estão listadas no Word (Ferramentas/Mala Direta/Criar/Etiquetas de Endereçamento). A dica para elaborar crachás e etiquetas ilustradas, como as exibidas na Figura 21.5, é produzir o documento básico e depois incluir os objetos e formatações na primeira etiqueta (na verdade, a célula de uma tabela). Em seguida, basta 189
copiar o conteúdo dessa célula e colar nos demais. Observe que a primeira célula contém o campo de dados NomeCliente (veja o arquivo Cracha3.dot). Nas demais, esse campo é sempre precedido pelo campo Próximo. Isso garante que cada etiqueta ou crachá corresponderá a um novo registro.
F I G U R A 2 1 . 5 Etiquetas ou crachás ilustrados: uma das
possibilidades da mala direta
F I G U R A 2 1 . 6 Estrutura dos campos para etiquetas ou crachás
com imagem e formatação
Por fim, um detalhe: ao contrário do que ocorre com os projetos desenvolvidos nos Capítulos 28 a 31 e 33-34, você precisa ter o Access ou o Excel em sua 190 máquina para executar essas operações de mala direta. De todo modo, quem
não possui esses dois programas pode trabalhar com tabelas do Word (veja o documento wdimport.doc), ou mesmo criar e acessar bancos de dados Access via programação (busque a ajuda do VBA sobre o método CreateDatabase, do DAO – Data Access Objects).
Para ir mais além 1. Entre todos os projetos deste livro, Mala Direta é um dos mais “ampliá-
veis”. Você pode modificá-la de inúmeras formas: para incluir opções do método OpenDataSource não tratados aqui (banco de dados protegidos por senhas, por exemplo), para criar nova interface ou, simplesmente, para redesenhar a interface do programa.
2. Mala Direta é um projeto que se adaptaria como uma luva à criação de
um assistente. O assistente (veja um exemplo no Capítulo 34) é um tipo de programa que guia o usuário, passo a passo, para concluir uma tarefa. Aqui, pode-se pensar no seguinte esquema:
n
Passo 1 – O usuário escolhe o documento-base.
n
Passo 2 – Escolha do tipo de fonte de dados.
n
Passo 3 – Localização do arquivo da fonte de dados.
n
Passo 4 – Indicação do complemento, conforme a fonte escolhida.
n
Fim – Emissão do documento com os dados mesclados.
3. Se você tem o Office 2000 e resolve experimentar o projeto Mala Direta
com o banco de dados Clientes.mdb, fornecido como exemplo, vai receber a seguinte mensagem ao operar o programa:
F I G U R A 2 1 . 7 Aviso do Access 2000: o banco de dados é de
versão anterior
Simplesmente acione OK. Essa mensagem é normal. De fato, o banco de dados Clientes.mdb foi construído com o Access 97. Preferi mantê-lo nessa versão para que tanto usuários do Office 2000 como do Office 97 pudessem usar o programa. Observe um detalhe: em outras aplicações deste livro, o mesmo banco de dados é utilizado e essa mensagem não aparece. Por quê?
191
Como foi dito no capítulo, a operação de mala direta exige a presença do Access, que é o responsável por esse aviso. Nos outros casos (por exemplo, nos Capítulos 28, 30 e 34) o projeto manipula banco de dados, por meio das bibliotecas DAO. Estas usam os princípios de programação do Access mas não precisam do programa, para funcionar.
Automação Por meio da Automação – antes chamada Automação OLE –, você pode trabalhar com outros programas em seus projetos tratando-os como objetos. Naturalmente, para que isso seja possível é necessário que o programa dê suporte a essa tecnologia. Veja a seguir um exemplo com o Excel. O código abaixo declara o programa como um objeto, abre-o e, em seguida, manipula o programa por meio desse objeto. O procedimento abre um arquivo, seleciona a primeira planilha da pasta de trabalho e, nela, a célula (1,1). Em seguida, apresenta numa mensagem o valor dessa célula e fecha o programa. Sub AbrirExcelViaAutomação() On Error GoTo ExcelAuto_Err Dim objXL As Object Set objXL = CreateObject(“Excel.Application”) objXL.Visible = True objXL.Workbooks.Open “c:\solucoes\plclient.xls” objXL.Worksheets(1).Cells(1, 1).Select MsgBox objXL.Worksheets(1).Cells(1, 1).Value objXL.Quit Set objXL = Nothing ExcelAuto_Fim: Exit Sub ExcelAuto_Err: MsgBox Err.Description Resume ExcelAuto_Fim End Sub
A última linha do procedimento destrói o objeto criado com CreateObject. Não se esqueça de incluir esse comando (Set Object = Nothing) para garantir a liberação da área de memória ocupada pelo objeto. 192
22 No comando de todas as barras Uma aplicação que lista todas as barras de menus e de ferramentas do Word Ficha do projeto Projeto: Barras de Comandos O que faz: Esta aplicação relaciona todas as barras de comandos (ferramentas, menus) disponíveis no Word. Oferece ainda uma forma de visualizar esses objetos e de gerar um documento com uma lista de suas principais propriedades. Arquivos e requisitos do projeto: Formulário frmBarrasComandos e código interno. Conhecimento técnico: Manipulação das coleções de barras de comandos do Word. Nível de programação: Intermediário
Assim como os estilos, as fontes, os documentos e uma longa série de outros itens, as barras de comando, no Word, também constituem uma coleção de objetos – a coleção CommandBars. Esse conjunto de recursos envolve três tipos de objetos: n
barra de menus
n
barra de ferramentas normal
n
barra de ferramentas pop-up
Naturalmente, as barras de menus e as barras de ferramentas são, dos três, as classificações mais populares. Para tentar uma diferenciação simples, a barra de menu é uma barra de ferramentas que, em lugar de botões e caixas de combinação, traz apenas opções de menu. As barras de ferramentas pop-up compreendem barras como Formas, AutoFormas, Setas Largas, Fluxogramas e Linhas. Em geral, o termo barra de comandos engloba os seguintes itens:
194
n
barras de menus, barras de ferramentas e menus de atalho
n
menus em barras de menus e barras de ferramentas
n
submenus em menus, submenus e menus de atalho
Para demonstrar como o VBA lida com as barras de comando, desenvolvemos um aplicativo chamado Barras de Comandos que trabalha com as principais propriedades desses objetos. O programa exibe uma lista de todas as barras disponíveis no ambiente do Word. O projeto compreende apenas um formulário e seu código interno. Esse formulário (frmBarrasComandos) contém uma caixa de listagem (lstBarras), um rótulo (labNumBarras), dois botões de comando principais – Exibir Barra (cmdExibir) e Ocultar Barra (cmdOcultar) – e dois botões de opção – Nome Local e Nome (opt1 e opt2). Adicionalmente, há ainda um botão Fechar (cmdFechar) e um rótulo Exibir Dica/Não Exibir Dica (labToolTip) que, clicado, funciona como indicador/comutador para exibir ou ocultar uma dica a respeito da barra selecionada na caixa lstBarras. Também está no formulário o botão de comando Produzir Lista Doc (cmdProduzir), que gera um documento com a relação de todas as barras de ferramentas e suas principais propriedades. Há ainda o rótulo Controles da Barra (labControlesBarra), que exibe a lista de controles existentes na barra de comandos, selecionada na caixa de listagem. Como funciona o programa? Ao entrar em ação, ele exibe, na caixa lstBarras, a lista de todas as barras de comando disponíveis no Word. Os itens, portanto, variam se o seu Word é 97 ou 2000, ou se você criou barras personalizadas. Ao rodar o Barras de Comandos em meu Word 2000, ele aponta a existência de 109 barras, das quais – verifico – somente duas são pessoais (ou seja, foram criadas por mim, não vieram embutidas no programa). Para exibir uma barra de ferramentas, deve-se selecionar o nome dela na caixa de listagem e acionar o botão Exibir Barra. Para ocultá-la, pressiona-se o botão Ocultar Barra.
F I G U R A 2 2 . 1 A tela da aplicação: exibir, ocultar e listar barras
Os botões de opção Nome Local e Nome fazem a caixa de lista mostrar, respectivamente, o nome da barra de comando em português e no original, em inglês. Naturalmente, as barras que você cria têm o mesmo nome nos dois casos. Por fim, vem a etiqueta Exibir Dica/Não Exibir Dica. Esse rótulo tem o papel duplo de comutador e indicador. Na primeira função, ao receber um clique, liga ou desliga a exibição da dica, no controle lstBarras. Na outra, indica, em sua propriedade Caption, a função ativa: Exibir Texto ou Não Exibir Texto. Quando o usuário clica no rótulo Controles da Barra, abre-se uma caixa de mensagem com a lista de todos os controles disponíveis na barra selecionada. Observe que os rótulos ExibirDica e Controles da Barra poderiam muito bem ser botões de comando. Contudo, achei que os botões para os dois casos deveriam ser menores. Infelizmente, os objetos CommandButton do VBA têm uma limitação: não aceitam redução de sua altura abaixo de certo valor, sem esconder a legenda. Como os rótulos não apresentam esse problema, resolvi ficar com eles, fazendo-os parecer botões (propriedade SpecialEffect igual a fmSpecialEffectRaised = 1).
O código O primeiro movimento do programa é preencher a caixa lstBarras com a lista das barras de comandos disponíveis no ambiente do Word. Para isso, é chamada à ação a rotina ExibirLista. Esta rotina recebe um parâmetro inteiro que indica se ela deve mostrar as barras com o nome original (propriedade Name) ou com o nome em português (propriedade NameLocal). Os valores – atribuídos arbitrariamente – são 1 para NameLocal e 2 para Name. Assim, ExibirLista define o que fazer a partir desse parâmetro. Em ambos os casos, a ação básica é percorrer a coleção de objetos CommandBars: 195
For n = 1 To CommandBars.count lstBarras.AddItem n & “ ” & CommandBars(n).NameLocal Next n
O que varia, aí, é apenas a propriedade: NameLocal ou Name. ExibirLista convoca o trabalho de outra rotina, DefinirToolTip. Esta recebe um parâmetro booleano. Se verdadeiro, a dica será exibida. Se falso, a propriedade ControlTipText de lstBarras é anulada – ou seja, reduzida a uma string nula: lstBarras.ControlTipText = “”
A definição dessa string, quando não nula, é uma lista de valores das propriedades da barra de comandos escolhida. Essa lista, separada por pontos-e-vírgulas, reúne: n
Propriedade NameLocal, se a listagem está em inglês; ou Name, em caso contrário;
n
Propriedade BuiltIn (Verdadeiro/Falso) – Indica se a barra é própria do Word ou criada externamente;
n
Propriedade Visible (Verdadeiro/Falso) – Indica se a barra está visível no momentoP;
n
Propriedade Type – Informa se a barra é do tipo msoBarTypeNormal (0), msoBarTypeMenuBar (1) ou msoBarTypePopup (2).
A dica a ser exibida é definida quando se seleciona uma barra de ferramentas na lista de lstBarras. Depois, basta deixar o cursor do mouse por alguns segundos sobre lstBarras para provocar a exibição da dica. Adicionalmente, pode-se dar um duplo clique no nome da barra para mostrar uma mensagem com a discriminação de todas as propriedades da barra selecionada.
F I G U R A 2 2 . 2 Resumo de propriedades da barra Cabeçalho e 196
Rodapé
As outras operações executadas pelo programa dependem da intervenção do usuário. Os botões Exibir Barra e Ocultar Barra cumprem suas funções, graças ao trabalho da rotina ExibirOcultar. Esta recebe dois parâmetros: blnExibir (True/False), que sinaliza exibir ou ocultar; e lngPos, um inteiro longo opcional, usado para indicar a posição em que a barra deve ser exibida na tela no caso de blnExibir=True. Essa posição é expressa por uma das seguintes constantes intrínsecas:
CONSTANTE
VALOR
POSIÇÃO
msoBarLeft
0
À esquerda
msoBarTop
1
No alto
msoBarRight
2
À direita
msoBarBottom
3
Na parte inferior
msoBarFloating
4
Flutuante
msoBarPopup
5
Pop-up
msoBarMenuBar
6
Barra de menu
Não importa se a operação consiste em exibir ou ocultar a barra selecionada: o eixo da atividade é definido por este mesmo trecho de código: With CommandBars(n) .Visible = blnExibir .Position = lngPos End With
O valor n, no caso, corresponde ao índice da barra indicada na coleção CommandBars. Se a chamada ao procedimento passa um valor para lngPos, então a barra é exibida na posição indicada; se nenhum valor é passado, o padrão é mostrar a barra na parte superior da janela. Ou seja, o valor-padrão é msoBarTop. Em muitos casos, o comando para tornar a barra visível/invisível vai produzir um erro: “Não é possível exibir/ocultar a barra indicada”. Na maioria das vezes, isso vai ocorrer durante as tentativas de exibir (e não de ocultar) a barra. Esse é o comportamento normal. Lembre-se, por exemplo, de que a maioria das barras de comando só se apresenta dentro de um contexto específico. A barra Editar Figura, por exemplo, só aparece quando há uma figura selecionada. Restrições similares ocorrem com Cabeçalho e Rodapé, Mala Direta, Tabelas e numerosas outras barras. O botão Produzir Lista Doc, como vimos, gera um novo documento Word, com a lista das barras de comandos e todas as suas principais proprieda-
197
des. Nessa operação, o texto é “digitado” com o método TypeText e depois convertido em tabela. Um aspecto interessante a destacar nesta aplicação é que, se você extrair o código associado ao botão Produzir Lista Doc, ela assume as características do programa em que estiver hospedada. Isso significa que é possível exportar o formulário e fazê-lo rodar, sem nenhuma modificação (com exceção do código citado acima), em diferentes programas VBA. Além de criá-lo no Word, experimentei-o no FrontPage, no Excel e no PowerPoint, versão 2000. No Access foi possível importar o formulário, mas as constantes relativas às barras de comandos não foram reconhecidas. Na versão 97, também consegui rodá-lo no Word e no Excel. Para não dizer que, absolutamente, não modifiquei o programa, acrescentei à rotina UserForm_Initialize a seguinte linha: Me.Caption = Me.Caption & “ - ” & Application.Name
O objetivo era forçar que, em ação, o formulário mostrasse, na linha de título, o nome de nossa aplicação ao lado do nome do programa hospedeiro. Além disso, alterei o rótulo localizado acima de lstBarras para que mostrasse apenas “Barras disponíveis”, e não “Barras disponíveis no Word”. É o que você pode ver nas telas a seguir:
F I G U R A 2 2 . 3 O programa, rodando no Excel, no FrontPage e
no PowerPoint
198
Você deve estar se perguntando: por que extrair a rotina cmdProduzir_Click? É simples: de todo o código, apenas ela contém referências a objetos que existem somente no Word – por exemplo, o método ConvertToTable. A tentativa de executar um comando desses em outro aplicativo resulta em erro.
Até agora, falamos unicamente das barras de comandos em si. Mas, é claro, essas barras abrigam diferentes tipos de controles, dos quais os mais populares são os botões de comando. Na verdade, as barras de comandos podem incorporar outros tipos de objetos, como caixas de combinação, menus (no estilo Arquivo, Editar etc.) e controles pop-up. Basta você dar uma olhada nos diferentes objetos existentes nas barras de ferramentas do Word. Em outros projetos deste livro, você pode ver como criar uma barra de ferramentas e, também, como incluir e eliminar controles. Neste projeto, o clique no rótulo Controles da Barra apenas mostra a lista dos controles existentes na barra selecionada. Isso é feito mediante o seguinte código: n = PegarNúmero() iTot = CommandBars(n).Controls.count s = “Total de controles: ” & iTot & vbCr & vbCr For i = 1 To iTot s = s & CommandBars(n).Controls(i).Caption & vbCr Next i MsgBox s, vbOKOnly + vbInformation, “Barra ” & _ CommandBars(n).Name
O valor n corresponde ao índice da barra de ferramentas, na coleção CommandBars. A partir dele, obtém-se o total de controles na barra em questão e, com um loop, acumula-se a lista dos nomes dos controles, afinal exibida numa caixa de mensagem. Esta tem por título o nome da barra e informa o número e o nome dos objetos.
F I G U R A 2 2 . 4 Dados sobre a barra Visual Basic
199
Para ir mais além 1. Os métodos e propriedades das barras de comandos somam-se às dezenas. Você só vai criar intimidade com esses itens se tiver necessidade de usá-los e se pesquisar sobre eles. O próprio VBA oferece as fontes básicas de pesquisa. A primeira delas é a ajuda pop-up. Em dúvida sobre um objeto? Escreva o nome dele na janela Imediata e, em seguida, coloque um ponto, como quem vai pedir um método ou propriedade. A caixa de ajuda pop-up lhe dá dicas preciosas sobre os recursos do objeto. Outra fonte de consulta é o Pesquisador de Objeto (no VBA, Exibir/Pesquisador de Objeto, ou simplesmente F2). Por fim, é claro, a Ajuda.
F I G U R A 2 2 . 5 Ajuda pop-up, uma fonte de pesquisa
2. Estenda os recursos da aplicação Barra de Comandos. Inclua no projeto
uma segunda caixa de listagem para abrigar os controles existentes na barra selecionada em lstBarras. Ou seja, um clique em lstBarras provoca a atualização da nova caixa, a qual passa a exibir a lista de controles correspondentes.
200
Intimidade com desconhecidos Ao longo deste livro, você tem visto que a maior parte dos objetos do Word pertence a uma coleção. A vantagem disso é que você ganha intimidade até com objetos dos quais nunca se aproximou antes. Eis um exemplo. Eu nunca escrevi uma rotina a respeito de parágrafos. Mas, por dedução, sei que deve existir uma coleção Paragraphs. Sei, também, que tem de haver uma propriedade que controle o alinhamento, que deve ser Alignment (à esquerda, à direita, centralizado etc.). Então, vamos escrever uma pequena rotina baseada, apenas, na dedução e na intimidade adquirida no trato com outros objetos. Aí está: Sub AlinhaParágrafo() Dim p As Paragraph For Each p In ActiveDocument.Paragraphs If p.Alignment = wdAlignParagraphCenter Then p.Alignment = wdAlignParagraphJustify End If Next p End Sub
Logo ao escrever Dim p As, na primeira linha, a ajuda pop-up já me deu a dica do tipo de objeto-variável Paragraph. Depois, joguei na lógica: os parágrafos existem num documento. Então escolhi o documento ativo. Com um If, defini: se for encontrado um parágrafo com alinhamento central, vamos trocá-lo para justificado. Talvez essa intimidade pré-adquirida não seja de muita valia para situações mais complexas, mas que ajuda, ajuda.
201
23 Todos os ícones à sua disposição Como criar um catálogo de todas as imagens de botões disponíveis no Word Ficha do projeto Projeto: Barras de Comandos O que faz: Esta aplicação relaciona todas as barras de comandos (ferramentas, menus) disponíveis no Word. Oferece ainda uma forma de visualizar esses objetos e gerar um documento com uma lista de suas principais propriedades. Arquivos e requisitos do projeto: Formulário frmBotões e código interno. Modelo Icones.dot. Conhecimento técnico: Manipulação das coleções de barras de comandos do Word. Nível de programação: Intermediário
As barras de ferramentas do Word reúnem centenas de ícones. Alguns já se tornaram óbvios, como o disquetinho do comando Salvar ou a impressora do comando Imprimir. Mas há uma enorme quantidade deles nos quais você nunca teve a oportunidade de pousar o olho. Muito bem, se eles são ilustres desconhecidos, então não interessam mesmo. Certo? Erradíssimo. Quando você desenvolve aplicativos no ambiente Word, precisa de ícones para montar suas barras de ferramentas personalizadas – e a melhor fonte deles é o próprio Word. Mas, para usar um ícone disponível no ambiente, você precisa conhecer duas de suas propriedades: a imagem, claro, e o seu número-índice. Este projeto mostra como listar essas duas propriedades de todos os ícones do Word, e assim poder escolher os mais convenientes para um novo projeto. Os botões em barras de ferramentas têm três características que agora nos interessam, em particular. Primeiro, eles são controles do tipo msoControlButton; depois, têm as propriedades Caption (a legenda que apresentam quando o cursor do mouse se move sobre eles) e FaceID, seu número-índice. De posse dessas informações, nosso objetivo é criar barras de ferramentas que reúnam numerosos botões com seus ícones. Esses botões devem mostrar como legendas o seu número índice. Como há um número muito grande de ícones (no Word 2000, verificamos que, sem contar os saltos, eles chegam a 1000), vamos dar ao usuário a opção de escolher uma faixa de FaceIDs que deseje exibir. Para isso precisamos de um formulário, a que chamaremos de frmBotões. Esse formulário terá duas caixas de texto – Início (txtIni) e Fim (txtFim ) – e três botões de comando: Exibir Botões (cmdExibir), Ocultar Botões (cmdOcultar) e Fechar (cmdFechar). Nas duas caixas de texto, o usuário indicará os números correspondentes ao início e ao fim da faixa de FaceIDs que deseja exibir. Os botões de comando têm, respectivamente, a função de exibir os ícones, ocultá-los e encerrar a aplicação.
F I G U R A 2 3 . 1 O form: criando barras de ferramentas
Passemos ao código. O eixo das atividades está na rotina cmdExibir_Click. Sua primeira tarefa é chamar a sub TestaValores para verificar o preenchimento das caixas de texto txtIni e txtFim. Superado esse teste, a sub associada ao clique 203
do botão Exibir cria, no âmbito do modelo Normal (CustomizationContext = NormalTemplate), uma nova barra de ferramentas, chamada Ícones do Word, cujos botões têm FaceIDs correspondentes aos números (de txtIni a txtFim) e legendas (propriedade Caption) iguais aos próprios números-índices. Usa-se o método Add para criar uma barra de ferramentas chamada Botões do Word. O mesmo método é convocado para agregar a essa barra uma coleção de botões cujos índices variam de txtIni a txtFim. O código é o seguinte: Set BarraFer = CommandBars _ .Add(Name:=strBarraFer, Position:=msoBarFloating, _ Temporary:=True) With BarraFer .Visible = True n = 1 For i = intIni To intFim .Controls.Add Type:=msoControlButton .Controls(n).Caption = i .Controls(n).FaceId = i n = n + 1 Next i CustomizationContext = NormalTemplate End With
Cria-se, então, uma barra de ferramentas com grande número de botões. Para acomodá-la em sua tela, arraste para baixo sua extremidade inferior e você terá algo como a figura abaixo:
F I G U R A 2 3 . 2 Barra, no Word 2000, para a faixa de 204
FaceIDs de 1 a 250
Nessa barra você vê apenas os ícones dos botões. Mas o que interessa, mesmo, é saber qual o índice de cada uma dessas figuras. Afinal, a intenção é usar esse painel como um catálogo no qual se possa escolher ícones para uso em aplicações personalizadas. Se você exibir o formulário frmBotões da maneira comum, isso não será possível. Simplesmente porque, como o form é modal (somente ele detém o foco), não há como passar o cursor sobre a barra recém-criada a fim de verificar o valor de FaceID para cada botão. A solução reside num pequeno truque. Você vai carregar o form a partir de uma macro. Nela, normalmente, você chamaria o formulário assim: Public Sub ÍconesDosBotões() frmBotões.Show End Sub
Pois bem. Use o método Show com um parâmetro incomum, forçando que o formulário seja não-modal: frmBotões.Show False
Ou, em outras, palavras, o foco da ação não será exclusivo do formulário. Desse modo, ele continuará ativo e, ao mesmo tempo, dará acesso à barra de ferramentas recém-criada. Ao passar o mouse sobre ela, será possível ver as etiquetas, indicando o número do FaceID de cada botão. Clique no botão Ocultar Botões e a barra de ferramentas desaparecerá. Para isso, entrará em cena a rotina RemoveCommandBar, cujo cerne são as seguintes linhas: CustomizationContext = NormalTemplate Dim cbar As CommandBar For Each cbar In Application.CommandBars If cbar.Name = strNomeBarra Then cbar.Delete End If Next cbar
Essa rotina recebe uma string com o nome da barra de ferramentas (strNomeBarra), localiza-a na coleção CommandBars e apaga-a. E aqui aparece mais uma idiossincrasia do VBA. O método Delete para o objeto CommandBar é perfeitamente legítimo e encontra-se documentado tanto no Word 97 como no 2000. Todavia, se você o utilizar no Word 2000, corre o risco de produzir resultados desagradáveis. Em várias situações, ao empregar esse método, o Word 2000 não apresentou nenhuma mensagem de erro. Mas, também, não eliminou a barra de ferramentas. Aí, estranhamente, permitiu que nova barra com o mesmo nome fosse criada – o que, obviamente, não é aceitável. Para checar isso, vá ao
205
menu Exibir/Barras de Ferramentas/Personalizar e tente criar nova barra com o nome de outra, embutida ou personalizada, já existente. O Word emite um aviso de erro dizendo que aquela barra já existe. Um bug? Talvez Pois bem. Por algum mistério, o Word 2000 finge que apagou uma barra com o nome X e, em seguida, aceita criar outra barra com o mesmo nome. Quando você vai conferir, há barras X duplicadas. Bug? Possivelmente sim. Na ajuda do Word 2000, você encontra um caminho alternativo para eliminar uma barra de comando, que é o seguinte: For Each cbar In Application.CommandBars If cbar.Name = strNomeBarra Then Application.OrganizerDelete _ Source:=NormalTemplate.Name, _ Name:=strNomeBarra, _ Object:=wdOrganizerObjectCommandBars End If Next cbar
Essa alternativa funciona corretamente nas duas versões do Word. Portanto, em lugar do simples e cômodo cbar.Delete, deve-se usar o longo e complicado Application.OrganizerDelete, a fim de manter a compatibilidade com as duas versões do processador de texto. Na verdade, o problema não é exatamente de compatibilidade, mas de fazer o programa funcionar. Para coletar uma seqüência de 500 ícones, o Word consome cerca de 40 segundos em meu Celeron 333 com 128 MB de memória. É tempo demais. Então, resolvi criar uma alternativa mais rápida para exibir os botões de comando do Word. Curiosamente, ela é uma solução que envolve procedimentos manuais. Primeiro, usei o programa descrito até aqui para gerar uma barra de ferramentas, chamada Botões do Word 1, com os botões de índices 1 a 400. Reutilizei o recurso para criar outra barra, Botões do Word 2, com os botões de 401 a 815. Em seguida, copiei as duas barras para um modelo, Icones.dot. Para isso, usei o comando Ferramentas/Modelos e Suplementos/Biblioteca. Transferi as barras Botões do Word 1 e 2 de Normal.dot para Icones.dot, que contém apenas essas duas barras de ferramentas. Em seguida, excluí as barras de Normal.dot. Em Icones.dot, na pasta ThisDocument, adicionei a seguinte rotina: Private Sub Document_New() CommandBars(“Botões do Word 2").Visible = True CommandBars(“Botões do Word 1").Visible = True End Sub
206
F I G U R A 2 3 . 3 Cópia manual de barras de ferramentas
para outro modelo
Dessa forma, toda vez que se criar um novo documento baseado em Icones.dot, automaticamente as duas barras de ferramentas serão exibidas. A diferença, em relação à primeira solução, está em que as barras são exibidas imediatamente. Afinal, o Word não precisa sair coletando ícone a ícone – ele já os tem reunidos em duas barras e, apenas, os apresenta. Nesse caso, basta fechar o documento que contém as barras, aberto apenas para que você possa visualizar os ícones e escolher algum para uso em seus projetos.
F I G U R A 2 3 . 4 Barras Botões do Word 1 e 2: mais de 800 ícones
Como a exibição rápida das barras Botões do Word 1 e Botões do Word 2 nada tem a ver com o projeto atual, podemos modificar o projeto para que elas sejam exibidas a partir do formulário frmBotões. Basta acrescentar a esse form um botão de comando. Ao fazer isso, redesenhamos o form, incluindo também 207
duas molduras, a fim de separar as barras de ferramentas a ser construídas e as barras já prontas. Também entrou no espaço uma caixa de imagem para abrigar um pequeno desenho, a fim de ocupar o espaço em branco à direita, acima do botão Fechar. O resultado final ficou assim:
F I G U R A 2 3 . 5 O form redesenhado: com molduras
Projeto concluído. Se você analisar cuidadosamente os ícones disponíveis no Word, com certeza encontrará muitos que lhe poderão ser úteis ao criar ou personalizar suas próprias barras de ferramentas.
Para ir mais além 1. Pratique a criação de barras de ferramentas. O procedimento é similar ao da criação de outros objetos do Word. Primeiro, adiciona-se nova barra à coleção CommandBars. Em seguida, agregam-se à barra recémcriada os botões desejados. Um exemplo desse processo está mostrado na rotina cmdExibir_Click.
2. Vimos aqui duas propriedades dos botões nas barras de ferramentas: FaceID, que identifica a imagem do botão, e Caption, a legenda apresentada diante do cursor do mouse. Outra propriedade muito importante é OnAction. Ela assume o nome da macro que o botão deve disparar, quando clicado. BarraFer.Controls(1).OnAction = “NomeDaMacro”
208
Não é demasiado lembrar que essa macro deve ser uma Sub do tipo Public. Caso contrário, não estará acessível para uma chamada feita fora do módulo em que se encontra.
Documento como modelo Você sabia que um documento pode funcionar como modelo para outro documento? É simples: o arquivo não precisa ser do tipo DOT (modelo) para que seja tomado como matriz a fim de gerar um novo documento. Para usar um arquivo DOC como modelo, utilize um código como o seguinte exemplo: Sub AbrirDocComoModelo() Dim strDoc As String strDoc = “c:\meus documentos\arquivo.doc” Documents.Add Template:=strDoc End Sub
Modelos globais e suplementos Se você escreveu no Word uma série de programas úteis para o dia-a-dia de seu trabalho, reúna-os num modelo, crie uma barra de ferramentas e passe a usar esse modelo como uma biblioteca global. Há duas maneiras de fazer isso. Uma é copiar o modelo para o diretório de inicialização, do Word, que varia conforme a versão do programa e a instalação. Se você não sabe onde ele fica, digite a seguinte linha na janela Imediata: ? Options.DefaultFilePath(wdStartupPath)
F I G U R A 2 3 . 6 Tela para a inclusão de modelos e
suplementos
209
Em minha máquina, com o Word 2000, ele fica em c:\windows\application data\microsoft\word\inicialização. No Word 97, essa pasta fica dentro do diretório do Office e se chama Iniciar. O Word carrega automaticamente todos os modelos colocados aí. Outra forma de deixar seus projetos sempre disponíveis no sistema é usar o modelo que os contém como um suplemento (add-in). Vá a Ferramentas/Modelos e Suplementos e adicione o modelo. Nesse caso, ele pode estar em qualquer diretório. Mas lembre-se: não é possível visualizar o código nem editar os projetos contidos num modelo global ou num suplemento. Nessa condição, eles podem apenas ser usados. Naturalmente, também é possível instalar um suplemento via programação. O exemplo abaixo instala como add-in o modelo Solucoes.dot, e exibe sua barra de ferramentas. Sub AcrescentaAddIn() AddIns.Add _ FileName:="C:\Meus documentos\Livro\Solucoes.dot", _ Install:=True CommandBars(“Soluções em Word 2000 e 97").Visible = True End Sub
210
24 Um quebra-cabeça de número e cores Domine os 16,7 milhões de cores existentes no arco-íris de seu monitor Ficha do projeto Projeto: Cores O que faz: Permite visualizar cores no padrão RGB (o sistema dos monitores de vídeo) , compondo-as a partir de cores primárias. Arquivos e requisitos do projeto: Formulário frmCores e código interno. Conhecimento técnico: Programação com o objeto ScrollBar (barra de rolagem) e números inteiros longos. Nível de programação: Intermediário
Neste capítulo, nosso alvo é mostrar como o Visual Basic trabalha com as cores disponíveis na tela do computador. Conhecendo o mecanismo das cores, você poderá tirar melhor proveito delas em seus projetos escritos em VB. Você sabe: a cor em cada ponto da tela de seu micro é formada por diferentes combinações de três tonalidades básicas: vermelho, verde e azul. Essas três cores, ditas primárias, constituem a base do sistema RGB (red, green, blue), usado nos monitores de vídeo. Cada componente básico desse sistema pode variar de 0 a 255, admitindo, portanto, 256 diferentes intensidades de uma mesma cor. Assim, o padrão RGB comporta um total de 256´ 256´ 256 combinações – ou 16,7 milhões de cores. Na caixa de diálogo Cores, do Windows, pode-se montar qualquer uma dessas combinações, atribuindo valores a R, G e B. No entanto, esse jogo de números dá apenas uma idéia solta e abstrata do imenso estoque de cores. Você entenderá melhor – ou, pelo menos, visualizará melhor – o sistema RGB se o imaginar como um cubo de cores, traçado num espaço cartesiano em três dimensões.
F I G U R A 2 4 . 1 O cubo de cores RGB
É fácil: cada cor ocupa um eixo e pode assumir valores de 0 a 255 (veja a Figura 24.1). Portanto, você tem coordenadas do tipo (R,G,B) correspondentes a pontos-cores localizados na superfície ou no interior desse cubo cromático. Na origem dos eixos está o ponto (0, 0, 0), que é o preto – ou seja, a ausência de cor. Cada cor básica ocupa um dos vértices do cubo, diretamente ligados à origem: vermelho (255, 0, 0), verde (0, 255, 0) e azul (0, 0, 255). Há ainda quatro vértices de que não falamos. Três deles são os pontos que formam faces do cubo com os pontos básicos. Esses representam as chamadas cores secundárias – amarelo, magenta e ciano. Na prática, elas são combinações de doses máximas de duas cores primárias: 212
CORES PRIMÁRIAS
COR SECUNDÁRIA
RGB
vermelho e verde
amarelo
(255, 255, 0)
vermelho e azul
magenta
(255, 0, 255)
verde e azul
ciano
(0, 255, 255)
O último vértice do cubo situa-se em posição diagonalmente oposta ao ponto de origem. Trata-se do ponto (255, 255, 255). Nele, todas as cores básicas estão em carga máxima. Aí temos o branco. Como o sistema RGB é moeda comum, diferentes linguagens (Basic, C, Pascal) o adotam. Em todas elas, existe uma função RGB, que fornece o valor das combinações de cores. A cor cinzenta, que é o padrão das telas do Windows, por exemplo, corresponde ao trio (242,242,242). Para usá-la como cor de fundo de uma caixa de imagem chamada Image1, basta fazer: Image1.BackColor = RGB(242, 242, 242)
Mas o que quer dizer RGB(242, 242, 242)? Ou, para ser mais claro, que tipo de variável a função retorna? Resposta: um inteiro longo, que varia de zero (preto) a 16.777.215 (branco). Isso traz à tona outra pergunta: de onde vêm esses números? O valor de retorno da função RGB é calculado com a seguinte fórmula: ValorRGB = R + 256*G + 65536*B
Aí, R, G e B são as intensidades das cores primárias que entram na composição de uma tonalidade específica. E os números multiplicados por essas variáveis são, respectivamente, 256 (o lado do cubo) elevado a 0, 1 e 2.
O aplicativo Cores Para demonstrar visualmente essa relação das cores e seus números RGB, vamos desenvolver um programa em VBA chamado Cores. Trata-se de um formulário único com uma caixa de imagem (Image1), três barras de rolagem horizontais (ScrollBar1, ScrollBar2 e ScrollBar3) e quatro labels (labValor1, labValor2, labValor3 e labRGB). Cada barra de rolagem representa uma cor primária. À medida que você manipula essas barras, vai atribuindo diferentes cores à caixa de imagem. Três das labels indicam os valores assumidos pelas barras de rolagem. A quarta exibe o número RGB, calculado a partir dos valores isolados de cada componente.
213
F I G U R A 2 4 . 2 O programa Cores: RGB na prática
O código do programa Vejamos como se desenvolve o código do programa Cores. Para que as três barras de rolagem representem o universo RGB, é preciso definir, adequadamente, os valores mínimo e máximo de cada uma delas para, respectivamente, 0 e 255. Quando o usuário deslocar uma das barras, ela assumirá um valor dentro desse espectro e esse valor deve ser transmitido para três outros pontos: para a etiqueta correspondente à barra deslocada; para a cor mostrada em Image1; e para o número representativo da cor na etiqueta labRGB. A transferência do valor assumido pela barra de rolagem para a etiqueta correspondente é feita a partir do código associado ao evento Change (mudança) da barra de rolagem. Para não escrever um código idêntico no evento Change de cada barra, cada uma delas chama a rotina MoveBarra com um parâmetro identificador: 1 para R, 2 para G e 3 para B. O procedimento MoveBarra recebe, portanto, a indicação de qual barra foi deslocada. A transferência ocorre mediante a seguinte linha de código: Controls(“labValor” & Index).Caption = _ Controls(“ScrollBar” & Index).Value
Observe que, aí, usamos o recurso de concatenar os nomes seqüenciais dos controles labValor (1, 2 e 3) e ScrollBar (idem) para fazer a correspondência dos números. A mesma rotina, MoveBarra, define a cor a ser exibida por Image1. Para isso, lê os valores atuais de cada uma das barras (valores R, G e B) e monta cor de fundo de Image1: 214
Image1.BackColor = RGB(R, G, B)
A cor RGB é exibida, visualmente, em Image1 e, numericamente, na etiqueta labRGB. Em outras palavras, isso significa que a tonalidade formada pela combinação de três números (R, G, B) também é representada por um número único – um inteiro longo que é o número RBG. Para calcular esse número, a rotina MoveBarra chama a função ValorRGB, que se resolve em uma linha e se baseia na fórmula já citada: ValorRGB = R + (256 * G) + (65536 * B)
Assim, o valor RGB, formatado, transfere-se para a etiqueta labRGB: labRGB = Format(ValorRGB(R, G, B), “##,##0")
A rotina MoveBarra revela-se o eixo de nosso aplicativo. Ela é que distribui todos os efeitos cromáticos e numéricos que representam o verdadeiro funcionamento do programa. Há ainda alguns retoques adicionais. Quando o aplicativo se abre, as três barras estão na posição zero. Portanto, a cor exibida na caixa Image1 deve ser (0, 0, 0), ou seja, preto. Mas todas as atualizações de números e cores só acontecem, como vimos, a partir do evento Change das caixas de rolagem. No momento inicial, não há mudança nessas caixas. Então, o valor zero não será escrito nas três etiquetas labValor (1, 2 e 3). Vamos, literalmente, forçar as barras, para que esse número apareça. No evento UserForm_Initialize, definamos o valor de cada barra, primeiro para 1 (número escolhido arbitrariamente) e depois para 0. Isso provoca um evento Change, que dá início à atualização dos valores nas etiquetas. O trecho do código é o seguinte: For i = 1 To 3 With Controls(“ScrollBar” & i) .Min = 0 .Max = 255 .LargeChange = 16 .Value = 1 ‘ força a mudança .Value = 0 End With Next i
Nesse mesmo trecho, define-se também como 16 o valor da propriedade LargeChange de cada barra de rolagem. Se você clica nas setas das extremidades da barra, o valor é incrementado ou reduzido de 1 em 1. LargeChange é o valor do acréscimo (ou subtração) que ocorre quando você clica diretamente na barra, à direita ou à esquerda do botão de rolamento. Escolhemos 16 por uma razão simples: é um submúltiplo de 256, o comprimento total da barra. Com alguns
215
toques à direita do botão de rolamento, podem-se definir valores-chave na escala RGB, como 64 (a quarta parte), 128 (o meio) e 242 (três quartos). Outro detalhe: para dar maior realidade às labels labValor, cada uma delas tem como tonalidade de fundo a cor primária que representa. O projeto Cores inclui ainda a rotina SeparaCores, que mostra como partir do número de um matiz RGB e extrair dele os números das cores primárias que o compõem: Private Sub SeparaCores() Dim R As Long Dim G As Long Dim B As Long Dim corRGB As Long corRGB = Image1.BackColor R G B End
= corRGB Mod 256 = corRGB \ 256 Mod 256 = corRGB \ 65536 Mod 256 Sub
Essa rotina foi agregada ao código apenas para demonstrar como separar as cores básicas de um número RGB. No entanto, ela não é utilizada no projeto. Agora que você já está craque nas correspondências entre números e cores, não vá cair na tentação de sair por aí dizendo frases do tipo: “Que seria do 16.711.680 se todos gostassem do 65.535?”
Para ir mais além 1. Um dos pontos de destaque, no código do projeto Cores, é o uso de va-
riáveis do tipo inteiro longo para trabalhar com os números RGB. Como o número RGB máximo atinge 16,7 milhões, se você usar variáveis inteiras, por exemplo, provocará erros por estouro de capacidade. Os valores inteiros variam apenas de -32.768 a 32.767.
2. Observe com cuidado o funcionamento do operador Mod – operador de módulo, ou resto. Por exemplo, 10 Mod 3 retorna 1, que é o resto da divisão de 10 por 3.
3. Na rotina UserForm_Initialize do projeto Cores, você encontra linhas com o seguinte feitio para definir a cor de fundo de um objeto: labValor1.BackColor = RGB(255, 0, 0)
216
Aqui, usou-se a função RGB porque o objetivo era colocá-la em destaque. No entanto, para cores primárias, seria mais fácil usar as constantes
intrínsecas do VBA relativas a cores: vbRed, vbGreen, vbYellow, vbBlue, vbWhite, vbBlack. Exemplo: labValor1.BackColor = vbRed
4. As cores usadas na indústria gráfica obedecem ao padrão CMYK. Diferente do RGB, esse sistema baseia-se em quatro cores primárias: ciano, magenta, amarelo (yellow) e preto (black). Uma das tarefas básicas dos programas encarregados que geram trabalhos que vão ser impressos em gráficas é converter o RGB que se vê na tela para o CMYK que vai ser posto no papel, e fazer com que os dois sistemas de cores tenham a melhor correspondência possível. O processo de impressão baseado no sistema CMYK é chamado quadricromia. Nele, prepara-se um filme para cada cor básica e obtém-se o resultado final imprimindo uma cor de cada vez. Quer dizer, a página passa por uma série de quatro impressões, uma sobreposta à outra.
Cores nos objetos de desenho A definição da cor de fundo dos objetos de desenho – caixas de texto e outras autoformas – é feita de forma diferente das tabelas. No modo interativo, em lugar de procurar o menu Formatar, clique com o botão direito no objeto e escolha, conforme o caso, Formatar Caixa de Texto ou Formatar AutoForma. Depois, na área Preenchimento, escolha uma cor. No VBA, a seleção do objeto deve ser feita pelo seu índice na coleção Shapes. Em seguida, aplica-se a cor, como mostrado a seguir: ActiveDocument.Shapes(3).Select Selection.ShapeRange.Fill.ForeColor.RGB = wdColorGray10
Na segunda linha, acima, também se pode aplicar a cor no formato: Selection.ShapeRange.Fill.ForeColor.RGB = RGB(204, 255, 255)
217
25 Fazendo os papéis da sorte Construa um programa que organiza e imprime cupons para sorteios Ficha do projeto Projeto: Bilhetes Numerados O que faz: Emite documentos numerados, como cupons ou bilhetes para sorteio. Arquivos e requisitos do projeto: Sorteio.dot, modelo que contém a base para a confecção dos bilhetes. Formulário frmBilhetesComControle, com a lógica de preenchimento, numeração e multiplicação dos bilhetes. Conhecimento técnico: Programação com objetos de desenho do Word. Nível de programação: Avançado
Muitas vezes, na escola ou no escritório, você quer organizar um sorteio entre colegas ou alunos. Nessas ocasiões, em geral, você tira cópias xerox de um texto e, depois, escreve à mão os números de cada bilhete. Com o Word é possível solucionar esse problema de forma mais fácil e elegante. É disso que trata o projeto atual. Na verdade, ele não se aplica somente a essas situações de brincadeira. Uma vez alguém – se não me engano, ligado a uma pequena gráfica – me perguntou como imprimir no Word certa quantidade de cópias numeradas de um mesmo documento. O princípio básico está aqui. Em primeiro lugar, vamos precisar o que pretendemos fazer. Para dar um certo charme ao nosso sorteio no escritório, vamos pegar uma folha de papel A4 e construir nela um modelo de documento, contendo seis bilhetes de sorteio. Trata-se de uma tabela com duas colunas, seis linhas principais e cinco linhas de menor altura que funcionam como separadores entre dois bilhetes (veja a Figura 25.1). Definido esse documento, ele deve ser salvo como o modelo Sorteio.dot.
F I G U R A 2 5 . 1 Página-modelo: seis bilhetes em cada folha A4;
destaque para as caixas de texto
A primeira coluna do bilhete serve como um canhoto de controle, como se fosse um cheque. Assim, se você mantém o canhoto, pode saber qual colega ficou, por exemplo, com o número 17 no sorteio do brinde de Natal em sua seção. A segunda coluna contém palavras-chave que serão substituídas, conforme o caso, por texto ou pelo número do bilhete. Elas são: , , ValorDoBilhete e NúmeroSort. Esta última aparece tanto na coluna de controle como no lado do bilhete: o número de um lado se repete no outro. Há ainda outra particularidade: o título e o texto do bilhete estão soltos na célula da tabela. Naturalmente, o título não deverá ultrapassar uma linha (máximo: 35 caracteres) e o texto, também, está limitado a um total de 325 caracteres. Esses números garantem que o texto para os dois itens não vai extrapolar o espaço previsto. Isso deve ser evitado a todo custo, caso contrário o objetivo de produzir seis bilhetes idênticos em cada página irá por água abaixo. Basta lembrar que, com mais texto, a célula da tabela vai ter sua altura aumentada. 219
Muito bem. Mas aqui aparece um problema: mesmo que o título e o texto principal não excedam os limites, dependendo do tamanho do texto, as palavras-chave ValordoBilhete e NúmeroSort poderão ficar mais acima ou mais abaixo, dentro da célula. Para impedir essa dança, colocamos esses itens dentro de caixas de texto. Esses objetos ancoram numa posição da página e não se deslocam com a variação do texto ao seu redor. Portanto, as duas palavras-chave NúmeroSort e ValordoBilhete estão contidas em caixas de texto (veja a Figura 25.1). Feito o desenho do primeiro bilhete, deve-se copiar o seu conteúdo e colá-lo nos demais. Assim, todas as linhas principais da tabela (ou seja, sem contar as linhas de separação de um bilhete para o outro) terão o mesmo conteúdo. Uma pequena manha: no programa, vamos precisar fazer referência às caixas de texto, que são objetos de desenho. Quando se incluem objetos desse tipo num documento, eles vão assumindo identidades numeradas seqüencialmente numa coleção chamada Shapes. O primeiro objeto de desenho é Shapes(1), o segundo Shapes(2), e assim por diante. Esses números vão sendo atribuídos pela ordem de inclusão do objeto. Se o primeiro que você desenhou fica no fim do documento, não importa – ele terá o índice 1. Em vista disso, tenha o cuidado de, no primeiro bilhete, desenhar as caixas de texto da esquerda para a direita. Ao copiar o conteúdo para os bilhetes subseqüentes, também tenha o cuidado de manter a ordem de cima para baixo. Isso garante que os objetos de desenho vão assumir índices em ordem crescente, da esquerda para a direita e de cima para baixo. Quando discutirmos o código, você vai entender por quê.
O formulário do projeto Neste projeto, o modelo com os bilhetes não contém código. A lógica de manipulação do documento está fora dele, em outro modelo. Basicamente, essa lógica reside num formulário (frmBilheteComControle) e em seu código interno.
F I G U R A 2 5 . 2 O formulário para preencher e numerar os 220
bilhetes
O formulário segue o padrão já definido no perfil dos bilhetes. Há quatro caixas de texto: uma para o título (txtTítulo), outra para o texto do bilhete (txtTexto), mais outra para abrigar o total de bilhetes (txtTotal), e uma última (txtValor), opcional, para situações em que seja necessário atribuir um valor ao bilhete – uma rifa, por exemplo. Por fim, vêm os botões OK (cmdOK), que dispara a confecção dos bilhetes, e Cancelar (cmdCancelar), que encerra o aplicativo.
O código Vejamos agora o funcionamento interno desse formulário. A ação básica ocorre quando o usuário clica no botão OK. A primeira tarefa da rotina cmdOK_Click é chamar a função TesteOK, que verifica o preenchimento dos campos do formulário. Caso haja problema, o processamento é interrompido com aviso ao usuário. A própria estrutura do formulário já é preparada para evitar alguns tipos de erros. A caixa txtTítulo, por exemplo, não aceita textos com mais de 35 caracteres. Sua propriedade MaxLength foi definida para esse número. Outro exemplo: as caixas txtTotal e txtValor estão ajustadas para só aceitar a digitação de números. As rotinas associadas ao evento KeyPress desses controles garantem essa característica. Veja o exemplo da sub txtTotal_KeyPress: Select Case KeyAscii Case 8, 48 To 57 ‘OK Case Else ‘ KeyAscii = 0 End Select
O que está dito nessas linhas de código é o seguinte. Se as teclas pressionadas em txtTotal corresponderem aos valores ASCII 8 (retrocesso) ou aos algarismos (48 a 57), tudo bem. Todas as outras serão anuladas. Mas voltemos à rotina cmdOK_Click. Feito o teste de preenchimento, ela passa a calcular o número de páginas necessárias para produzir o total de bilhetes, indicado pelo usuário em txtTotal. (Aqui, é necessário abrir um parêntese: neste projeto, limitamos o número máximo de bilhetes a 1000. Esse limite está definido na função TesteOK. Para alterá-lo, modifique a função.) Para calcular o número de páginas, basta dividir o total de bilhetes pela capacidade de cada página (seis bilhetes, em nosso exemplo). Com esse número na mão, a rotina avança. Cria um documento novo baseado no modelo Sorteio.dot. Esse documento tem apenas uma página, ou seis bilhetes. Agora, vamos preencher esses seis bilhetes com o título e o texto digitados pelo usuário. Para isso é convocada a sub DefinePágina. Numa primeira versão dessa rotina, não conseguíamos passar mais de 255 caracteres como texto 221
do bilhete. O problema: a rotina baseava-se no esquema de busca e troca, e o objeto Selection.Find não substitui strings maiores que 255 caracteres. Para fugir dessa limitação, recorremos a um pequeno truque em DefinePágina. Usamos Selection.Find para localizar e selecionar a palavra-chave procurada. Mas em lugar de usar o recurso de substituir, aproveitamos que a expressão está selecionada e aplicamos o método TypeText. Isso nos garantiu um acréscimo de 65 caracteres no texto, elevando a capacidade total para 325 – o máximo que é possível escrever na célula. Como o valor, quando existe, é um elemento fixo, é hora de aproveitar e também preencher cada uma das caixas com valor. Para isso, observe que os índices dessas caixas são 2, 5, 8 etc. até 17 – uma progressão aritmética de razão 3. Escrever o valor agora, em cada uma dessas caixas, resulta em economia de trabalho. Se deixarmos para depois, será necessário escrevê-lo não apenas seis vezes, mas em seis vezes o número de páginas necessário para perfazer o total de bilhetes escolhido pelo usuário. Agora, temos uma página com seis bilhetes preenchidos. Falta numerá-los. Mas antes é preciso replicar essa página única para o número já calculado. Como fazer isso? Seleciona-se toda a página, cujo conteúdo é copiado para a memória. Agora, num loop de 2 (a página 1 já está pronta) até o total de páginas, emprega-se o método Paste (Colar) para gerar novas páginas e bilhetes. Isso é feito pelas linhas abaixo: ActiveDocument.Content.Select ‘ Copia a primeira página With Selection .Range.Copy .EndKey Unit:=wdStory .TypeParagraph End With ‘ Expande o documento até intTotalPáginas For n = 2 To intTotalPáginas Selection.Paste Next n
O próximo passo é numerar os bilhetes.Trata-se da tarefa mais complicada do projeto. Em cada bilhete há três caixas de texto a preencher. Se fosse apenas uma caixa em cada bilhete, tudo ficaria muito fácil: bastaria percorrer, num loop, caixa a caixa, selecioná-las e numerá-las. Aí, o índice da coleção Shapes seria, basicamente, igual ao número a ser impresso na caixa. Em nosso caso, a coisa é mais complexa. É preciso escrever o mesmo número na primeira e na terceira caixas de cada bilhete, sendo que a segunda caixa já está preenchida com o valor. 222
No final, o programa produz bilhetes nos moldes mostrados na Figura 25.3. Basta imprimir o documento, empilhar as folhas e cortá-las com uma guilhotina ou com um estilete de escritório. Os bilhetes estão prontos. Uma observação final. Durante o processo de multiplicação dos bilhetes, o programa mantém na Área de Transferência do Windows (Clipboard) uma página com a estrutura de seis bilhetes básicos. Por isso, no final do procedimento cmdOK_Click, chama-se a sub LimpaMemória, que zera o conteúdo do Clipboard. No entanto, nos testes que fiz, esse procedimento funciona em algumas versões do Word, em outras não. Se em sua máquina você receber uma mensagem de erro, desative a chamada a LimpaMemória. Não vou cometer a temeridade de afirmar em quais sim e em quais não. Digo apenas que nas versões do Word 2000 a que tive acesso, o programa funcionou normalmente. Idem na versão 97a. Mas o Word 97, versão b, acusa um erro, embora não haja nenhum problema no código. É bom dizer que também não tenho certeza quanto a essas versões. É difícil encontrar uma máquina em que se possa afirmar qual é a versão existente. Somente em condições de laboratório, mas não cheguei a fazer testes tão apurados. Mesmo quando duas máquinas informam, por exemplo, versão 97b, isso não significa que as duas têm o Word com a mesmíssima configuração. É possível que a DLL que contém o número da versão seja a mesma. Só que há numerosos arquivos interferindo no funcionamento de programas como o Word. Fique atento a esse detalhe. O esquema de numeração de documentos, demonstrado neste projeto, tem utilidade muito maior do que a simples confecção de bilhetes para pequenos sorteios em seu local de trabalho. Usei esse exemplo porque, aparentemente, é o mais próximo da maioria das pessoas. Mas tenho certeza de que você vai encontrar aplicações comerciais, escolares e domésticas realmente úteis e criativas, empregando essa técnica. Mesmo que não haja sorteio, boa sorte.
F I G U R A 2 5 . 3 Os bilhetes prontos: basta cortar e distribuir 223
Para ir mais além 1. Quer tornar mais acessível a idéia deste projeto? Adapte-o para produzir cupons com apenas um número.
2. Para adicionar um pouco de complexidade, avance para bilhetes que incluem duas vezes o mesmo número, como os mostrados aqui, mas sem o item complicador da caixa com o valor do bilhete.
3. Também vale a pena considerar a confecção de bilhetes duplos – ou seja,
bilhetes que têm dois números em lugar de um. Para um universo de mil números, por exemplo, você pode numerar os bilhetes com os pares 000-500, 001-501, e assim por diante. Ou então, começando pelas extremidades: 000-999, 001-998...
4. Com base nos recursos mostrados neste projeto, desenvolvi outro que
produz seqüências de várias cópias de um documento com o mesmo número – ou seja, 1,1,1–2,2,2–3,3,3, num exemplo para três cópias de cada. Esse novo projeto, que você pode analisar no próximo capítulo, também permite numerar documentos partindo de qualquer número indicado pelo usuário.
224
Constantes, uma força para o programador As constantes do Visual Basic representam uma mão na roda para o programador. Elas evitam que você tenha de memorizar números ou consultar, com freqüência, a ajuda ou manuais. Numa caixa de diálogo, por exemplo, se o usuário clica no botão Sim, escolhe o valor vbYes; se aciona o botão Não, escolhe vbNo; se desiste da operação, escolhe vbCancel. Isso é muito mais fácil de guardar do que os números correspondentes. No caso, Sim, na resposta do usuário, equivale a 6, Não a 7; e Cancelar a 2. Há constantes para várias outras situações, como o valor dos botões do teclado, cores e dias da semana. Em geral, são itens muito fáceis de memorizar e deduzir uns a partir de outros. Por exemplo: vbKeyA, vbKeyB, vbKeyF3, vbKeyNumlock; vbBlue, vbBlack, vbYellow; vbMonday, vbTuesday, vbSaturday. Para visualizar a lista completa de constantes intrínsecas do VB, acione Exibir/Pesquisador de Objeto (ou F2) no ambiente de desenvolvimento do Visual Basic. Também é possível descobrir o valor numérico da constante sem consultar o Pesquisador de Objeto. Basta ir à Janela Imediata e digitar ? seguido do nome da constante. Por exemplo: ? vbMonday <Enter>
O VBA apresenta o número 2 na linha seguinte. Se a resposta for uma string vazia, das duas uma: ou a constante não existe, ou você a digitou erroneamente. Observe que o Word também tem suas constantes intrínsecas, todas com o prefixo wd. Boa parte delas se refere a itens específicos do Word, mas algumas têm o mesmo objetivo (e, em certos casos, o mesmo valor) de constantes do VB. Nos exemplos abaixo, tanto faz usar uma constante como a outra, embora nem sempre elas tenham o mesmo valor numérico: wdBrazil = vbBrazil = 55 wdBlue = 2; vbBlue = 16711680 wdDarkBlue = 9; vbDarkBlue não existe
225
26 Clones ao gosto do freguês Produza documentos numerados com quantidade de cópias indicada pelo usuário Ficha do projeto Projeto: Documentos Múltiplos Numerados O que faz: Produz documentos numerados com cópias múltiplas em número indicado pelo usuário. Arquivos e requisitos do projeto: Multidoc.dot, modelo que contém a base para a confecção dos documentos. Formulário frmDocsRepetidos, com a lógica de numeração e multiplicação dos documentos. Conhecimento técnico: Programação com objetos de desenho do Word. Algoritmo de cálculo para o número dos documentos. Nível de programação: Avançado 226
Este projeto é uma extensão do anterior. Naquele, o objetivo era criar uma série de bilhetes numerados (vários numa mesma folha de papel), com texto, valor e número. Neste caso, o processo é simplificado, pois envolve apenas o número. Em compensação, traz uma diferença básica: em vez de vários bilhetes numa mesma página, temos aqui um documento em cada página. O usuário vai determinar quantas cópias deseja produzir do mesmo documento e o número de documentos a emitir, cada um com cópias múltiplas. Se, por exemplo, o usuário escolher três cópias, os documentos, todos iguais, serão numerados na seqüência: 1,1,1-2,2,2 etc. O projeto baseia-se num modelo, Multidoc.dot. O documento-padrão contém apenas uma caixa de texto para receber o número do documento. Fica claro que o objetivo, aqui, é apenas mostrar o uso do recurso. Num documento real, você vai colocar essa caixa de texto no tamanho certo e no lugar adequado, ao lado de outras informações. Como o usuário precisa fornecer informações ao programa, impõe-se a necessidade de um formulário. Vamos criar um, chamado frmDocsRepetidos. Esse formulário tem apenas dois botões, OK (cmdOK) e Fechar (cmdFechar), além de três caixas de texto: txtNumBilInicial, txtTotalBilhetes e txtRepetições. Nessas caixas o usuário deve digitar os números que vão definir as características do documento a ser gerado. Na primeira, ele deve informar o número do documento inicial. Na segunda, o total de documentos que deseja produzir. E, na última, o número de repetições de cada documento. A ação é rápida. Quando o botão OK é pressionado, o programa primeiro verifica se as três caixas de texto estão preenchidas. Em seguida, chama o procedimento Multidoc, que é realmente a principal rotina do aplicativo. O algoritmo para determinar os números dos documentos é semelhante ao utilizado no projeto anterior. A diferença é que agora, em vez de repetir o número num mesmo bilhete, o número se repete em cópias do mesmo documento. Esse recurso é útil na produção de qualquer documento que precise ser numerado, e que tenha mais de uma via em cada número. Vale observar que o total de documentos a ser informado pelo usuário refere-se apenas à primeira via. No exemplo da Figura 26.1, está proposta a emissão de 15 documentos, numerados a partir de 50, com três vias cada. Ou seja, serão 45 folhas. Uma idéia visual do resultado é dada pela Figura 26.2.
227
F I G U R A 2 6 . 1 O formulário frmDocsRepetidos
F I G U R A 2 6 . 2 Documentos numerados a partir de 50, com três
repetições
Para ir mais além 1. Estude com critério a rotina Multidoc. Ela utiliza alguns artifícios para
manter em paralelo várias seqüências de numeração. Uma é a seqüência dos índices das caixas de texto. Não importa o que você faça, eles são sempre números naturais: 1, 2, 3 etc. Outra seqüência é definida pelo número inicial escolhido pelo leitor. Há ainda uma terceira, que contém a lógica de repetição do número de cada documento conforme a quantidade de repetições solicitada.
2. Você pode adaptar o formulário deste projeto para receber, além dos
dados que vão controlar a numeração, as informações textuais do documento – como no caso dos bilhetes numerados.
3. Com os recursos de bancos de dados, mostrados nos Capítulos 27 a 32 e 228
33 a 34, você pode pensar num composto de numeração e persona-
lização. Digamos que seja preciso emitir três cópias de um documento para cinqüenta pessoas cadastradas num banco de dados. Então, além de numerar, você marcaria cada cópia com o nome da pessoa.
Atributos de arquivos Quer saber, num programa, se um arquivo é somente leitura? Use a função GetAttr. Ela retorna um número correspondente à soma dos atributos do arquivo. Por exemplo, para saber se o arquivo Teste.doc, em c:\download, é somente leitura: r = GetAttr(“c:\download\teste.doc”) And vbReadOnly
Se r for igual a zero, isso significa que ele não possui o atributo somente leitura (read only). Caso contrário, ele será igual à constante correspondente ao atributo definido depois da expressão And. Essas constantes são: vbNormal = 0; vbReadOnly = 1; vbHidden = 2; vbSystem = 4; vbDirectory = 16; e vbArchive =32. A definição dos atributos obedece à mesma lógica e é feita com a instrução SetAttr. Exemplo para tornar um arquivo somente leitura e oculto: SetAttr “c:\download\teste.doc”, vbReadOnly + vbHidden
229
27 Para acabar com a “letra de médico” Um programa que emite receitas para médicos e dentistas Ficha do projeto Projeto: Receita Médica O que faz: Automatiza a emissão de receitas médicas e odontológicas. Preenche todo o cabeçalho, numera e salva o documento. Arquivos e requisitos do projeto: Receita.dot, modelo que contém, externamente, a base do documento e, internamente, o formulário frmReceita, com seu código interno. Receita.ini, arquivo que armazena os itens de personalização do aplicativo. Conhecimento técnico: Programação com indicadores (bookmarks) e o objeto Selection.Find. Escrita e recuperação de informações em arquivos INI. Acesso, via VBA, ao cabeçalho e ao rodapé do documento. Nível de programação: 230
Intermediário
Alguns meses atrás, fui ao consultório de um médico e fiquei animado com o fato de vê-lo emitir a receita usando um computador e uma impressora. Enquanto ele digitava, pensei comigo: Que bom! Isso resolve o problema da “letra de médico” – aquelas garatujas que exigem trabalho de interpretação e adivinhação do paciente e do balconista da farmácia. Claro que há aí algo folclórico: nem todos os médicos têm caligrafia incompreensível, mas pensei nisso ao vê-lo operando o computador. Só que, notei depois, ele usava muito pouco do que a tecnologia é capaz de oferecer. Motivado por essa constatação, resolvi criar, no Word, uma solução simples para automatizar a emissão de receitas médicas e odontológicas. Sem nenhuma mudança, a mesma aplicação também pode ser usada para emitir a solicitação de exames. Essa aplicação, a Receita Médica, é apresentada a seguir.
O documento-modelo Toda a aplicação está contida no modelo Receita.dot, que apresenta, por fora, a base do documento-receita e, internamente, o código para a automação de seu preenchimento. A estrutura do documento é a mais singela possível. Contém um cabeçalho, no qual constam o nome do médico ou dentista, sua especialidade e seu número no Conselho Regional de Medicina ou no de Odontologia. Ainda no cabeçalho, estão o endereço e o telefone do consultório. Algo com estrutura semelhante ao seguinte: Dra. Ana Lúcia de Lima
Rua das Acácias, 978 cj. 54
Endocrinologista
00000-000 – Cidade – UF
CRM-UF 00.000
Tel. (000) 2727-3344 E-mail:
[email protected] Numa primeira versão deste projeto, incluí um cabeçalho fixo, como o mostrado acima. Para usar a solução, o médico ou dentista teria de abrir o arquivo Receita.dot e editar o cabeçalho, escrevendo seu nome, especialidade e endereço. Depois, decidi que seria mais prático para o usuário que o preenchimento do cabeçalho também fosse automatizado. Assim, substituí todas as informações daquela área por palavras-chave de substituição. A idéia é que, durante o preenchimento da receita, o cabeçalho também seja ajustado com os dados fornecidos pelo usuário. Essa parte do documento ficou assim: TítuloNomeDoMédico
EndereçoMédico
EspecialidadeDoMédico
CEPMédico – CidadeMédico – UFMédico
NúmeroCRM
TelefoneMédico EmailMédico
231
O documento-receita, em si, envolve quatro blocos de variáveis. No primeiro bloco estão a data e o nome do paciente. O segundo reúne os medicamentos (ou exames) prescritos e as respectivas orientações (“Tomar uma colher de chá, de duas em duas horas”). No terceiro grupo, as variáveis dizem respeito à pessoa do médico ou dentista: seu nome, título (“Dr.”, “Dra.”), especialidade, matrícula no CRM/CRO etc. Há ainda um conjunto de variáveis relativas ao consultório do profissional: endereço, telefone, e-mail. As variáveis dos dois primeiros blocos devem ser preenchidas na hora de emitir a receita ou o pedido de exames. Já os dados do profissional e as informações do consultório correspondem a configurações fixas para cada médico ou dentista. Com base nessa análise, vamos construir o único formulário da aplicação, frmReceita.
O formulário frmReceita O formulário frmReceita contém os seguintes controles: Um objeto multipágina (Multipage1) com três orelhas: Receita, Dados Profissionais e Consultório (Page1 a Page3). A primeira contém os controles associados às variáveis dos blocos 1 e 2, que são: Caixa de texto Data (txtData) Caixa de texto Nome do Paciente (txtNome) Quatro caixas de texto Medicamento 1 a 4 (txtMedicamento1 a txtMedicamento4) Quatro caixas de texto Dose e Uso (txtDoseUso1 a txtDoseUso4).
F I G U R A 2 7 . 1 O programa em ação, destacando a orelha 232
principal (Receita)
Na orelha Page2 (Dados Profissionais) estão os seguintes objetos: Caixa de texto Nome do Médico ou Dentista (txtNomeMédico) Caixa de texto Número CRM/CRO (txtCRM) Caixa de texto CPF (txtCPF) Caixa de combinação Título (cboTítulo) Caixa de combinação Conselho Regional (cboConselho) Caixa de texto Especialidade (txtEspecialidade) Caixa de texto Pasta para Armazenamento das Receitas (txtPastaDoc) Caixa de verificação Não Armazenar as Receitas Emitidas (chkArmazenar) Botão de comando Procurar (cmdProcurar), que abre uma caixa de diálogo para definir um diretório para txtPastaDoc. Botão de comando Salvar (cmdSalvar), que salva num arquivo INI as configurações indicadas nos controles acima.
F I G U R A 2 7 . 2 Orelha 2: dados do médico ou do dentista, e
pasta-padrão para as receitas
A orelha Page3 (Consultório) reúne os controles ligados ao endereço do consultório: Caixa de texto Endereço (txtEndereço) Caixa de texto CEP (txtCEP) Caixa de texto Cidade (txtCidade) Caixa de texto Estado (txtEstado) Caixa de texto Telefone (txtTelefone) Caixa de texto Email ou Bip (txtEmail) Caixa de verificação Não Preencher o Cabeçalho (chkCabeçalho)
233
Diretamente no formulário – ou seja, não no controle multipágina – há ainda dois botões de comando, OK (cmdOK) e Cancelar (cmdCancelar). O primeiro executa a emissão da receita e o outro fecha o formulário, encerrando a aplicação. Afora o cabeçalho, o documento-modelo da receita contém apenas as seguintes informações: Para: Data: Na hora do preenchimento, as expressões que contêm os sinais < e > são substituídas pelos valores correspondentes. Mas o documento tem ainda, ocultos, oito indicadores (Medicamento1 a Medicamento4; e doseMedicamento1 a doseMedicamento4), que marcam as posições em que as informações correspondentes devem aparecer no texto. Esta solução também poderia ser concebida usando apenas o recurso da substituição de palavras-chave. O uso dos indicadores constitui um caminho alternativo. Vamos agora entender o mecanismo de funcionamento do aplicativo, começando pela sua rotina fundamental, a que está associada ao botão de comando OK. Quando o usuário clica nesse botão, o programa chama a função TestaPreenchimento, que verifica se estão preenchidos os itens fundamentais da receita. Quais são esses itens? Primeiro, a data; depois, o nome do paciente e, por fim, pelo menos um dos campos MedicamentoX. O programa também testa se as informações pessoais do médico ou do dentista estão disponíveis. Em caso negativo, o processo é interrompido.
F I G U R A 2 7 . 3 Os indicadores de Receita.dot 234
Com todos os dados no lugar, o aplicativo segue em frente e, primeiro, usa a técnica da substituição de palavra-chave para preencher o nome do paciente e a data da consulta. Em seguida, localiza cada um dos indicadores (bookmarks), posiciona o cursor nos pontos marcados por eles e lá insere a informação correspondente. Isso é feito mediante um loop que varia de 1 a nCaixas (=4), o número de caixas para medicamentos. For n = 1 To nCaixas If Controls(“txtMedicamento” & n) “” Then With Selection .GoTo What:=wdGoToBookmark, Name:="Medicamento" & n .Font.Bold = True .TypeText CStr(n) & “. ” & _ Controls(“txtMedicamento” & n).Text .Font.Bold = False .GoTo What:=wdGoToBookmark, _ Name:="DoseMedicamento" & n .TypeText Controls(“txtDoseUso” & n).Text End With End If Next n
Preenchida a receita propriamente dita, o programa recolhe as informações pessoais do médico ou do dentista e acrescenta-as ao final do documento, no qual o profissional deve assinar. Nesse caso, a técnica empregada vale-se do método TypeText do objeto Selection: With Selection For n = 1 To 4 ‘ quatro linhas em branco .TypeText vbCrLf Next n .Font.Italic = True .Font.Size = 10 .TypeText cboTítulo & “ ” & txtNomeMédico & vbCrLf .TypeText cboConselho & “: ” & txtCRM & vbCrLf .TypeText “CPF: ” & txtCPF .Font.Italic = False .Font.Size = 12 End With
As informações inseridas no documento pelas linhas de código acima estão todas na orelha Dados Profissionais do formulário (Figura 27.2). Muito bem. Já vimos o funcionamento básico da aplicação. Mas faltam ainda alguns pontos essenciais. Todos os dados exibidos na orelha Configurações ficam guardados num arquivo INI (Receita.ini). Como eles vão parar nesse 235
arquivo e como são recuperados de lá? Para personalizar suas receitas, o médico ou o dentista deve preencher esses campos de configuração. Após isso, ao clicar no botão Salvar Configurações, transfere todos os dados pessoais para o arquivo INI. Como um toque adicional de personalização, seu nome também passa a aparecer na barra de título do formulário. Assim: Receita – Dra. Ana Lúcia de Lima Para salvar as informações no arquivo INI, o aplicativo usa a propriedade PrivateProfileString, do objeto System. O mesmo recurso é utilizado para recuperar essas informações, durante a inicialização de Receita Médica. O programa lê os dados armazenados em Receita.ini e os exibe nas caixas de texto e caixas de combinação da orelha Configurações, quadro Dados Profissionais. Observe que as informações que aparecem em caixas de texto (nome do médico, CPF e número de matrícula no Conselho Regional) são armazenadas no arquivo, como texto normal. No entanto, o que se registra das caixas de combinação não é o texto do item, mas o índice correspondente a ele. Na caixa cboTítulo, por exemplo, a opção “Dr.” é a primeira da lista – e portanto tem o índice 0. Se “Dr.” é a opção escolhida, o número 0 vai para a chave Título, no arquivo INI. Ao ser recuperado, ele é aplicado à propriedade ListIndex da caixa de combinação: cboTítulo.ListIndex = CInt(strTítulo)
A função CInt é usada, aí, para converter em número inteiro a string lida no arquivo INI (strTítulo). Além dos dados profissionais do médico ou do dentista, a orelha 2 envolve itens que estão vinculados ao controle dos documentos-receitas. Esses itens estão dentro do quadro Documentos. O primeiro deles é a caixa de texto txtPastaDoc, na qual o usuário deve indicar um diretório-padrão para o armazenamento das receitas emitidas. O botão Procurar facilita essa tarefa. Ele abre uma caixa de diálogo do Word na qual o usuário escolhe o diretório desejado. O último item de configuração dessa orelha é representado pela caixa de verificação chkArmazenar, que tem por legenda “Não Armazenar as Receitas Emitidas”. Portanto, se essa caixa for ativada, as receitas não serão salvas. Ou melhor, todas elas serão salvas com o mesmo nome, Receita.doc, de modo que a receita mais nova sobrescreverá a anterior. Quando a opção Não Armazenar está selecionada, o rótulo labDocReceita passa a exibir Receita.doc como nome do próximo documento a ser emitido. O padrão, no entanto, é a emissão de documentos numerados. Para gravar todas as receitas, os nomes dos arquivos obedecem a uma numeração similar à adotada no projeto do Capítulo 14. Os documentos-receitas recebem nomes seqüenciais obedecendo ao formato Rxxxx-yy.doc, no qual 236 xxxx é um número que varia de 0001 a 9999; yy é o ano, escrito com dois dígi-
tos. No início de um novo ano, yy assume o valor correspondente e o número xxxx recomeça de zero. Esse esquema de denominação dos arquivos funciona graças ao registro do último número feito no arquivo Receita.ini. Não discutiremos os procedimentos aqui porque eles são idênticos aos usados no Capítulo 14. Aliás, eles serão discutidos também no Capítulo 31. O arquivo Receita.ini abriga ainda anotações referentes aos controles reunidos na terceira orelha de Multipage1 – as informações que servem para preencher o cabeçalho da receita. O médico ou o dentista deve preencher esses campos e clicar no botão Salvar. Observe que há dois botões Salvar, um na orelha 2 e o outro na orelha 3. Trata-se de uma redundância, de propósito. Pareceu-me que assim fica mais fácil para o usuário. Ele preenche os campos da orelha 2 e salva; depois, preenche a orelha 3 e salva outra vez. Internamente, de fato, os dois botões salvam as informações em separado.
F I G U R A 2 7 . 4 Orelha Consultório: informações para o
cabeçalho da receita
Depois de personalizado com as informações do médico e de seu consultório, e iniciado o processo de numeração dos arquivos, o conteúdo do arquivo Receita.ini assume o seguinte formato: [Usuário] CRM=00.000 CPF=000.000.000-00 Título=1 Nome=Ana Lúcia de Lima Conselho=0 Espec=Endocrinologista
237
[Arquivos] Ano=1999 RecNum=35 PastaDoc=c:\solucoes\receitas SalvarDocs=Sim [Endereço] Rua=Rua das Acácias, 318 cj. 42 CEP=04568-000 Cidade=São Paulo Estado=SP Telefone=(011) 2727-3344 E-mail=E-mail:
[email protected] Cabeçalho=Sim
Conforme você pode observar, as três seções de Receita.ini armazenam informações de áreas distintas. A seção Usuário junta os dados sobre o médico ou o dentista. A seção Arquivos ocupa-se de controlar as opções associadas ao armazenamento e à numeração dos documentos. Por fim, o bloco Endereço guarda os dados do consultório e a alternativa de preencher ou não o cabeçalho da receita. Preencher ou não preencher, eis a questão. Se você tem um cabeçalho próprio, mais sofisticado que o modelo simplório usado neste projeto, prefira o seu. Então, escolha a opção Não Preencher o Cabeçalho da Receita. O programa não perderá tempo tentando substituir palavras-chave que não irá encontrar. No entanto, se você não tem um cabeçalho e quer começar, desde já, a usar uma receita personalizada, informe seus dados pessoais/ profissionais, e utilize sem medo o cabeçalho de Receita.dot. O preenchimento do cabeçalho e do rodapé traz um aspecto inédito. Até agora, sempre trabalhamos com o corpo do documento. Para editar essas regiões que ficam em segundo plano, é necessário torná-las acessíveis. O acesso ao cabeçalho é dado pelas linhas: ActiveDocument.ActiveWindow.View.Type = wdPrintView ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
Na verdade, somente a segunda linha é que coloca o cabeçalho (PageHeader) em primeiro plano. A linha anterior coloca a página no modo Layout de Impressão (wdPrintView), um pré-requisito para se ter acesso ao cabeçalho ou rodapé. Se o documento não estiver nesse modo de visualização, a segunda linha de código, acima, provoca um erro. De modo similar, abre-se o rodapé com o mesmo comando, apenas trocando o valor da propriedade para wdSeekCurrentPageFooter. E, para retornar ao corpo do documento, wdSeekMainDocument. Para concluir o projeto, falta apenas lembrar que, no módulo ThisDocument do modelo Receita.dot, você deve incluir a seguinte rotina: 238
Private Sub Document_New() frmReceita.Show End Sub
Como toda rotina Document_New, ela garante a abertura do formulário frmReceita sempre que um novo documento é criado com base no modelo Receita.dot. No final, a receita produzida tem o aspecto mostrado na Figura 27.5.
F I G U R A 2 7 . 5 A receita pronta: cabeçalho e rodapé
preenchidos e personalizados
Para ir mais além 1. Preste atenção para duas propriedades das caixas de texto DoseUso, na
orelha Receita. Como o conteúdo dessas caixas pode não caber em uma única linha, elas têm a propriedade MultiLine definida para True. Isso
239
garante a introdução de uma quebra de linha automática no final do controle, evitando que o texto prossiga na mesma linha, indefinidamente. A outra propriedade é ScrollBars, ajustada para 2 – fmScrollBarsVertical. Com isso, se o texto não couber na altura do controle, surgirá, automaticamente, uma barra vertical para facilitar ao usuário o acesso a qualquer parte do texto.
2. Veja as caixas de verificação Não Armazenar as Receitas Emitidas e Não Preencher o Cabeçalho da Receita. Ambas definem uma situação pelo lado negativo. No arquivo INI, ao contrário, elas são representadas por chaves afirmativas: SalvarDocs=Sim Cabeçalho=Sim
Portanto, ao gravar a informação marcada nesses controles e também ao recuperá-las no arquivo de inicialização, é preciso inverter o sinal da informação. Se houver “Sim” no arquivo INI, o valor da caixa de verificação deve ser falso. E, inversamente, se a caixa está marcada (valor igual a True), registre-se “Não” no arquivo Receita.ini.
3. Pense num sistema mais completo de automação para médicos. Além de
emitir receitas, esse sistema poderia cadastrar pacientes e armazenar informações sobre eles – as prescrições da receita e outras anotações – num banco de dados. Como todo projeto mais arrojado, dá trabalho, mas é perfeitamente possível fazê-lo sem sair do Word. Ou, se for o caso – por que não? –, usar também recursos de outros aplicativos do Office, como o Excel e o Access. Tente. Com certeza, você não vai precisar de informações mais complexas do que as mostradas neste livro.
4. O modelo de documento Receita.dot foi desenhado de modo a aproveitar ao máximo as bordas do papel. Isso leva a variações de comportamento de uma impressora para outra. Se, por acaso, em sua impressora a tabela que contém os bilhetes ficar fora da área de impressão, ajuste-a, convenientemente, com pequenos retoques, a fim de que toda ela caiba, justa, numa página A4.
240
Construa bibliotecas públicas Neste projeto, conforme você viu, rotinas inteiras foram reaproveitadas de outra aplicação. Depois que você começa a desenvolver certa experiência em programação, nota que uma série de procedimentos tendem a se repetir. Isso ocorre, especialmente, com atividades “burocráticas”, como exibir caixas de diálogo do Word e capturar as definições do usuário, verificar se um diretório existe ou formatar um determinado tipo de string. Neste livro, fazia sentido copiar rotinas de um projeto para o outro porque a intenção era tratar cada projeto como unidade isolada e auto-suficiente. Mas em seu trabalho é possível acabar com a necessidade de copiar rotinas de uma aplicação para outra. Uma boa solução é criar uma biblioteca. O que é isso? Crie um módulo no modelo Normal.dot que contenha todos os procedimentos repetitivos. Esses procedimentos – subs e functions – devem ter abrangência global, para estar disponíveis a qualquer momento para todos os outros projetos. Portanto, cada sub e cada função deve ser iniciada com a instrução Public. Melhor ainda será se você construir duas bibliotecas públicas: uma para procedimentos específicos do Word e outra para procedimentos válidos em qualquer outra aplicação Visual Basic. A primeira coleção de rotinas ficará exclusiva para o Word. A outra poderá ser exportada e reaproveitada no Excel, no Access ou em qualquer outro aplicativo que suporte o VBA.
241
28 Epa! Word com banco de dados? Use o processador de texto para gerenciar arquivos do Access Ficha do projeto Projeto: Bancos de Dados no Word O que faz: Abre um banco de dados num formulário VBA e executa todas as operações básicas como salvar, alterar e apagar registros. Arquivos e requisitos do projeto: Formulários frmBancoDeDados e frmBancoTrat e respectivos códigos internos. Arquivo de banco de dados Clientes.mdb. Conhecimento técnico: Trabalho com bancos de dados Access, usando os Data Access Objects, DAO. Conceitos básicos de bancos de dados relacionais. Nível de programação: 242
Avançado
Para quem só navega em águas rasas, o Word é para escrever texto, o Excel para calcular, e o Access para gerenciar bancos de dados. Claro, isso não está errado, mas representa apenas uma parte da história. E se eu disser que o Word também serve para gerenciar bancos de dados? Não se espante: é a pura verdade – e vamos demonstrá-la neste capítulo. Nosso objetivo, aqui, é criar um aplicativo que abra um arquivo Access e ofereça ao usuário a oportunidade de executar todas as operações típicas de um banco de dados, como adicionar, editar e remover registros. Para essa tarefa, precisamos, em primeiro lugar, de um banco de dados Access. Vamos usar o arquivo Clientes.mdb, criado com o Access versão 97. Com isso, pretende-se garantir o uso do aplicativo tanto no Word 97 como no 2000, já que as duas encarnações do programa trabalham com versões diferentes das bibliotecas de acesso a dados do VBA, ou DAO (Data Access Objects). O Word 97 utiliza o DAO 3.5 e o 2000, o DAO 3.6. Essas bibliotecas reúnem todas as ferramentas de manipulação de dados do Access, disponíveis a todos os produtos que suportam o VBA, via programação.
Atenção
É possível que você receba uma mensagem de erro ao tentar abrir um programa que envolve o uso das bibliotecas DAO de acesso a bancos de dados. No ambiente do VBA, acione Ferramentas/Referências e marque a caixa Microsoft DAO 3.6 Object Library, se estiver no Word 2000. No 97, marque Microsoft DAO 3.5 Object Library.
O banco de dados Clientes.mdb tem apenas duas tabelas, Clientes e Tratamento, cuja estrutura é mostrada no quadro a seguir. Os campos Pronome, na tabela Tratamento, e Tratamento, na tabela Clientes, estão relacionados, sendo que a tabela Tratamento funciona como fonte primária de informações. O relacionamento entre esses campos é do tipo “um-para-muitos” – ou seja, um registro exclusivo na tabela Tratamento deve corresponder a um número indeterminado de registros na tabela Clientes. Exemplo: o pronome “Sr.” é uma das opções na tabela primária. Ele se repete em todos os registros nos quais o usuário resolva tratar o cadastrado como “Sr.”. Mas só pode existir essa opção uma única vez na tabela Tratamento. O relacionamento entre as tabelas Cliente e Tratamento apresenta ainda outra característica: reforça a chamada integridade referencial. Como a tabela Tratamento é a fonte primária, só pode ser incluído no campo Tratamento, em Clientes, um valor previamente cadastrado na tabela primária. Para visualizar as informações do banco de dados, precisamos criar um formulário, que vai receber o nome frmBancoDeDados. Nele, vamos traçar sete caixas de texto, as quais exibirão informações de diferentes campos da tabela Clientes. Os nomes dessas caixas acompanham os nomes dos campos: txtNomeCliente, cboTratamento, txtEndereço, txtCEP, txtCidade, txtEstado e txtEmail. O campo Tratamento, como vem de outra tabela, é representado por uma caixa de combinação, na qual o usuário deve escolher uma das opções disponíveis: Sr., Sra., Srta., Dr., Prof. etc. 243
F I G U R A 2 8 . 1 Relacionamento entre as tabelas
TABELA
CAMPO
TAMANHO DO CAMPO (caracteres)
TIPO DE DADOS
Clientes
Código
–
Inteiro Longo
NomeCliente
50
Texto
Tratamento
10
Texto
Empresa
50
Texto
Endereço
50
Texto
CEP
9
Texto
Cidade
50
Texto
Estado
2
Texto
Email
50
Texto
Telefone
25
Texto
Fax
25
Texto
ValorPago
—
Moeda
Tratamento2
20
Texto
Pronome
20
Texto
Tratamento
244
FIGURA 28.2
O formulário frmBancoDeDados: controle de clientes
Além desses oito controles de dados – sete caixas de texto e uma caixa de combinação –, precisamos agregar ao formulário controles para administrar e visualizar as informações. Vamos adicionar nove botões de comando e uma etiqueta, ou rótulo. Os botões são divididos em três grupos. No primeiro estão os comandos manipuladores de registros: Novo, Salvar e Apagar (cmdNovo, cmdSalvar e cmdApagar). Em seguida, vêm os botões de navegação do banco de dados: Primeiro (cmdFirst), Último (cmdLast), Próximo (cmdNext) e Anterior (cmdPrevious). Por fim, os botões Fechar (cmdFechar), que encerra o aplicativo, e o pequeno cmdTratamento, sem legenda, localizado acima da caixa de combinação cboTratamento. Este último dá ao usuário a possibilidade de cadastrar, no banco de dados, novas formas de tratamento. Duas observações. A primeira: o nome dos botões de navegação, em inglês, faz referência aos comandos de linguagem que você deverá usar em cada caso. A outra: os nomes Primeiro, Último etc. não estão escritos na face dos botões de navegação. Em lugar deles, há os já tradicionais >, como nos aparelhos de videocassete e gravadores de áudio. Se, em lugar dos sinais de maior e menor, você quiser algo mais sofisticado, use a fonte WingDings 3 (disponível, acho, apenas no Windows 98). Com ela, escreva t minúsculo para a seta à esquerda e u minúsculo para a seta à direita. O resultado é mostrado na próxima figura. O único cuidado a observar é que nem todo usuário possui a fonte WingDings 3 em sua máquina. Muito bem. Só não falamos do rótulo. Situado entre os botões de navegação, o rótulo labNumReg exibe o número do registro atual e o total de registros, no formato X de Y. Com isso, o formulário está pronto. Agora, falta o principal: a lógica dentro dele, que o faz funcionar. 245
FIGURA 28.3
Botões de navegação com setas na fonte WingDings 3
O código por trás do formulário Vamos acompanhar o código, passo a passo, a partir do momento em que o formulário frmBancoDeDados é ativado. A primeira rotina que entra em ação é UserForm_Initialize. Nela, abre-se o banco de dados, trabalha-se com informações das duas tabelas e, por fim, exibe-se o primeiro registro ao usuário. A primeira tarefa é abrir o banco de dados. Depois, criam-se dois dynasets (conjuntos dinâmicos de dados): rs, baseado na tabela Clientes e organizado em ordem alfabética, segundo o campo NomeCliente; e rsTrat, que traz as informações da tabela Tratamento. ‘ Abre o banco de dados strSQL = “Select * from Clientes Order By NomeCliente” strSQLTrat = “Select * from Tratamento Order By Pronome” strDirMDB = “c:\solucoes” Set db = OpenDatabase(strDirMDB & “\clientes.mdb”) Set rs = db.OpenRecordset(strSQL, dbOpenDynaset) Set rsTrat = db.OpenRecordset(strSQLTrat, dbOpenDynaset)
Está concluída a leitura inicial no banco de dados. Agora, é preciso mostrar ao usuário o resultado dessa leitura. Primeiro, vamos ao último registro porque, como rs é um dynaset, só assim é possível apurar o total de registros (lngNum) correspondente à propriedade RecordCount: ‘ Captura o total de registros rs.MoveLast lngNum = rs.RecordCount rs.MoveFirst
Em seguida, percorre-se o conjunto de dados rsTrat, da tabela Tratamento, para preencher a caixa de combinação cboTratamento: ‘ Preenche cboTratamento Do While Not rsTrat.EOF cboTratamento.AddItem rsTrat!Pronome rsTrat.MoveNext Loop MostraRegistro 246
Por fim, chama-se a rotina MostraRegistro, que se encarrega de exibir, nos controles correspondentes, o registro atual – no caso, o primeiro registro do conjunto rs. MostraRegistro coloca, em cada campo do formulário, o valor correspondente no banco de dados: ‘ Atualiza o valor dos campos de dados txtNomeCliente = rs!NomeCliente cboTratamento = rs!Tratamento txtEmpresa = rs!Empresa txtEndereço = rs!Endereço txtCEP = rs!CEP txtCidade = rs!Cidade txtEstado = rs!Estado txtEmail = rs!Email ‘ Exibe a posição labNumReg = rs.AbsolutePosition + 1 & “ de ” & lngNum
Na última linha, a rotina define o valor do rótulo labNumReg, que indica o número do registro atual e o total de registros do conjunto. O banco de dados foi aberto e seu primeiro registro está visível na tela. Passemos agora para as diversas operações. Se você clicar no botão Próximo (>), o que acontece? Basicamente, a rotina cmdNext_Click vai mover o ponteiro do banco de dados para o próximo registro (rs.MoveNext) e chamar a rotina MostraRegistro para exibir, nos controles do formulário, as informações do banco de dados agora ativas. Em cada movimento de navegação, esses dois comandos serão necessários. Primeiro, o método correspondente ao botão. Depois, MostraRegistro. Mas há também certas precauções. No caso do botão Próximo, não se pode deixar que o ponteiro do conjunto de dados caia no vazio. Esclareçamos: ao navegar num conjunto de dados, o ponteiro em cada momento encontra-se num registro. Quando ele atinge o último registro e o usuário pede para que siga em frente (botão Próximo), o que acontece? Ele cai naquilo que chamamos de vazio – uma área que não corresponde a nenhum registro. O mesmo acontece, em sentido inverso, quando o ponteiro está no primeiro registro e o usuário aciona o botão Anterior. Essas áreas vazias são propriedades do conjunto de dados: EOF (sigla de End of File, ou fim do arquivo) e BOF (de Beginning of File, ou início do arquivo). Na movimentação pelo bancos de dados, deve-se evitar que o ponteiro caia no BOF ou no EOF. A partir desses pontos, qualquer tentativa de deslocamento produz um erro. Por causa disso, o código da rotina associada ao clique no botão Próximo fica assim: If rs.AbsolutePosition + 1 = lngNum Then Exit Sub If Not rs.EOF Then rs.MoveNext
247
MostraRegistro blnNovoReg = False End If
A primeira linha testa se o próximo passo é o fim do arquivo. Em caso positivo, cancela-se a movimentação com a instrução Exit Sub. Aqui, o teste se faz com a ajuda da propriedade AbsolutePosition, que indica a posição do ponteiro, contando os registros a partir de zero. Em seguida, move-se o ponteiro para o próximo registro, o qual é exibido no formulário. Na última linha, indica-se que não se trata de um registro novo, tornando o indicador blnNovoReg igual a False. Mais adiante você vai entender a função desse indicador. Para exibir o registro Anterior, o procedimento praticamente se repete: If rs.AbsolutePosition = 0 Then Exit Sub If Not rs.BOF Then rs.MovePrevious MostraRegistro blnNovoReg = False End If
Observe, no trecho de código acima, que agora trabalhamos com a propriedade BOF, o início do arquivo. No mais, tudo é igual. Para os botões Último e Primeiro, as coisas são mais simples. Vejamos o Último: rs.MoveLast MostraRegistro blnNovoReg = False
Como não há nenhum perigo de cair no vazio na ida ao último registro, executa-se o comando sem ressalvas. O mesmo acontece em relação ao primeiro (rs.MoveFirst). Vejamos, então, as operações de modificação do registro, indicadas pelos botões Novo, Salvar e Apagar. Botão Novo. A tarefa fundamental cumprida por esse botão é limpar a área para a digitação de um novo registro. Ao mesmo tempo, é preciso indicar que estamos trabalhando com um registro zero km, e não um velho registro modificado. Isso será importante na hora da operação Salvar. Então, o código fica assim: blnNovoReg = True LimpaRegistro labNumReg = “Novo”
248
Na primeira linha, indica-se que o registro é novo. Na última, exibe-se a palavra Novo no rótulo que indica o número do registro atual. Na linha do
meio, chama-se a sub LimpaRegistro, que apaga todo o conteúdo dos controles no formulário. LimpaRegistro tem o seguinte código: Dim ctl As Control For Each ctl In Me.Controls If Left$(ctl.Name, 3) = “txt” Then ctl = “” End If Next ctl cboTratamento = “” txtNomeCliente.SetFocus
LimpaRegistro reduz a branco o conteúdo de todos os controles do formulário e põe o foco da ação no primeiro controle: txtNomeCliente. Só para você não achar estranho o código em LimpaRegistro, o que está escrito acima corresponde ao seguinte: txtNomeCliente = “” cboTratamento = “” txtEmpresa = “” txtEndereço = “” txtCEP = “” txtCidade = “” txtEstado = “” txtEmail = “” txtNomeCliente.SetFocus
Botão Apagar. Ao clicar nesse botão, o usuário entra numa área perigosa. Será que ele clicou com plena consciência, ou o fez inadvertidamente? Na dúvida, você precisa dar a ele a chance de corrigir um eventual descuido. Por isso, a primeira tarefa, nesse caso, é perguntar ao usuário se ele de fato deseja apagar o registro. s = “Tem certeza de que deseja apagar este registro?” i = MsgBox(s, vbYesNo + vbDefaultButton2 + vbQuestion, “Apagar Registro”) If i = vbNo Then GoTo cmdApagar_Fim
Aqui, mais um parêntese. Ao apresentar a caixa de mensagem com a pergunta ao usuário, preste atenção para alguns detalhes. A mensagem oferece dois botões para resposta: Sim e Não. Como se trata de uma operação perigosa (apagar um registro), coloque o foco no botão que não oferece risco (Não). Assim, se o usuário o acionar sem querer, nada acontecerá. Isso é garantido com a constante vbDefaultButton2. Outro detalhe: para apresentar o ícone com o sinal de interrogação, adicione a constante vbQuestion. Fecha o parêntese.
249
Se o usuário responde Sim, então elimina-se o registro. Para isso, primeiro faz-se o teste: será que o ponteiro não está no vazio (BOF ou EOF)? Se não estiver, apaga-se o registro (rs.Delete), move-se o ponteiro para o próximo registro e subtrai-se de 1 o número total de registros. Em seguida, só para evitar problemas, faz-se o teste para ver se o ponteiro não caiu no fim do arquivo. Se caiu, ele é reposicionado para o último registro. Tudo isso é providenciado pelo código seguinte: If Not (rs.BOF Or rs.EOF) Then rs.Delete cmdNext_Click ‘próximo lngNum = lngNum - 1 End If If rs.EOF Then rs.MoveLast
Botão Salvar. Este é o mais complicado dos três comandos de modificação do registro. A tarefa 1, quando o usuário clica nesse botão, consiste em determinar se ele deseja gravar um registro novo ou um registro modificado. O registro novo é sinalizado pelo indicador blnNovoReg, de que já falamos antes. Como vimos, um clique no botão Novo atribui o valor True a esse flag. Somente depois que a adição de um novo registro é confirmada (com a operação Salvar) ou abortada (com o deslocamento para um registro qualquer) é que blnNovoReg assume o valor False. Há diferenças de tratamento se o registro a salvar no banco de dados é novo ou apenas alterado. No primeiro caso, é preciso usar o comando rs.New, que prepara o recordset para receber um novo registro. No outro, emite-se o comando rs.Edit, avisando ao banco de dados que o registro, já existente, sofrerá alterações. ‘ Adiciona ou edita registro If blnNovoReg Then rs.AddNew Else rs.Edit End If
Seja o registro novo ou antigo, é preciso definir, item a item, o que deve ser gravado, atribuindo, a cada campo de dados, a informação presente no correspondente controle do fomulário:
250
‘ Define/redefine valores dos campos rs!NomeCliente = txtNomeCliente rs!Tratamento = cboTratamento rs!Empresa = txtEmpresa
rs!Endereço = txtEndereço rs!CEP = txtCEP rs!Cidade = txtCidade rs!Estado = txtEstado rs!Email = txtEmail
Após tudo isso, o registro é salvo: rs.Update
Por fim, chegamos a uma porção de código específica para registros novos. Lembre-se de que o conjunto de dados exibe os registros em ordem alfabética segundo o campo NomeCliente. Quando se inclui um novo registro, é necessário manter essa regra. Para isso, usa-se um estratagema simples, contido nas linhas a seguir: If blnNovoReg Then blnNovoReg = False rs.MoveLast lngID = rs!Código rs.Close Set rs = db.OpenRecordset(strSQL, dbOpenDynaset) rs.MoveLast lngNum = rs.RecordCount rs.FindFirst “Código=” & lngID MostraRegistro End If
Primeiro, desliga-se o flag blnNovoReg: se já foi salvo, o registro não é mais novo. Depois, move-se o ponteiro até o fim do recordset, a fim de armazenar, na variável lngID, o número de código do último registro – aquele que acabou de ser gravado. Então, o recordset rs é fechado e aberto outra vez. Obtém-se o número total de registros (lngNum) e exibe-se, exatamente, o registro que acabou de ser gravado. Essas operações transcorrem tão rapidamente que o usuário nem percebe. Como resultado, o registro recém-adicionado passa a ser exibido com um número – 17 de 95, por exemplo. O 17 corresponde à posição do novo registro, respeitada a ordem alfabética que define o conjunto de dados. O último item a mencionar está ligado ao pequeno botão Tratamento. Quando se clica nele, abre-se o formulário frmBancoTrat, que é o segundo do projeto. Esse formulário tem botões de navegação, botões de edição e apenas um campo de informação: txtPronome. A lógica de navegação é semelhante à já discutida para o formulário principal. Os botões de edição (Novo, Salvar e Apagar) fazem as tarefas esperadas.
251
Esse formulário trabalha com um reduzido conjunto de dados: a tabela Tratamento, que tem apenas um campo, Pronome, e nele as formas de tratamento cadastradas. A operação mais simples, no caso, é adicionar um novo registro. Até aí, tudo bem. No entanto, se você resolver apagar um registro existente ou modificá-lo, entram em cena alguns senões.
F I G U R A 2 8 . 4 O segundo formulário: formas de tratamento
Como sabemos, as duas tabelas do banco de dados Clientes.mdb mantêm um relacionamento entre si tendo como eixos os campos Tratamento, na tabela Clientes, e Pronome, na tabela Tratamento. Além do mais, esse relacionamento é marcado pela opção Manter Integridade Referencial”, oferecida pelo Access. Se, por exemplo, o pronome “Sr.”, assim escrito, já foi usado em algum registro, o banco de dados não vai aceitar que você o modifique para “Senhor”. A própria inteligência embutida no arquivo MDB vai dizer que já existem registros na tabela Clientes utilizando a informação que você deseja alterar. Da mesma forma, não será possível apagar um registro de Tratamento já em uso na outra tabela. Em ambos os casos, o programa apresentará o seguinte aviso de erro:
FIGURA 28.5
Aviso para manter a lógica do relacionamento entre tabelas
Depois que você faz as alterações permitidas na tabela Tratamento, elas precisam ser refletidas na lista da caixa de combinação cboTratamento, no formulário principal. Afinal, o conteúdo dessa lista não é outra coisa senão a própria tabela. Mas como executar essa atualização se existe a fronteira entre os dois formulários? Precisamos, aqui, lançar mão de uma leve artimanha. Observe: no formulário frmBancoDeDados existe uma rotina chamada PreencheRsTrat. Ela varre a tabela Tratamento e transfere seu conteúdo para a 252
lista da caixa cboTratamento. PreencheRsTrat é acionada pela primeira vez no evento de inicialização do formulário. O segredo dessa rotina está no fato de ela ser declarada como Public. Assim, pode ser chamada a partir de outro módulo qualquer do projeto – o que resolve nosso impasse. Após fazer as modificações no formulário auxiliar, você o fecha. Nesse momento, antes que o form seja eliminado da memória, entra em ação o evento Terminate. Na rotina associada a esse evento, chamamos PreencheRsTrat no formulário principal. Para isso indicamos o nome do módulo e o nome da rotina: frmBancoDeDados.PreencheRsTrat
Pronto. A caixa de combinação Tratamento é esvaziada e repreenchida com o conteúdo atualizado da tabela Tratamento. Com essas rotinas, sua aplicação está pronta para adicionar, remover ou modificar registros no banco de dados. E fica provado – pelo menos com um exemplo – que o Word não serve apenas para escrever texto.
Para ir mais além 1. Você deve ter observado que o projeto não inclui a detecção de mudan-
ça no registro atual. Quer dizer, se um registro estiver na tela e você o modificar, sem salvar, perderá todas as alterações ao passar para outro registro. Com um detector de mudança, quando você tentasse mudar de registro, o programa perguntaria: deseja salvar as modificações? Você pode incluir um detector no projeto. Basta criar uma variável booleana válida para todo o formulário (por exemplo, blnRegistroMudou) e dar-lhe valor verdadeiro se houver qualquer mudança num dos controles. A mudança é indicada pelo evento Change. Por exemplo, para a caixa txtNomeCliente: Private Sub txtNomeCliente_Change() blnRegistroMudou = True End Sub
Então, nos eventos associados aos botões de navegação, você verifica o valor de blnRegistroMudou e, se for o caso, pergunta ao usuário se quer salvar as modificações. Se a resposta for positiva, chama-se a rotina cmdSalvar_Click.
2. O gerenciamento de banco de dados oferecido por este projeto é bem
simples. Não inclui, por exemplo, a possibilidade de listar registros ou imprimir relatórios. Mas tudo isso é possível, mediante a criação de documentos do Word a partir das informações do banco de dados, conforme é mostrado no capítulo dedicado ao Assistente de Banco de Dados ou no capítulo sobre os conversores de arquivos do Word. Tente fazer uma dessas adaptações. 253
Integridade referencial?!! As tabelas Tratamento e Clientes, no projeto atual, estão relacionadas de forma que a primeira fornece dados primários à segunda. Para ter um exemplo prático do que isso significa, abra o aplicativo, digite uma palavra qualquer no campo Tratamento e tente salvar o registro. O programa vai apresentar a seguinte mensagem de erro: “Não foi possível adicionar ou alterar registros, pois um registro relacionado é exigido na tabela ‘Tratamento’.” Em palavras mais simples, isso significa que, como as tabelas estão relacionadas, você não pode incluir no campo Tratamento da tabela Clientes um item que não esteja previamente cadastrado na tabela Tratamento. Essa reação do banco de dados tem o objetivo de manter a chamada integridade referencial. Em respeito a essa lógica, também não se aceita a eliminação da tabela Tratamento de um registro já incluído em Clientes. Isso é pura lógica.Veja um exemplo real. Num aplicativo para gerenciamento de pessoal, admita que você tem duas tabelas: uma chamada Funcionários, que reúne os dados de cada empregado, e a outra, Seções, com a lista dos departamentos da empresa. Você já percebeu: a tabela primária, aqui, é Seções. Portanto, não é possível incluir na tabela Funcionários uma área não cadastrada em Seções. Seria o mesmo que dizer: “este funcionário trabalha em nossa empresa numa área que não existe!” Se você pensar em outras situações, poderá encontrar dívidas sem devedores, vendas sem compradores e objetos (mercadorias, documentos, valores) que se movimentam, mas não se sabe nem de onde, nem para onde. É esse tipo de absurdo que a manutenção da integridade referencial procura evitar.
254
29 Os dados estão na planilha No Word, use os métodos do Access para gerenciar arquivos do Excel Ficha do projeto Projeto: Planilha como banco de dados O que faz: Abre uma planilha Excel e trabalha com ela como se fosse um banco de dados, empregando recursos de gerenciamento do Access. Arquivos e requisitos do projeto: Formulário frmExcel e código interno. Arquivo do Excel PlClient.xls. Conhecimento técnico: Utilização dos métodos do Data Access Objects, DAO, para planilhas Excel. Nível de programação: Avançado 255
No capítulo anterior, vimos como um banco de dados do Access pode ser controlado a partir do Word. Agora, na mesma linha de raciocínio, vamos mostrar como trabalhar com uma planilha do Excel, tratando-a como um banco de dados. Você vai ver que os procedimentos são bastante semelhantes aos utilizados para o arquivo do Access. O ponto de partida, neste caso, é o arquivo Plclient.xls. Para manter o paralelismo com a solução anterior, esse arquivo contém apenas uma folha de dados (Plan1), a qual exibe as mesmas informações da tabela Clientes no banco de dados Clientes.mdb. Portanto, nossa intenção é construir um projeto idêntico ao anterior, tendo essa planilha como base de dados.
F I G U R A 2 9 . 1 A planilha Excel que sevirá como banco de dados
para o projeto
Como a planilha já está pronta, o primeiro passo é construir, no VBA do Word, um formulário para manipulação dos dados. Vamos chamá-lo de frmExcel. Sua estrutura é completamente idêntica à do formulário criado no projeto anterior. Há apenas uma diferença: em lugar de uma caixa de combinação com as opções de tratamentos, temos aqui uma caixa de texto comum. Vejamos o que acontece com a primeira rotina que entra em ação (UserForm_Initialize) quando se ativa o programa. A tarefa básica consiste em abrir a planilha, verificar o número de registros existentes e exibir o primeiro deles. No que se refere aos procedimentos de bancos de dados, tudo é semelhante ao já visto no projeto anterior. Mas há alguns macetes relativos à abertura da planilha. O arquivo XLS funciona como o banco de dados e a folha de cálculo (quer dizer, a planilha) faz o papel de uma tabela. O nome da planilha deve receber um cifrão no final. Eis porque a variável strFolhaPlanilha é mostrada abaixo como “Plan1$”, quando o nome da folha de cálculo é apenas Plan1.
256
strArquivoPlanilha = “c:\solucoes\plclient.xls” strFolhaPlanilha = “Plan1$”
F I G U R A 2 9 . 2 O formulário frmExcel: planilha como
banco de dados
Set db = OpenDatabase(strArquivoPlanilha, _ False, False, “Excel 8.0") Set rs = db.OpenRecordset(strFolhaPlanilha)
Simplificadamente, o método OpenDatabase obedece à seguinte sintaxe: OpenDatabase (nomedobancodedados, exclusivo, somenteleitura, conexão) Veja a seguir o que significa cada um dos parâmetros exigidos na linha acima: PARÂMETRO
OBSERVAÇÃO
Nomedobancodedados
Caminho completo do arquivo de banco de dados.
Exclusivo
– True, abre o banco de dados em modo exclusivo. – False, abre o banco de dado sem modo compartilhado.
Somenteleitura
– True, abre o arquivo somente para leitura. – False, abre o arquivo para leitura e gravação.
Conexão
Uma string que indica o tipo de banco de dados, a senha de acesso e outras informações.
No parâmetro conexão, usaremos aqui apenas o tipo de banco de dados: “Excel 8.0”, válido para planilhas das versões 97 e 2000. Para abrir uma planilha do Excel 95, por exemplo, você deve usar a string “Excel 7.0”. Ao contrário do que ocorre com os bancos de dados Access, não existe o perigo da movimentação indevida do ponteiro de registros para fora da área de trabalho. Portanto, não há com que se preocupar nas rotinas dos botões de na- 257
vegação. Ao atingir o último registro, o programa simplesmente se recusa a seguir em frente. O mesmo ocorre com o primeiro registro: nada ocorre se você clicar no botão Anterior. Um detalhe: quando você clicar no botão Apagar, vai receber uma mensagem, indicando que não é possível eliminar registros em bases de dados do Excel. De fato, ao acessar pasta de trabalho com o DAO, não há um método para apagar registros. Mas você pode acrescentar e editar todas as informações.
Para ir mais além 1. Neste projeto também é possível usar uma caixa de combinação para
apresentar uma lista de pronomes de tratamento disponíveis. Basta fazer um paralelo com o banco de dados Access, criando nova folha de dados para conter a lista de pronomes. Na verdade, o arquivo plclient.xls contém duas folhas de cálculo. Uma, Plan1, contém a tabela de clientes. A outra, Plan2, exibe uma tabela com uma única coluna (Pronome), a qual contém a lista de tratamentos disponíveis. Então, desenhe, numa área livre do formulário, uma caixa de combinação – por exemplo, cboTratamento. Em seguida, vá à rotina UserForm_Initialize e acrescente as seguintes linhas junto às outras declarações de variáveis: Dim rsTrat As Recordset
Agora, depois da linha que define o conjunto de dados rs (Set rs =...), escreva: Set rsTrat = db.OpenRecordset(“Plan2$”) Do While Not rsTrat.EOF
258
F I G U R A 2 9 . 3 A tabela Plan2 no Excel
cboTratamento.AddItem rsTrat!Pronome rsTrat.MoveNext Loop
Execute a aplicação e verifique o conteúdo da caixa de combinação. Os pronomes estão lá! Sim, eles vieram da folha Plan2 da pasta de dados do Excel. Para que a caixa cboTratamento exiba as informações do banco de dados, registro a registro, será necessário aplicar a ela todos os métodos antes aplicados à caixa de texto txtTratamento.
2. No Access, a manutenção da integridade referencial é um item que faz
parte da própria estrutura do banco de dados. Vimos isso no capítulo anterior. Neste projeto, se você quiser garantir que somente pronomes já cadastrados possam ser incluídos na planilha, terá de cuidar pessoalmente disso. Uma saída é, por exemplo, ajustar as propriedades da caixa de combinação para que aceite somente itens constantes da lista.
Detalhes na caixa de mensagem Ao apresentar uma caixa de mensagem com pergunta ao usuário, preste atenção nos detalhes. Eles fazem uma enorme diferença nesse caso. Veja o seguinte trecho de código: s = “Tem certeza de que deseja apagar este registro?” i = MsgBox(s, vbYesNo + vbDefaultButton2 + vbQuestion, _ “Apagar Registro”) If i = vbNo Then GoTo cmdApagar_Fim
A constante vbYesNo indica que a caixa de mensagem exibirá dois botões de comando: Sim e Não. Outra constante, vbQuestion, garante a exibição de um ícone com o sinal de interrogação. É mais uma informação ao usuário, mostrando que ele precisa dar uma resposta. E esse valor vbDefaultButton2, para que serve? Essa constante coloca o foco da ação no botão 2 (Não). Num caso como o do exemplo, em que a resposta Sim deflagra a destruição de um item, o botão-padrão deve ser sempre o que não causa nenhum problema. Se, por acaso, o usuário esbarrar na tecla Enter, sem querer, nada acontecerá.
F I G U R A 2 9 . 4 A mensagem: foco no botão Não
259
30 Do banco de dados para o documento Monte um controle imobiliário com o Word e recursos de bancos de dados Ficha do projeto Projeto: Controle de Aluguéis O que faz: Cadastra e gerencia clientes, emitindo relatórios simples e recibos de aluguel personalizados para uma pequena imobiliária ou para pessoa física que gerencia locações de imóveis. Arquivos e requisitos do projeto: Formulário frmDocBDados e código interno. Arquivo Clientes.mdb, com o banco de dados Access. Modelo para recibos de aluguel Reciboal.dot. Biblioteca para geração de valores por extenso em reais, Extens32.dll. Conhecimento técnico: Trabalho com bancos de dados Access, usando os Data Access Objects – DAO. Preenchimento automático de um modelo de documentos. Geração de relatórios, a partir de um banco de dados. Nível de programação: 260
Avançado
Nos dois capítulos anteriores, você viu como o Word tem condições de trabalhar com arquivos do Access e do Excel, gerenciando-os com métodos de bancos de dados. Agora vamos usar esses mesmos recursos, combinando-os com a produção automática de documentos no Word. Portanto, vão-se encontrar aqui técnicas de gerenciamento de bancos de dados e esquemas de produção de documentos, também já vistos em capítulos anteriores. Neste projeto, vamos trabalhar com o mesmo banco de dados (Clientes.mdb) utilizado dois capítulos atrás, no projeto Banco de Dados no Word. Como agora a intenção é construir documentos, utilizaremos também um modelo do Word (Reciboal.dot) e um formulário. Este último, chamado frmDocBDados, é semelhante ao daquele projeto, tanto no design como no código. Portanto, vamos destacar aqui o que é diferente. Antes de avançar para o traçado do formulário, é importante determinar quais são nossos objetivos com este novo projeto. A idéia é oferecer a uma pequena imobiliária, ou a um locador pessoal de imóveis, um pequeno controle de seus clientes. Com o projeto Controle de Aluguéis, associado à fictícia Imobiliária Cidade Aberta, ele vai poder cadastrar clientes, listá-los em relatórios e emitir recibos personalizados. Essas funções demandaram a inclusão de um novo campo na tabela Clientes. Trata-se de ValorPago, que abriga o valor do aluguel. Na verdade, esse campo já estava lá, simplesmente não havia sido utilizado até o momento. Aparecem também três novas caixas de combinação: Mês, Ano e Tratamento – nenhuma delas ligada ao banco de dados. Nas duas primeiras o usuário deve escolher o mês e o ano de referência na hora de emitir um recibo de aluguel.
F I G U R A 3 0 . 1 Controle imobiliário: cadastro, relatório e recibo
A terceira caixa, embora também se chame Tratamento, tem um conteúdo diferente de sua homônima no projeto Banco de Dados no Word. Em vez de 261
“Sr.”, “Sra.”, lista opções como “do Sr.”, “da Sra.” ou simplesmente “de”. Seu objetivo é fornecer, pronta, uma partícula de texto para a redação do recibo: “Recebemos do Sr. Fulano de Tal...” As informações que compõem as listas dessas três caixas de combinação provêm do próprio código, e não do banco de dados. Além dos objetos associados ao gerenciamento do banco de dados, o formulário frmDocBDados exibe ainda dois botões de comando – Recibo e Relatório – envolvidos por uma moldura cuja legenda é Imprimir. As caixas Valor, Mês e Ano também estão circunscritas por uma moldura – Dados para o Recibo –, indicando sua vinculação direta à emissão desse tipo de documento. A nova caixa de texto Telefone também se associa a um campo já existente antes no banco de dados. Outra novidade especial é a caixa de combinação Localizar (cboLocalizar), que funciona como um instrumento de busca rápida. Escolha um nome na lista de Localizar, e o registro correspondente é trazido para o primeiro plano.
As caixas Tratamento, Mês e Ano Passemos aos destaques no código deste projeto. Na rotina de inicialização do formulário, além dos procedimentos relativos ao banco de dados, chama-se a sub PreencheCombos. Esta encarrega-se de fornecer conteúdo às caixas de combinação Tratamento, Mês e Ano. O preenchimento das duas últimas envolve alguns macetes. A caixa cboMês exibe os nomes dos meses por extenso. Para incluí-los, poderíamos ter usado repetidamente o método AddItem, para janeiro, fevereiro etc. Mas há uma forma mais elegante: capturar o nome do mês a partir de um loop de doze datas quaisquer, uma em cada mês: With cboMês For n = 1 To 12 .AddItem Format$(“1/” & n & “/2000", ”mmmm") Next n .ListIndex = Month(Now) - 1 End With
A caixa Ano é preenchida com 21 anos: o atual, dez anteriores e dez futuros. Tanto em cboMês como em cboAno, a propriedade ListIndex é ajustada de tal forma que os controles exibam, como padrão, o mês e o ano atuais. With cboAno For n = Year(Now) - 10 To Year(Now) + 10 .AddItem n Next n .ListIndex = 10 End With 262
Caixa de combinação Localizar O funcionamento da caixa de combinação Localizar (cboLocalizar) exige um esforço adicional de programação. No evento de inicialização do formulário, o programa chama o procedimento AtualizaCboLocalizar. Essa sub-rotina cria um conjunto de dados (recordset) chamado rsNome, que lê, na tabela Clientes, os campos Código e NomeCliente. Código é o número que fornece o identificador único de cada registro. Os valores de rsNome são adicionados à lista da caixa de combinação Localizar. Preste atenção ao trecho abaixo: usa-se o método AddItem para preencher a primeira coluna e a propriedade List para incluir o código na coluna 2 (na verdade, o índice dela é 1 porque o da primeira coluna é 0). i = -1 Do While Not rsNome.EOF i = i + 1 cboLocalizar.AddItem rsNome!NomeCliente cboLocalizar.List(i, 1) = rsNome!Código rsNome.MoveNext Loop
Vale lembrar que, desde o procedimento UserForm_Initialize, a caixa cboNomeCliente já foi caracterizada como tendo uma lista de duas colunas: cboLocalizar.ColumnCount = 2
Quando o usuário seleciona um nome na lista (evento Click da caixa de combinação), o programa pega o código associado ao nome escolhido e usa o método FindFirst para encontrar o registro. lngCódigo = CLng(cboLocalizar.Column(1)) If blnRegistroMudou And Not blnNovoReg Then QuerSalvar rs.FindFirst “Código =” & lngCódigo MostraRegistro cboLocalizar.ListIndex = -1
Como cboLocalizar tem apenas a função de busca – não precisa exibir nenhuma informação –, tão logo ela ajuda a encontrar um registro, seu texto é anulado, definindo-se valor da propriedade ListIndex para –1.
Detector de alterações Uma deficiência do projeto Banco de Dados no Word era sua incapacidade de detectar alterações ocorridas no registro durante sua exibição na tela. Assim, ao passar de um registro para outro, perdiam-se as alterações feitas e não salvas no
263
primeiro. O projeto atual preenche essa lacuna. O detector de mudanças é montado da seguinte maneira: quando se modifica o texto de um controle, essa ação é indicada no evento Change. Com base nisso, o primeiro passo é criar uma variável booleana, blnRegistroMudou, para representar a mudança do registro. Exemplo, para a caixa de texto txtTelefone: Private Sub txtTelefone_Change() blnRegistroMudou = True End Sub
A linha acima (blnRegistroMudou = True) deve ir para os eventos Change de todos os controles vinculados ao banco de dados. Isso vale para todas as caixas de texto, mas não atinge as caixas de combinação. Agora, a segunda parte. Nos eventos Click dos botões de navegação, o programa verifica o valor de blnRegistroMudou. Em caso positivo, pergunta ao usuário se deseja salvar as modificações. Se ele responder Sim, entra em cena a rotina cmdSalvar_Click. Portanto, a pergunta “o registro mudou?” deve ser feita no evento Click dos quatro botões de navegação e da caixa Localizar. Em todos eles, a rotina Objeto_Click deve conter a seguinte linha: If blnRegistroMudou Then QuerSalvar
Não estranhe: QuerSalvar é o nome da rotina que faz a pergunta ao usuário, por meio da seguinte caixa de mensagem:
F I G U R A 3 0 . 2 Mensagem emitida pela rotina QuerSalvar
Outro ponto em que o detector de alterações deve ser acionado é no fechamento do formulário, que pode ser disparado a partir de dois comandos: o botão Fechar e o botão com o pequeno X, também chamado Fechar, no canto superior direito do form. Portanto, a linha de teste também foi inserida no evento QueryClose, que ocorre antes de o formulário ser eliminado da memória. Para que o programa não dê alarmes falsos, apontando mudança no registro depois do acionamento do botão Novo, é preciso afirmar, logo aí, que não aconteceu nenhuma alteração. É por isso que, na rotina cmdNovo_Click, você encontra a linha: 264
blnRegistroMudou = False
Emissão de recibos Na Imobiliária Cidade Aberta, é fácil emitir um recibo personalizado. Basta selecionar o registro do cliente e clicar no botão Recibo. A rotina cmdRecibo_Click cria um documento novo com base no modelo Reciboal.dot e preenche esse documento com os valores lidos nos controles do formulário. O recurso usado aqui baseia-se nos serviços do objeto Selection.Find, responsável pelas funções de localizar e substituir no Word. Para tanto, como já vimos em outros projetos, o recibo-modelo precisa conter palavras-chave que serão encontradas e substituídas pelas variáveis adequadas. Um detalhe oculto, mas que merece ser ressaltado nesse modelo de recibo, é a data automática. Em projetos anteriores, usei o VBA para montar a data e inseri-la no documento. Nesse caso, resolvi aproveitar a data que o Word, sem programação, é capaz de oferecer. Ao elaborar um documento, há, pelo menos, duas maneiras interativas de incluir datas. A mais simples é utilizar o comando Inserir/Data e Hora. Se, na tela Data e Hora, a caixa de verificação Atualizar Automaticamente estiver desligada, você simplesmente insere a data ou hora no formato escolhido. Com aquela caixa marcada, insere um campo de data/hora – que aparentemente serve para os propósitos de automatizar o preenchimento de documentos. O problema é que o valor mostrado nesse campo vai mudar cada vez que você abrir o documento. Ou seja, ela mostra dia 20 hoje, dia 21 amanhã. Em suma, não fica registrada ali a data da criação do texto. Portanto, o comando Inserir/Data e Hora não é o caminho. A outra maneira de automatizar datas está também no menu Inserir, alternativa Campo, categoria Data e Hora.Você encontra lá seis possibilidades. O campo que nos interessa é CreateDate. Ele fornece exatamente o que queremos: a data em que o documento foi criado. Assim, o conteúdo do campo passa a ser: CREATEDATE \@ “d’ de ‘MMMM’ de ‘yyyy” A parte depois do sinal @ é o formato da data, aqui ajustado para algo como “22 de março de 2000”. No Word 2000, a caixa de diálogo para inserção de campos é mostrada na Figura 30.3. Para ver o conteúdo do campo, acione Ferramentas/Opções/orelha Exibir, caixa Código de Campos. Para emitir recibos reais com o Controle de Aluguéis, você precisa adaptar o modelo Reciboal.dot aos seus padrões. Primeiro, ir ao cabeçalho e colocar lá o nome e o logotipo de sua própria empresa, acompanhados de endereço, telefone etc. Precisa, também, ajustar o próprio texto do recibo aos seus padrões. Você pode modificar tudo, desde que preserve as palavras-chave. Nesse modelo elas são: quantiavalorpago pronometratamento nomedopagador
265
F I G U R A 3 0 . 3 Inserir/Campo: para exibir datas que não
mudam
quantiavalorpago quantiaporextenso mespagamento anopagamento endereçodoimóvel Só para lembrar: as palavras-chave são assim esquisitas para evitar a confusão com qualquer outro termo do texto. Se você quiser, pode incluir novas variáveis, como o nome da pessoa que assina o recibo, o cargo que ela ocupa e até mesmo o nome da empresa. No final, o recibo produzido tem o layout mostrado na Figura 30.4.
Nota importante
O recibo só pode ser emitido se duas condições estiverem satisfeitas. A primeira: o arquivo Extens32.dll deve estar na pasta de sistema do Windows. A função extenso (assim mesmo, com letra minúscula), residente nessa DLL, é a responsável por fornecer o texto do número por extenso. A declaração da função, nos moldes da interface de programação do Windows, deve ser feita assim:
Public Declare Function extenso Lib “Extens32.dll” _ (ByVal valor As String, ByVal Retorno As String) As Integer
266
O segundo requisito está ligado ao fato de que essa declaração só pode ser feita num módulo: não é aceito o código de um formulário. Por isso, o formulário frmDocBDados deve ser acompanhado de um módulo que contenha a linha acima.
F I G U R A 3 0 . 4 Recibo de aluguel: os valores em
destaque vêm do aplicativo
Relatório O relatório produzido por esta aplicação lista, em ordem alfabética por nome, todos os clientes da imobiliária, com seus dados básicos: endereço, telefone, valor do aluguel. A rotina cmdRelatório_Click, disparada com o clique no botão Relatório, envolve uma série de detalhes interessantes. A lista de clientes é apresentada em forma de tabela, com todos os campos fundamentais mostrados no formulário. O documento é montado, basicamente, com a ajuda do método TypeText, aplicado ao objeto Selection em diferentes situações. Primeiro, cria-se um documento novo e ajusta-se a orientação da página para a posição horizontal, para que os campos da tabela – nove – sejam acomodados com mais espaço. Em seguida, escrevem-se duas linhas de cabeçalho mais uma linha em branco. Agora, vem a tabela. Cada linha é formada pelos valores dos campos separados pelo caractere Tab, aqui representado pela constante intrínseca vbTab. No final, a constante vbCrLf, que introduz novo parágrafo. Primeiro, a linha com os nomes dos campos, ou seja, os títulos da tabela: Selection.TypeText “No.” & vbTab & “Nome” & vbTab & _ “Aluguel (R$)” & vbTab & “Endereço” & vbTab & _ “CEP” & vbTab & “Cidade” & vbTab & _ “Estado” & vbTab & “Telefone” & vbTab & _ “E_mail” & vbCrLf
Seguem-se as outras linhas da tabela, agora retiradas do banco de dados. Um loop percorre todo o conjunto de dados rs e vai enfileirando os valores de cada campo a ser incluído na tabela. Ao escrever o código do qual vai resultar a 267
tabela, é necessário prestar muita atenção à seqüência utilizada na linha de cabeçalho, a fim de que as outras linhas correspondam exatamente. n = 0 Do While Not rs.EOF n = n + 1 Selection.TypeText CStr(n) & vbTab & rs!NomeCliente & _ vbTab & Format(rs!ValorPago, “#,##0.00") & _ vbTab & rs!Endereço & vbTab & rs!CEP & vbTab & _ rs!Cidade & vbTab & rs!Estado & vbTab & _ rs!Telefone & vbTab & rs!Email & vbCrLf rs.MoveNext Loop
O valor n incluído na tabela é um recurso para numerar seqüencialmente cada linha da tabela. Depois de escrever todas as linhas, na verdade ainda não temos a tabela. Para construí-la e formatá-la, vamos precisar de mais de um micromalabarismo. O primeiro deles tem por objetivo selecionar somente a parte do documento que formará a tabela – o cabeçalho fica de fora. Como fazer isso? Ao escrever a última linha da tabela, o cursor, naturalmente, está no final do documento. Então, vamos levá-lo até o início, estendendo a seleção. Em seguida, para deixar à parte as três linhas do cabeçalho (duas, mais uma em branco), vamos mover o cursor três linhas para baixo. With Selection ‘ Seleciona somente o texto que será tabela .HomeKey Unit:=wdStory, Extend:=True .MoveDown Unit:=wdLine, Count:=3, Extend:=True
Perfeito. Agora, basta usar o método ConvertToTable e temos, enfim, uma tabela. Nem é preciso indicar que o caractere separador é o Tab, e que são nove colunas. O Word é suficientemente esperto e faz tudo direitinho. Quer dizer – se você ajudar. Aqui vai um exemplo em que você pode atrapalhar. Aliás, mais um caso de atenção para os detalhes. Você vai notar, ao emitir relatórios, que a palavra E-mail, no título da coluna, aparece como E_mail. Esta foi uma saída que encontrei para resolver um embaralhamento da tabela que ocorre quando se usa no título a palavra “E-mail”. A confusão ocorre, igualmente, no Word 2000 e no 97. Portanto, cuidado com o hífen no cabeçalho. Mais detalhes: para que o valor do aluguel seja mostrado corretamente no formulário ou no relatório – como moeda, com duas casas decimais –, é preciso formatá-lo: Format(rs!ValorPago, “#,##0.00") 268
Sigamos em frente. A tabela está pronta, mas precisa de ajustes. Ao criar uma tabela, o Word atribui largura igual a todos os campos. Além disso, alinha todas as colunas à esquerda. Para corrigir a primeira inconveniência, apliquemos o comando Selection.Cells.AutoFit com toda a tabela selecionada. Com ele, as colunas se rearranjam, redistribuindo o espaço. Quanto ao alinhamento do texto, ele só constitui problema nas colunas 1 (o número seqüencial) e na 3, o valor do aluguel. Então, vamos selecionar a primeira coluna e aplicar nela o alinhamento de parágrafo à direita. Por fim, salva-se o documento com o nome Lista de Clientes.doc. A montagem do relatório envolve ainda outros detalhes, como a atribuição de uma cor de fundo à linha de títulos da tabela e a aplicação de diferentes tamanhos de fontes no cabeçalho e no corpo da tabela. Analise a rotina cmdRelatório_Click. Ela tem uma série de aspectos utilíssimos para quem programa com o Word. No final, o relatório tem o aspecto mostrado pela Figura 30.5:
F I G U R A 3 0 . 5 O relatório: números formatados e cabeçalho
com destaque em cor
Para ir mais além 1. A aplicação Controle de Aluguéis é daquelas que permitem reunir um
imenso rol de idéias para modificação e ampliação. Por exemplo: você pode incluir no banco de dados e no formulário um campo do tipo Memorando, para armazenar informações textuais sobre os clientes.
2. Somente o item Relatório dá pano para muitas mangas. Basta você parar
alguns minutos e já terá uma lista de diferentes relatórios. Pense – e ponha as mãos na massa! 269
3. Ao longo deste livro, temos repetido, de forma clara ou implícita, que
uma parte fundamental do trabalho de programação consiste em prestar atenção aos detalhes. Eis mais um exemplo. Em todas as rotinas em que foi incluída, a linha de código abaixo é sempre a primeira. If blnRegistroMudou Then QuerSalvar
No entanto, o evento cboNomeCliente_Click constitui uma exceção. Nele, essa linha é a número dois. A mudança foi feita porque o programa acusava um erro na seguinte situação: O registro sofreu mudança e o usuário escolhia um nome na caixa Localizar. Surgia a mensagem com a opção de salvar o registro. Se a resposta fosse não, tudo bem. Caso contrário, vinha o erro. Portanto, uma simples troca na posição de duas linhas aparentemente desvinculadas e sem obrigação de manter seqüência pode produzir um bug.
4. Ao percorrer o conjunto de dados rs para gerar o relatório de clientes, usou-se uma variável n, que vai sendo acumulada a cada linha escrita no documento. Uma alternativa, baseada apenas em recursos do próprio motor do banco de dados, seria trabalhar com a propriedade AbsolutePosition. Bastaria declarar uma variável – digamos j – como Long e incluir a seguinte linha dentro do loop: j = AbsolutePosition + 1 Selection.TypeText CStr(j)
5. Uma das novidades do Word 2000 é a propriedade BackGroundPat-
ternColor, que aceita cores RGB (16,7 milhões de tonalidades). No Word 97 só está disponível a propriedade BackgroundPatternColorIndex, correspondente a um estoque de apenas dezoito cores. Portanto, a linha abaixo produz um erro no Word 97, mas é aceita no 2000. Selection.Range.Shading. _ BackgroundPatternColor = wdColorGray10
Para manter a compatiblidade com as duas versões do programa, usamos sempre a propriedade BackgroundPatternColorIndex. No entanto, se você usa o Word 2000, prefira a outra.
270
Banco de dados com senha Se você conhece a senha, é fácil abrir um banco de dados protegido via programação VBA. Admita que a senha seja “abc123”. Para acessar o banco de dados, basta usar: Set db= OpenDatabase (“g:\custos.mdb”, False, _ False, “MS Access;pwd=abc123")
final:
Se se tratasse de uma planilha do Excel, bastaria adaptar a string
“Excel 8.0;pwd=abc123"
Planilha Excel sem Excel Nos três últimos capítulos, usamos, como banco de dados, um arquivo do Access e outro do Excel. Partimos de arquivos prontos, criados com os respectivos programas. Todavia, mesmo quem não tem o Access e o Excel pode criar bancos de dados MDB e planilhas XLS, via programação. Sobre os primeiros, consulte a ajuda do VBA quanto ao método CreateDatabase, do DAO. Para criar pastas de trabalho do Excel, o esquema, também baseado no DAO, é mostrado no exemplo a seguir. Ele cria uma pasta de trabalho chamada Pasta0.xls, com uma planilha chamada Custos. Nesta, cria dois campos: Item, de texto, e Custo, com valores em formato Moeda. Além disso, escreve duas linhas nessa planilha, conforme se vê na figura abaixo.
F I G U R A 3 0 . 6 A planilha Custos Sub CriaPastaXLS() Dim db As Database Dim td As TableDef Dim fl As DAO.Field Dim rs As Recordset
‘banco de dados ‘tabela ‘campo ‘planilha 271
‘ Cria o db, adiciona a tabela (planilha) ‘ e dois campos (colunas) Set db = OpenDatabase(“c:\teste\Pasta0.xls”, _ False, False, “Excel 8.0;”) Set td = db.CreateTableDef(“Custos”) Set fl = td.CreateField(“Item”, dbText, 20) td.Fields.Append fl Set fl = td.CreateField(“Custo”, dbCurrency) td.Fields.Append fl db.TableDefs.Append td ‘ Abre a tabela para edição Set rs = db.OpenRecordset(“Custos$”) ‘ Adiciona um registro (linha) rs.AddNew rs(“Item”) = “Peças” rs(“Custo”) = 2345.51 rs.Update ‘ Adiciona outro registro rs.AddNew rs!Item = “Acessórios” rs!Custo = 1350.85 rs.Update ‘ Fecha o arquivo rs.Close db.Close End Sub
Alguns comentários. 1. Observe que a variável fl, para o campo de dados, é declarada como DAO.Field. Por quê? Como estamos no Word, e este também tem seu objeto Field (campo), o prefixo DAO evita a confusão, indicando que estamos tratando do campo de banco de dados. 2. Ao abrir a tabela para edição, não se esqueça de adicionar o cifrão ($) ao final do nome da tabela. 3. Ao adicionar registros, o programa acima exibe duas formas de referência ao campo de dados. A primeira usa o nome do campo como índice textual: rs(“Custo”). A outra usa o campo diretamente como objeto rs!Custo. Há ainda uma terceira, que trabalha com índices numéricos: rs(1). 4. No código, use o padrão americano para escrever números não inteiros: 1350.85, e não 1350,85.
272
31 Um recibo para todas as transações Crie um gerador de recibos configurável para diferentes usos e situações Ficha do projeto Projeto: Gerador de Recibos O que faz: Oferece ao usuário um programa configurável e adaptável a diferentes tipos de recibos. Usuários potenciais: Qualquer usuário do Word. Arquivos e requisitos do projeto: Formulário frmReciboGeral e código interno, contido nos no modelo Recibog.dot, o qual também abriga o documento-base para o recibo. O projeto envolve ainda os seguintes arquivos: • Rclients.lst, lista de clientes. • Rpessoas.lst, lista de pessoas que assinam recibos. • Rtipserv.lst, lista de tipos e descrições de serviços. • Extens32.dll, biblioteca para geração valores monetários por extenso. • Rc_ajuda.txt, arquivo de ajuda.
Conhecimento técnico: Operações com arquivos-texto e manipulação de listas. Uso do controle MultiPage (guias) e do objeto Selection.Find, para busca e substituição de informações em documentos do Word. Nível de programação: Avançado
Depois de experimentar com o desenvolvimento de aplicativos em Word/VBA para automatizar a emissão de diferentes documentos, resolvi criar um gerador universal de recibos. A idéia é criar um programa configurável para emitir recibos, de modo que o usuário possa utilizá-lo sem a necessidade de mexer na programação. A origem deste projeto é curiosa. Eu já havia escrito soluções específicas para o preenchimento automático de memorandos, cartas, bilhetes de sorteio e até recibos específicos, como os de aluguel. Todavia, jamais me ocorrera criar um recibo universal, um documento pau-para-toda-obra, capaz de atender a diferentes finalidades comerciais. Foi então que um leitor, Eduardo Smith, dono de um escritório de consultoria em Niterói, RJ, enviou-me um e-mail perguntando como automatizar a confecção de recibos. Respondi dizendo que tinha uma solução pronta e a mandaria para ele. A solução de fato existia: era um gerador de recibos de aluguel. Antes de enviá-la, passou-me pela cabeça que ela não serviria aos propósitos do leitor. Assim, comecei a lhe fazer perguntas (sempre via e-mail) sobre o que ele realmente queria fazer, qual era a tarefa a automatizar. Nessa correspondência eletrônica, Smith revelou o que queria, mandou-me um modelo de recibo. A minha intenção era tornar a aplicação a mais genérica possível, então fomos trocando idéias até chegar a um resultado final, que é o projeto Gerador de Recibos, discutido neste capítulo.
O que faz o Gerador de Recibos Como ficou claro, o programa evoluiu a partir da necessidade concreta de um usuário e de minha intenção de torná-lo universal. Uma das idéias-chave é permitir que o usuário utilize a solução de forma personalizada, não importa o tipo de recibo que ele queira emitir. Para tanto, o Gerador de Recibos oferece as seguintes características:
1. Trabalha com as variáveis: valor do recibo; cidade onde é emitido; data;
274
nome da pessoa ou empresa pagadora; tratamento a ser dado a essa pessoa (senhor, senhora, empresa etc.); extenso automático do valor; mês e ano do fato gerador do recibo; motivo do pagamento (descrição de ser-
viços prestados, aluguel etc.); nome da empresa recebedora; nome e cargo da pessoa que assina o recibo. Alguns desses itens (como data) são fornecidos pelo próprio sistema operacional. Outros, como o nome do pagador e do recebedor, devem ser informados pelo emitente.
2. Certas informações utilizadas num recibo são recorrentes em cada em-
presa. O escritório de prestação de serviços, por exemplo, tende a ter clientes fixos, dos quais recebe pagamentos periódicos. Da mesma forma, várias pessoas dentro da empresa podem assinar os recibos. Além disso, os recibos são emitidos pela prestação de diferentes tipos de serviços. Todos esses itens (clientes, pessoas que assinam os recibos e tipos de serviços) podem ser catalogados e se transformar em itens de escolha na hora produzir um recibo.
3. Outros itens (como cidade e nome da empresa que emite o recibo) são únicos e, portanto, merecem outro tipo de tratamento.
Neste projeto, mais uma vez, vamos trabalhar com um documento-modelo para o recibo, e um formulário – a caixa de diálogo, a partir da qual o usuário vai configurar cada recibo. A estratégia será manter as palavras-chave no documento, as quais serão substituídas conforme as definições feitas pelo usuário na caixa de diálogo. O usuário que não conhece programação terá a liberdade de modificar, a seu gosto, o texto do documento. Ele apenas é obrigado a manter as palavras-chave, para permitir a substituição. O formato do documento também pode ser, livremente, modificado, com a inclusão de cabeçalho e rodapé, ajuste da folha de papel (Carta, A4, Ofício), mudança de margens, alteração dos formatos de parágrafo, tamanho e estilo de fontes. Os únicos itens que não podem ser modificados – repita-se – são as palavras-chave. Para apresentar uma imagem concreta do formulário, veja a Figura 31.1. O formulário frmReciboGeral contém um controle Multipage, com três guias, ou orelhas. Na orelha principal (Dados para o Recibo), são apresentados todos os elementos necessários para a emissão de recibos. Nas outras duas estão apenas itens de personalização das informações fixas (cidade, nome da empresa), ou móveis (clientes, tipos de recibo etc.).
Orelha Dados para o Recibo Para emitir um recibo, o usuário deve fazer as seguintes operações:
1. Escolher (ou digitar) o nome do pagador na caixa Nome do Pagador. 2. Escolher o tratamento para o pagador (Sr., Sra., empresa, etc.). 3. Digitar o valor recebido. O padrão é 1234,67. O programa incluirá, automaticamente, a separação de milhares, se necessário (1.234,67).
275
F I G U R A 3 1 . 1 O formulário para a emissão de recibos: com
orelhas
4. Selecionar a pessoa que assina o recibo. 5. Selecionar o mês e o ano aos quais o recibo se refere. No caso de não se
tratar de recibo repetitivo, o usuário tem a liberdade de modificar o modelo de recibo, eliminando as variáveis relativas a mês e ano.
6. Escolher o tipo de recibo. 7. Clicar no botão OK. Antes de disparar a confecção do documento, o Gerador de Recibos dá ao usuário a oportunidade de rever suas escolhas e voltar para corrigi-las. Isso é feito mediante a apresentação de uma caixa de diálogo com o resumo das definições estabelecidas pelo usuário.
276
F I G U R A 3 1 . 2 A caixa de diálogo de confirmação
Na dúvida, o usuário pode acionar o botão Ajuda, que abre um texto com orientação completa sobre o uso do programa. Detalhe: a etiqueta, ou rótulo, exibida acima do botão OK mostra o nome com o qual o recibo será salvo em disco.
Orelha Empresa e Cidade Esta segunda orelha (Figura 31.3) é dedicada à configuração dos itens que serão fixos para cada usuário na emissão de seus recibos: cidade, empresa do usuário, e pasta para armazenar os documentos. Para emitir o recibo, o usuário deve preencher as caixas de texto Sua Cidade, Sua Empresa e Pasta para Armazenamento dos Arquivos e, em seguida, acionar o botão Salvar Configurações. No caso da caixa Pasta para Armazenamento, ele tem a opção de clicar no botão Procurar e indicar o diretório desejado.
F I G U R A 3 1 . 3 Orelha Empresa e Cidade: configurações fixas
A orelha Empresa e Cidade inclui ainda a caixa de verificação Confirmar Definições ao Emitir Recibos. Ligada, ela provoca a exibição da caixa de diálogo de confirmação quando o usuário aciona o botão OK para disparar a emissão de um recibo. Se, depois de algum tempo, ele concluir que a confirmação se tornou um estorvo, pode desligá-la e, para fixar essa escolha, clicar no botão Salvar Configurações. Esse último botão também provoca a exibição de uma caixa de diálogo, apenas para informação do usuário.
277
F I G U R A 3 1 . 4 Informação após salvar configurações
Orelha Clientes, Recebedores e Serviços Se a guia Empresa e Cidade é dedicada à configuração dos itens fixos, a orelha Clientes, Recebedores e Serviços volta-se para as informações móveis. Para incluir, modificar ou excluir um item cadastrado, basta clicar num dos três botões principais dessa guia.
F I G U R A 3 1 . 5 Guia Clientes, Recebedores e Serviços: cadastro
O clique num desses botões provoca a abertura, com o Bloco de Notas, de um arquivo texto que contém um cadastro de opções correspondentes. O botão Lista dos Clientes Mais Comuns abre o arquivo Rclients.lst, que contém uma relação simples com os nomes dos clientes, um em cada linha. O botão Lista das Pessoas Que Assinam Recibos abre um arquivo idêntico (Rpessoas.lst) com os 278 nomes das pessoas da empresa autorizadas a assinar recibos.
O terceiro botão, Tipos de Recibos e Descrição dos serviços abre no Bloco de Notas uma listagem (Rtipserv.lst) um pouco diferente. Admita que sua empresa preste diferentes gêneros de serviços, os quais devem constar nos recibos. Com algum esforço, você pode classificar esses serviços, dando a cada um título e descrição. Exemplos: Serviços contábeis execução de balancete mensal e emissão de guias fiscais Assessoria jurídica consultoria jurídica e acompanhamento de questões imobiliárias Esta é a estrutura do arquivo Rtipserv.lst. Aqui, é importante deixar claros alguns detalhes. A regra básica é: um título, uma descrição. Cada item deve ocupar uma única linha de texto. Isso significa que, se uma descrição for mais longa, mesmo assim deve ficar em apenas uma linha. Para garantir isso, no Bloco de Notas, basta escrevê-la normalmente e só quebrar a linha com a tecla Enter no final da descrição. Outro detalhe, que obedece ao esquema de redação do recibo mostrado no modelo Recibog.dot: a descrição deve começar e terminar com letra minúscula e não deve incluir ponto final. Embora títulos e descrições de serviços estejam juntos no arquivo Rtipserv.lst, são apresentados em separado na orelha principal do aplicativo. Os títulos são exibidos como opções na caixa de combinação Tipo de Recibo. Ao escolher um título nessa caixa, o usuário provoca o preenchimento da caixa de texto Descrição dos Serviços Prestados com a descrição que faz par com o item escolhido. A caixa Tipo de Recibo é neutra – ou seja, não participa diretamente da confecção dos recibos. Ela serve apenas para preencher a caixa Descrição dos Serviços Prestados. Esta, sim, está ligada à construção do documento. Por isso, se o usuário precisar emitir um recibo especial cuja descrição não esteja cadastrada, basta escrever o texto desejado diretamente na caixa Descrição. Depois de alterar, incluir ou apagar um item nos arquivos de configuração, o usuário deve clicar no quarto botão da orelha Clientes, Recebedores e Serviços. Esse botão força a atualização das informações nas caixas de combinação da orelha principal, Dados para o Recibo. Assim, um item incluído passa a estar disponível imediatamente. De outra forma, ele só estaria listado na próxima abertura do Gerador de Recibos. Já conhecemos todo o funcionamento externo do programa. Vejamos, agora, o que é o Gerador de Recibos por dentro, do ponto de vista do programador.
O aplicativo por dentro Em linhas gerais, você já sabe qual a estrutura de trabalho do Gerador de Recibos. Resta-nos apenas revelar quais os fios condutores que garantem esse fun- 279
cionamento. Em primeiro lugar, é bom dizer que o tanto o texto do recibo como a lógica de seu preenchimento estão contidos no mesmo modelo – Recibog.dot. Assim, a primeira tarefa é criar um recibo e salvá-lo como modelo (Recibog.dot). O texto do recibo deve conter as palavras-chave que serão substituídas pelas variáveis escolhidas pelo usuário. Essas palavras-chave são as seguintes:
PALAVRA-CHAVE
REFERE-SE A
SubValorPago
Valor do recibo
SubCidadeDaTransação
Nome da cidade em que o recibo é emitido
SubDataDoRecibo
Data de emissão do recibo
SubPronomeDeTratamento
Tratamento para o pagador: senhor, senhora etc.
SubNomeDoPagador
Nome do pagador
SubExtensoDoValorPago
Valor do recibo, escrito por extenso
SubMêsDoPagamento
Mês de referência do recibo
SubAnoDoPagamento
Ano de referência do recibo
SubDescriçãoDosServiçosPrestados
Descrição dos serviços pagos
SubNomeDaEmpresaQueRecebe
Nome do recebedor
SubNomeDaPessoaQueAssina
Nome do responsável pela emissão do recibo
SubCargoDaPessoaQueAssina
Cargo do responsável pela emissão do recibo
As palavras-chave são, de propósito, estranhas e longas. Isso evita que, na operação de busca e troca, outras palavras do texto sejam confundidas com elas. Ao elaborar o seu modelo de recibo, você pode modificar e personalizar, livremente, o modelo que acompanha este projeto. No entanto, se você alterar essas palavras-chave, deverá também fazer as alterações pertinentes no código.
O formulário Agora, vamos ao formulário. Com o modelo Recibog.dot aberto, passe para a janela do Visual Basic (Alt+F11) e selecione o projeto Recibog. Agora, acione o comando Inserir/UserForm, para criar o formulário frmReciboGeral. Neste, in280 clua um objeto Multipágina dotado de três orelhas, ou guias: Dados para o Reci-
bo; Empresa e Cidade; e Clientes, Recebedores e Serviços. Veja, agora, a lista dos controles que cada uma dessas guias deve conter:
TIPO DE CONTROLE
LEGENDA
NOME
Guia Dados para o Recibo Caixa de combinação
Nome do Pagador
cboPagador
Caixa de combinação
Tratamento
cboTratamento
Caixa de texto
Valor Recebido
txtValor
Caixa de combinação
Pessoa Que Assina o Recibo
cboQuemAssina
Caixa de combinação
Mês do Recibo
cboMês
Caixa de combinação
Ano do Recibo
cboAno
Caixa de combinação
Tipo de Recibo
cboTipos
Caixa de texto
Descrição dos Serviços Prestados
cboDescrição
Botão de comando
OK
cmdOK
Botão de comando
Cancelar
cmdCancelar
Botão de comando
Ajuda
cmdAjuda
Rótulo
-
labPróximoRecibo
Guia Empresa e Cidade Caixa de texto
Sua Cidade
txtCidade
Caixa de texto
Sua Empresa
txtEmpresa
Caixa de texto
Pasta para Armazenamento dos Arquivos
txtPastaDoc
Botão de comando
Salvar Configurações
cmdSalvar
Botão de comando
Procurar
cmdProcurar
Caixa de verificação
Confirmar definições ao emitir recibos
chkConfirmar
Guia Empresa e Cidade Botão de comando
Lista dos Clientes Mais Comuns
cmdClientes
Botão de comando
Lista das Pessoas Que Assinam Recibos
cmdPessoas 281
TIPO DE CONTROLE
LEGENDA
NOME
Botão de comando
Tipos de Recibos e Descrição dos Serviços
cmdTipos
Botão de comando
Atualizar Configurações
cmdAtualizar
Rótulo
-
labListaClientes
Rótulo
-
labListaPessoas
Rótulo
-
labTipos
Rótulo
-
labAtualizar
O código Muito bem. O formulário está desenhado e você já conhece como o programa funciona. Falta colocá-lo em funcionamento. Vamos pensar no que é preciso fazer. A operação básica consiste em pegar os valores indicados pelo usuário e substituir, com eles, as palavras-chave que estão no texto do documento. Esse é o núcleo do problema, que é relativamente simples. Mas, para tornar o programa universal, é necessário garantir a execução de uma série de operações que devem ocorrer antes e depois que o usuário clique no botão OK. Nelas é que estão os detalhes e as verdadeiras dificuldades deste projeto. Vamos destacar algumas operações que vêm antes do clique. O primeiro bloco delas, naturalmente, deve acontecer logo durante a inicialização do formulário. É preciso, por exemplo, dizer onde estão os arquivos com os quais o programa irá trabalhar. Todos eles estão localizados no diretório-padrão para armazenamento de modelos do Word, cuja localização exata varia conforme a versão do programa. Para simplificar, é a mesma pasta onde fica o modelo Normal.dot. Em código, ela é dada por: Options.DefaultFilePath(wdUserTemplatesPath)
Outra providência que ocorre durante a inicialização do programa é o preenchimento das caixas de combinação. Comecemos pelas mais simples. A caixa cboTratamento recebe sua coleção de itens da seguinte maneira:
282
With cboTratamento .AddItem “de” .AddItem “da” .AddItem “do” .AddItem “da empresa” .AddItem “do Sr.” .AddItem “da Sra.”
.AddItem “da Srta.” .ListIndex = 0 End With
Apenas para lembrar: esses itens têm o formato “do Sr.”, “da Sra.” etc. porque devem combinar na frase que abre o recibo: “Recebemos do Sr. Fulano de Tal”... Incluir, aí, a preposição é uma forma de já passar o tratamento com o gênero correto, a fim de evitar coisas como “do senhora”. Outras caixas de combinação fáceis de preencher são as que abrigam o mês e o ano de referência do recibo. Para o mês, vale destacar um pequeno truque. Poderíamos muito bem usar o método AddItem e incluir uma lista de janeiro a dezembro. No entanto, há um jeito mais esperto de fazer isso: With cboMês For n = 1 To 12 .AddItem Format$(“1/” & n & “/2000", ”mmmm") Next n .ListIndex = Month(Now) - 1 End With
Observe: fizemos um loop de 1 a 12, e forçamos a montagem de uma data com o primeiro dia do mês correspondente (o ano poderia ser qualquer um). Em seguida – ou, melhor dizendo, no mesmo passo –, usamos a função Format para que o VBA inclua na caixa de combinação o nome do mês correspondente àquela data. Assim, com três linhas resolvemos uma questão que, da primeira forma pensada, consumiria pelo menos doze. Preenchida a caixa Mês do Recibo, usa-se a sua propriedade ListIndex para exibir o mês corrente, que equivale a: Month(Now) - 1
O -1, aí, tem apenas o objetivo de compatibilizar a série de meses (1 a 12) com os índices dos itens da caixa de combinação, que tem base zero. A caixa Ano do Recibo recebe outro tratamento. Ela exibe 21 anos: o atual, dez anteriores e dez posteriores. Se você achar esse número excessivo, pode trocar o número 10 por outro menor – por exemplo, 5. O código é o seguinte: With cboAno For n = Year(Now) - 10 To Year(Now) + 10 .AddItem n Next n .ListIndex = 10 End With
Agora, passemos à leitura das configurações já feitas pelo usuário. Primeiro, as fixas: empresa, cidade e pasta para armazenamento dos recibos como ar- 283
quivos DOC. Essas configurações estão armazenadas no arquivo Recibog.ini. Então, a primeira tarefa é tentar ler essas informações. Para isso, usa-se a propriedade PrivateProfileString, do objeto System: txtEmpresa = System.PrivateProfileString(m_strArqIni, _ “Configs”, “Empresa”)
Se o arquivo INI não existir (ou existir, mas sem um valor para a chave Empresa), a propriedade PrivateProfileString vai retornar uma string vazia. Nesse caso, não há problema, pelo menos, por enquanto — pois o usuário será avisado para informar o nome de sua empresa ao tentar emitir qualquer recibo. O mesmo vale para o item cidade. Para a pasta de documentos e a opção Confirmar Definições ao Emitir Recibos, adota-se procedimento diferente. Se o nome da pasta não existir, adota-se como padrão c:\meus documentos. Se não houver no arquivo INI uma opção registrada (Sim ou Não) para a exibição de confirmações, adota-se como padrão o Sim, que será transferido como uma marca na caixa de verificação. Ainda na sub UserForm_Initialize, garante-se que o nome da empresa do usuário (se já estiver registrado no arquivo INI) seja exibido na barra de título do formulário. Com isso, cada usuário fica com um Gerador de Recibos como se tivesse sido feito sob medida para ele. Passemos, agora, às configurações móveis – ou seja, à leitura dos arquivos LST (Rclients, Rpessoas e Rtipserv), que vão fornecer informações para as caixas de combinação cboPagador, cboQuemAssina e cboTipos. Portanto, é preciso ler, linha a linha, cada um desses arquivos e preencher as caixas de combinação correspondentes. Neste caso, essa operação não é feita na rotina UserForm_Initialize. O motivo é simples: se um desses arquivos não for encontrado, é preciso emitir um aviso ao usuário, informando-o. Mas se esse aviso partir do evento Initialize, ele será exibido antes do formulário – uma esquisitice que é bom evitar. Por causa disso, transferimos a abertura dos arquivos LST para o evento UserForm_Activate, que ocorre depois de Initialize, quando o formulário passa a ser o objeto ativo e, portanto, já está visível. Observe, porém, que esse recurso só se tornou legítimo porque o projeto tem apenas um formulário. Num projeto com múltiplos forms, as operações comandadas a partir do evento Activate seriam disparadas toda vez que outro formulário se fechasse e o foco voltasse para frmReciboGeral. Mesmo que as operações fossem silenciosas e não envolvessem avisos ao usuário, não deveriam ser colocadas aí, visto que seriam realizadas muitas vezes, desnecessariamente. Na sub UserForm_Activate, as caixas de combinação cboPagador, cboQuemAssina e cboTipos ganham, cada uma, um trecho de código como este: 284
If ArqExiste(m_strArqClientes) Then PreencheCombos cboPagador Else indMsg = 2 End If
A primeira linha testa se existe o arquivo LST correspondente. Em caso afirmativo, recorre à rotina PreencheCombos, passando o objeto a ser preenchido. Se o arquivo não existir, a variável inteira indMsg assume um valor. Para as duas outras caixas de combinação, se o arquivo também não existir, indMsg acumulará valores (2 para o primeira, 4 para a segunda e 8 para a terceira). No final, o número assim obtido será passado para a rotina Mensagens, que exibirá o aviso adequadamente, dizendo quais arquivos estão faltando. O preenchimento da caixa de combinação cboTipos é diferente dos demais. Ele envolve duas partes. Na primeira, a caixa é povoada da mesma forma que as outras. Mas isso significa que ela passa a conter não somente os títulos (ou tipos) de serviços, mas também as descrições. Então, quando se trata da caixa cboTipos, a rotina PreencheCombos chama a sub AjustaCboTipos para fazer a segunda parte. AjustaCboTipos encarrega-se de eliminar da caixa cboTipos as descrições de serviços. Para isso, faz o seguinte. Primeiro, divide por dois o número de itens na caixa cboTipos e dimensiona uma matriz para conter esse número de elementos. Essa matriz vai conter as descrições. Em seguida, entra num loop For/Next. Em cada volta desse loop uma descrição é selecionada, armazenada como um elemento da matriz e, em seguida, apagada da caixa de combinação. O código que faz isso é o seguinte: ‘ Matriz de descrições intMatriz = cboTipos.ListCount \ 2 ReDim m_sDescrição(1 To intMatriz) ‘ Preenche a matriz For n = 0 To intMatriz - 1 cboTipos.ListIndex = n + 1 m_sDescrição(n + 1) = cboTipos cboTipos.RemoveItem n + 1 Next n
Ao final, a caixa de combinação contém apenas títulos dos serviços, enquanto as descrições correspondentes ficam guardadas na matriz. Quando o usuário escolher uma opção na caixa Tipo de Recibo, a descrição associada será inserida na caixa de texto txtDescrição, graças à seguinte linha na sub cboTipos_Click: txtDescrição = m_sDescrição(cboTipos.ListIndex + 1)
285
Ainda na sub UserForm_Activate são chamados dois procedimentos: VerificaAno e CapturaNúmero. Ambas referem-se ao nome do arquivo do recibo — algo que irá acontecer depois que o usuário clicar no botão OK para emitir o recibo. Contudo, precisam ser disparadas desde já, até porque esse nome é informado previamente ao usuário, na etiqueta labPróximoRecibo. Esclareçamos. Para permitir o armazenamento dos recibos como um documento do Word, decidimos nomear cada um deles com um sistema de numeração crescente, que adota o seguinte formato: R0001-00.doc, R0002-00.doc, R0003-00.doc (...) R9999-00.doc. Essa denominação do arquivo envolve três partes. O R provém de recibo. Depois, vem o número, ue pode crescer, mantendo o padrão, até 9999. Por fim, vem o ano, em dois dígitos, antecedido por um hífen. Quando ocorre uma virada de ano, a numeração recomeça e o ano é também ajustado: R0001-01.doc. Para que isso aconteça, o arquivo INI é utilizado para armazenar o ano atual e o número utilizado na emissão do último documento. Quando completo, o arquivo Recibog.ini tem o seguinte formato: [Configs] Empresa=Neves & Naves Ltda. Cidade=São Paulo RecNum=2 Ano=2000 Confirmar=Sim PastaDoc=c:\meus documentos
As chaves referentes ao nome do documento do recibo são RecNum e Ano. Pois bem. A rotina VerificaAno tenta ler no arquivo INI o valor armazenado na chave Ano. Se não existir, ela consulta a data no relógio do sistema e escreve Ano=. Se o ano existe, a rotina o compara com o ano fornecido pelo PC. Se este for maior que o registrado no arquivo INI, então conclui-se que estamos num novo ano. Então, chama-se a rotina ZeraContagem, que escreve o novo ano no arquivo INI e faz RecNum=0. A outra rotina, CapturaNúmero, também tenta ler o número do último recibo emitido. Se não existe, ela, imediatamente, escreve RecNum=0, para dar início à numeração. Com isso, o próximo recibo já pode ter número: 1. Número e ano agora estão garantidos, de modo que é possível montar o nome do arquivo. Você deve estar se perguntando: por que tanto trabalho para fazer essa numeração seqüencial se o Windows 95/98, o NT e o 2000 suportam nomes longos? A resposta é simples. Além de um exercício de programação, os arquivos com nomes de igual comprimento facilitam a organização. Você vai saber que o documento R0036-00.doc foi escrito antes de R0041-00.doc. Além disso, no Explorer, os arquivos são exibidos em ordem numérica (e, em conseqüência, cronológica), o que não aconteceria se não houvesse a numeração com os zeros marcando as posições de milhar, centena e dezena. 286
Um detalhe: a rotina UserForm_Activate começa definindo a variável m_blnProgIni como verdadeira e termina trocando o seu valor para falso. Com o primeiro valor, ela evita que, durante a inicialização, seja disparado o evento clique da caixa de combinação cboTipos, produzindo um erro. O mesmo recurso é usado durante a atualização do conteúdo das caixas de combinação, provocada pelo botão Atualizar Configurações. A rotina associada a este último botão simplesmente recorre à sub PreencheCombos para todas as caixas de combinação ligadas aos arquivos LST. Assim, as caixas são esvaziadas e repovoadas, agora incluindo as mudanças introduzidas nos arquivos. Quando, enfim, o usuário clica no botão OK, começam os procedimentos que preparam a emissão do recibo. Inicialmente, a rotina cmdOK_Click verifica se todas as caixas necessárias estão preenchidas. Em caso negativo, o processamento é interrompido e o usuário recebe um aviso. Um exemplo dessa verificação: se o campo Empresa ou Cidade não estiver preenchido, o usuário é remetido para a orelha Empresa e Cidade. Para exibir a segunda orelha do objeto Multipágina, o comando é: MultiPage1.Value = 1
Como se vê, o índice das orelhas desse controle opera com base zero. Duas outras verificações são feitas antes de deflagrar o preenchimento do recibo. Uma testa a validade do diretório, indicado na caixa txtPastaDoc. A outra é o pedido de confirmação, exibido se a caixa de verificação chkConfirmar estiver marcada. Concluídos os testes, entramos na reta final para a emissão do recibo. Então a rotina oculta o formulário e chama a função de extenso na biblioteca Extens32.dll (arquivo que acompanha o projeto), passando-lhe o valor do recibo. Obtém, assim, esse valor por extenso, em reais, e armazena numa variável. Depois, determina a data. No Word 2000, fica assim: strData = Format(Now, “d \de mmmm \de yyyy”)
No Word 97, a linha acima não funciona. Use, então, a linha abaixo, que também funciona no Word 2000: strData = Day(Now) & “ de ” & Format(Now, “mmmm”) & _ “ de ” & Year(Now)
Por fim, o procedimento monta a matriz strVars, de duas dimensões, a primeira para os valores definidos no formulário e a outra para a palavra-chave no documento. Exemplo: strVars(7, 1) = strMes strVars(7, 2) = “subMêsDoPagamento”
287
strVars(8, 1) = strAno strVars(8, 2) = “subAnoDoPagamento”
O uso da rotina facilita a montagem da operação de busca das palavras-chave e sua substituição pelos valores definidos no formulário. For n = 1 To iTotalVars With Selection.Find .ClearFormatting .Replacement.ClearFormatting .Text = strVars(n, 2) ‘ texto a localizar .Replacement.Text = strVars(n, 1) ‘novo texto .Forward = True .Wrap = wdFindContinue .Execute Replace:=wdReplaceAll End With Next n
O resultado é o recibo, tal como mostrado na Figura 31.6. Depois de preencher o documento, a rotina cmdOK_Click salva o arquivo, nomeando-o conforme o esquema de numeração seqüencial já discutido anteriormente.
F I G U R A 3 1 . 6 O documento final: preenchido e salvo com um
número seqüencial
Por fim, é preciso relembrar que todo projeto do Gerador de Recibos está alojado no modelo Recibog.dot. A idéia é exibir o formulário do programa sempre que um novo documento seja criado com base nesse modelo. Então, é necessário escrever uma rotina que abra o formulário frmReciboGeral, no momento da criação de um novo documento. Para isso, dê um duplo clique na pasta This288
Document, dentro do projeto Recibog. Abre-se uma janela de código, vazia. Na caixa Geral, escolha Document. Automaticamente, o VBA cria um procedimento chamado Document_New. Nele escreva a linha para abrir o formulário. A rotina, completa, ficará assim: Private Sub Document_New() frmReciboGeral.Show End Sub
Comentários finais Aqui concluímos a apresentação deste projeto. O resto, como sempre, são detalhes espalhados aqui e acolá dentro do programa. A caixa txtValor, por exemplo, só aceita a digitação de números, ponto, vírgula e retrocesso. Isso é garantido pelo seguinte código, no evento KeyPress: Select Case KeyAscii Case 8, 44, 46, 48 To 57 ‘OK Case Else ‘ anula a digitação KeyAscii = 0 End Select
Mais detalhes. Quando o usuário abre o programa e aciona o botão Cancelar, fecha o formulário e também o arquivo do recibo que o contém. Na orelha Clientes, Recebedores e Serviços, quando o cursor do mouse passa em cima de um botão, aparece, à direita, uma explicação do que faz aquele botão. Essas explicações estão em rótulos ocultos (propriedade Visible=False). Quando o cursor está sobre o botão, um código no evento MouseMove desse botão torna visível o rótulo correspondente. Ao mesmo tempo, o evento MouseMove do objeto Multipágina torna invisíveis todas as quatro etiquetas com explicações. Assim, quando o cursor sai de cima do botão e cai na área da multipágina, a etiqueta desaparece. Esse recurso é interessante, mas apresenta um senão: em certos casos, quando se passa o cursor muito rapidamente sobre um botão, e logo depois, sobre outro, duas etiquetas ficam visíveis. Isso ocorre porque a área do controle multipágina é pequena entre os botões e o evento MouseMove não é disparado.
Para ir mais além 1. Uma das modificações óbvias que você pode fazer neste projeto é redesenhar o modelo do recibo para adaptá-lo ao seu gosto ou ao layout dos documentos de sua empresa. Aja livremente, preservando apenas as palavras-chave.
289
2. O armazenamento das listas de variáveis deste programa em arquivos
texto obedeceu à idéia de manter o projeto simples, resolvido apenas com recursos do próprio Word. No entanto, se você quiser, pode guardar essas variáveis em tabelas de bancos de dados. Outros projetos neste livro mostram como acessar bases de dados Access.
3. Uma das falhas do armazenamento de variáveis em arquivos texto é
mostrada no caso do arquivo Rtipserv.lst. Ele armazena dois tipos de listagem que precisam, obrigatoriamente, estar associados, dois a dois. Se essa condição falhar (por exemplo, o usuário digitar o título e não inserir a descrição correspondente na linha seguinte), o programa, obviamente, passará a operar com erro. E, como se trata de um arquivo texto, não há como evitar isso. A solução com banco de dados, sugerida no item 2, sanaria essa falha. De todo modo, o projeto, tal como é apresentado aqui, serve bem aos propósitos de pequenos escritórios e de usuários que têm o controle de seu próprio trabalho.
4. Em vez de formatar a data no código VBA e depois inseri-la no recibo, você pode deixar tudo a cargo do Word. Para isso, use o comando Inserir/Campo, categoria Data e Hora, nome de campo CreateDate. Para mais detalhes, veja o projeto Controle de Aluguéis, no Capítulo 30.
Arquivos de inicialização Os arquivos INI, uma herança do Windows 3.x, e o Registro do Windows representam recursos práticos para o armazenamento de variáveis de configuração que precisam ser “lembradas” entre uma e outra execução de um aplicativo. Sempre que você pensar em usar esses recursos, tenha em conta que eles impõem a criação de dois tipos de rotinas. Primeiro, um ou mais procedimentos para ler as configurações durante a inicialização do programa ou em outros momentos, quando necessário. Depois, outros procedimentos para dar ao usuário a oportunidade de modificar as definições gravadas.
290
32 Façam as suas apostas! Como construir um jogo de dados gráfico com três modalidades de apostas Ficha do projeto Projeto: Jogo gráfico Álea, baseado em dois dados e três modalidades de apostas O que faz: Oferece um painel gráfico com faces de dados em 3D. O jogador aposta em dois dados e escolhe um tipo de jogo. O programa apura e exibe os resultados conforme a modalidade escolhida. Arquivos e requisitos do projeto: Formulário frmDados e código interno. Conhecimento técnico: Trabalho com imagens e cálculo com números randômicos. Nível de programação: Intermediário
Em geral, quem gosta de computador tende a gostar, e muito, dos games eletrônicos. Nesse item sou uma exceção. Confesso que não tenho nenhuma paciência para os jogos – nem mesmo para o inevitável Paciência. Mas reconheço que a construção de jogos constitui um excelente exercício para programadores. Neste capítulo, vamos desenvolver um jogo de dados. Trata-se de uma adaptação de um programa que escrevi, há muito tempo, com o Visual Basic 1.0 ou 2.0. Primeiro, o problema. A idéia é criar um jogo no qual as apostas são feitas com dois dados. O jogador precisa ter uma forma de indicar as duas faces de dados em que deseja apostar. Feita a aposta, ele manda rolar os dados. O programa deve exibir os dados da banca, compará-los com a aposta e indicar o resultado. Os dados são o jogo de azar, por excelência. Portanto, o aplicativo deve incluir uma forma de tornar os resultados aleatórios, como num jogo real. Agora, a solução. Para representar os dados, vamos usar seis pequenos bitmaps (27´ 27 pixels) com imagens de dados em 3D, um para cada face. Cada um dos dois dados do jogo será representado por uma coleção desses seis bitmaps. Para apostar, o jogador usará o mouse e clicará numa face do dado 1 e numa face do dado 2 (veja a Figura 32.1).
FIGURA 32.1
292
A tela do jogo: três modalidades de apuração dos resultados
Para dar a idéia de conjunto, as imagens dos dois dados são mostradas dentro de molduras. Quando o jogador escolhe as faces desejadas no dado 1 e no dado 2, essas faces são copiadas para a área sob a legenda Sua Aposta. Assim, ficam visíveis os números em que ele apostou. Rolados os dados, o programa apresenta os números finais na área chamada Mesa da Banca. O resultado é exibido no painel Placar. Em Álea, cada partida está definida como uma seqüência de treze lances. Usei treze para acentuar o aspecto cabalístico do jogo, mas poderia também usar sete. O programa vai acumulando os resultados e, no final, encerra a partida, informando o percentual de acertos do jogador. A apuração é feita de três formas diferentes, conforme a modalidade de jogo escolhida na caixa de combi-
nação Modalidade. Há três: Comum (padrão), Ordem e Soma. Veja na tabela a seguir como funciona cada uma delas. Os números de cada lance, na aposta ou no resultados, são expressos como um par ordenado: (d1, d2). Ou seja, os números no dado 1 e no dado 2.
MODALIDADE
APURAÇÃO DOS PONTOS
Comum
Conta 1 ponto, não importa a ordem da aposta e do resultado. Se você apostar (1, 4), marcará 1 ponto se der (1, 4) ou (4, 1). Em comparação com as outras duas variantes, oferece nível médio de dificuldade.
Ordem
A marcação de 1 ponto positivo só ocorre quando o resultado da mesa reproduz a ordem dos dados da aposta. É a modalidade mais difícil do jogo.
Soma
O jogador conquista 1 ponto quando a soma dos dados da mesa é igual à soma dos dados da aposta. Por exemplo: (3, 4) na aposta e (6, 1) na mesa. É a variante mais fácil do jogo.
FIGURA 32.2
Desenho ampliado de uma das faces do dado
293
Desenho da interface Em primeiro lugar, você precisa ter as imagens dos dados. Elas são seis bitmaps desenhados com o Paint, do Windows. Para representar o dado 1, usamos uma moldura (fraDado1) e, dentro dela, seis objetos Image, nomeados seqüencialmente como imgDado11, imgDado12, e assim por diante, até imgDado16. De forma idêntica, criamos outra moldura (fraDado2), com seis imagens: imgDado21, imgDado22 etc. Aqui, um detalhe: para tornar o programa mais leve, carregamos apenas as imagens no dado 1. As imagens do dado 2 são copiadas durante a inicialização do programa. O carregamento do bitmap para um controle Image é feito mediante a definição da propriedade Picture. Para o dado 1, as imagens foram definidas, manualmente, na caixa de propriedades de cada objeto. Se fosse o caso, também poderiam ser configuradas em código. Por exemplo: imgDado11.Picture = “c:\meus documentos\dado1.bmp”
No dado 2, as imagens são cópias das existentes no dado 1. A cópia é feita igualando-se os valores da propriedade Picture dos dois objetos: imgDado21.Picture = imgDado11.Picture
F I G U R A 3 2 . 3 O formulário, mostrado pelo ponto de vista do
desenvolvedor
Na moldura que exibe os dados apostados (fraAposta), incluímos três imagens: imgAposta1, imgAposta2 e imgVazio. As duas primeiras recebem cópias das faces dos dados que representam a aposta do jogador. A última serve apenas como objeto auxiliar. Trata-se de um controle sem nenhuma imagem. Quando é preciso zerar o jogo (no final da partida ou por escolha antecipada do jogador), usa-se imgVazio para limpar as imagens da mesa da banca e da área de apostas. Assim: 294
imgAposta1.Picture = imgVazio.Picture
A moldura fraBanca abriga duas imagens de dados: imgBanca1 e imgBanca2. Por fim, os resultados são exibidos em outra moldura, fraPlacar. Nela estão seis caixas de texto, que exibem os acertos, os erros e o número de jogadas, em valores absolutos e em números percentuais. Essas caixas são txtAcertoA, txtAcertoP, txtErroA, txtErroP, txtTotalA e txtTotalP. Ainda na área dos resultados, encontra-se o botão de comando Zerar (cmdZerar), que limpa todos os resultados do placar e as imagens de Sua Aposta e da Mesa da Banca, preparando o programa para o início de nova partida. O botão Rolar os Dados (cmdRolar) dá início a todo o processo. Ou, para não perder o trocadilho, ele é que dispara o verdadeiro processamento de dados.
A lógica do programa Agora que já vimos o funcionamento do programa por fora, passemos para o lado de dentro. A primeira coisa que o jogador deve fazer é colocar sua aposta, clicando com o mouse numa face do dado 1 e numa face do dado 2. O programa precisa executar três ações com base nesse clique do jogador. n
armazenar a informação da aposta: qual face do dado 1 foi escolhida?
n
controlar se o usuário de fato já apostou nos dois dados. Isso é obrigatório, porque não será possível apurar o resultado se a aposta não estiver completa;
n
copiar a imagem escolhida para a área de aposta.
Para executar essas operações, criamos duas rotinas sub, CliqueDado1 e CliqueDado2. Elas recebem o número índice da imagem clicada e são chamadas a partir do evento Click do controle. A seguir, um exemplo que começa com o clique na face 2 do dado 1: Private Sub imgDado12_Click() CliqueDado1 2 End Sub Sub CliqueDado1(idx As Integer) ‘ Processa o clique em qualquer face do dado 1 blnClicouDado1 = True intAposta1 = idx imgAposta1.Picture = Controls(“imgDado1" & idx).Picture Me.Repaint End Sub
A rotina CliqueDado1 recebe o valor 2. Primeiro, marca como verdadeira a variável booleana blnClicouDado1, definida na área de declarações do formulário e, portanto, válida para todo o módulo. Outra variável válida para todo o formulário é intAposta1. Ela indica que o jogador apostou na face 2 do dado 1. 295
A propriedade Picture da imagem imgAposta1 é definida, usando-se a coleção Controls e concatenando o nome do controle escolhido: imgAposta1.Picture = Controls(“imgDado1" & idx).Picture
No exemplo, “imgDado1” & idx resultará em imgDado12. O método Repaint provoca o redesenho do formulário. Talvez não seja desnecessário, no caso, mas é interessante que fique aí. Em muitos casos de redefinição da propriedade Picture, a tela não se repinta e não se obtém o resultado visual desejado . Se o usuário não completar a aposta (ou seja, escolher somente uma face num dos dados) e clicar no botão Rolar os Dados, o processo será interrompido. O programa exibirá uma mensagem orientando o jogador. Esse controle é feito com o valor das variáveis booleanas blnClicouDado1 e blnClicouDado2. A ação só prossegue se ambas forem verdadeiras. Para que o lance de dados seja autêntico, é necessário obter, para cada um dos dois dados, um número randômico – ou seja, aleatório. Aqui cabe um pouco de etimologia. O jogo de dados é um dos mais antigos de que se tem notícia. É classificado como um jogo de azar, visto que depende exclusivamente da sorte, e não da estratégia ou experiência do jogador. Azar e sorte, aliás, são faces diferentes de um mesmo dado. A palavra azar, segundo os dicionaristas, provém do árabe yásara – termo que também está ligado ao jogo de dados. Em espanhol, azar significa um lance de dados malsucedido. Essa idéia de má sorte foi a que passou mais fortemente para o português. Mas azar também significa acaso. Ao longo da História, os dados têm sido o símbolo por excelência da casualidade. Não por acaso, alea, palavra latina, significa jogo de dados. Quando César cunhou a conhecida frase Alea jacta est, queria dizer que a sorte estava lançada. Mas, na verdade, o lance era de dados. De alea vem aleatório, adjetivo usado para indicar aquilo que não obedece a nenhuma regra predefinida, senão ao acaso. Ou ao azar, tanto faz. Aliás, também existe a palavra álea em português. Segundo o dicionário DicMaxi Michaelis Português, ela significa “sorte, risco, acaso”. Na linguagem Basic, a instrução Randomize, combinada com a função Rnd, gera números aleatórios. Em nosso caso, elas são utilizadas da seguinte forma: Randomize intDado1 = Int(6 * Rnd + 1) intDado2 = Int(6 * Rnd + 1)
Os valores obtidos para intDado1 e intDado2 são números inteiros randômicos, situados na faixa de 1 a 6 – os valores das faces de um dado. A próxima tarefa é comparar esses números com os números apostados pelo jogador. Para 296 isso, é preciso primeiro verificar qual a modalidade de jogo vigente e chamar a
rotina que implementa a forma específica de apuração. Há três rotinas de apuração: ApuraComum, ApuraOrdem e ApuraSoma.
Modalidade Comum Nos três casos de apuração, o algoritmo baseia-se na idéia de somar dois pontos, um para cada coincidência de valores dos dados. Assim, o resultado pode ser 0 (nenhuma coincidência), 1 (uma coincidência) ou 2 (duas coincidências). Esse número será passado ao placar e, naturalmente, o número 2 valerá um acerto, enquanto os outros marcarão erros. Em termos de código, a apuração da modalidade Comum é a mais enganosa. Não é longa nem complicada, mas exige atenção. Em princípio, basta verificar se os dois pares de dados são iguais, não importando a ordem. Ou seja, se a aposta é (2, 5) e a mesa produz (2, 5) ou (5, 2), em ambos os casos o jogador venceu. E como fica em código? If (intAposta1 Then Ponto = If (intAposta2 Then Ponto =
= intBanca1) Or (intAposta1 = intBanca2) _ 1 = intBanca1) Or (intAposta2 = intBanca2) _ Ponto + 1
Na modalidade Comum, basta verificar se cada número da aposta tem um correspondente no resultado da mesa. Isso funciona bem, na maioria das vezes, mas pode produzir um erro em casos como (4, 4) na aposta e (4, 3) na mesa. Comparado com a aposta, o primeiro 4 obtém um ponto. O segundo, também. Então, corre-se o risco de passar essa aposta como vencedora, quando, claramente, não é. Daí a necessidade de outro If: If Ponto = 2 Then If (intAposta1 = intAposta2) And _ (intBanca1 intBanca2) Then Ponto = Ponto - 1 End If
Assim, garante-se que uma aposta com dois números iguais (4, 4) só será vencedora se os dois números da mesa também forem iguais.
Modalidade Ordem A modalidade Ordem é a mais difícil de jogar, porém bem mais simples de apurar. Ela se resolve em uma linha. O dado 1 da aposta deve ser igual ao dado 1 da mesa. O dado 2 da aposta deve ser igual ao dado 2 da mesa. If (intAposta1 = intBanca1) And (intAposta2 = intBanca2) _ Then Ponto = 2
297
Modalidade Soma Na modalidade Soma, o código também não apresenta problemas. A soma dos pontos apostados deve ser igual à soma dos pontos rolados na mesa: If (intAposta1 + intAposta2) = (intBanca1 + intBanca2) _ Then Ponto = 2
Em termos estatísticos, cada modalidade de jogo apresenta um grau de dificuldade diferente. Refiro-me, aqui, não à programação, mas à probabilidade de ganhar do acaso em cada lance de dados. No modo Comum, o jogador tem 2 chances em 36, ou seja, 5,6 %. O modo Ordem, que é o mais difícil, oferece apenas uma chance em 36, ou 2,8 %. O modo Soma é o mais fácil, porque, embora sob o comando do acaso, admite certa esperteza do jogador. As chances nessa modalidade variam de 1 em 36 (2,8%) a 6 em 36 (16,7%). E é nessa variação que entra a malícia do jogador: ele ganhará mais se apostar sempre para o lados das maiores chances. Como? É matemático. O quadro abaixo mostra todas as possibilidades de resultados quando se jogam dois dados. Os pares de números representam os resultados no primeiro e no segundo dado: 1, 1
1, 2
1, 3
1, 4
1, 5
1, 6
2, 1
2, 2
2, 3
2, 4
2, 5
2, 6
3, 1
3, 2
3, 3
3, 4
3, 5
3, 6
4, 1
4, 2
4, 3
4, 4
4, 5
4, 6
5, 1
5, 2
5, 3
5, 4
5, 5
5, 6
6, 1
6, 2
6, 3
6, 4
6, 5
6, 6
F I G U R A 3 2 . 4 Resultados possíveis num lance de dois dados
298
A tabela acima representa o conjunto total de possibilidades em qualquer jogo com dois dados. Em outras palavras, esse é o espaço de apostas possíveis, não importa a modalidade do jogo. Se você observar bem, verá que essa tabela – na verdade, um quadrado, 6x6 – distribui, nas linhas paralelas à diagonal que vai do canto (6, 1) ao canto (1, 6), todas as jogadas vencedoras da modalidade Soma. Comece pela diagonal destacada na tabela: ali, a soma dos pares de números sempre dá 7. Portanto, se você apostar em qualquer arranjo de dados que some 7, terá seis chances de ganhar sobre um total de 36. Suba para a linha paralela (5, 1) – (1 ,5) e a soma baixa para 6, em cinco quadrinhos. Suba mais ainda:
a soma cai para 5, e assim por diante. Se você mantiver o paralelismo com a diagonal e descer, verá que o valor da soma aumenta, mas as chances serão as mesmas nas linhas eqüidistantes da diagonal. Resumo: as chances crescem quando se aposta em valores centrais e diminuem quando se caminha para as extremidades, sempre tendo como eixo a diagonal (6, 1) – (1, 6). Portanto, apostar na soma intermediária, 7, é sempre mais vantajoso que apostar em qualquer outra, de 2 (1, 1) a 12 (6, 6). Aqui, como em muitas outras situações da vida, a verdade está no meio. Ou, para ser mais matemático, na média. No mesmo quadro de possibilidades, você pode analisar as chances para os outros dois modos de jogo. No modo padrão, o Comum, também existe uma pequena variação de chances. Se você escolher qualquer par de números diferentes – digamos (2, 3) –, suas chances serão 2 em 36. No exemplo, elas são exatamente (2, 3) e (3, 2). Mas há seis casos especiais, todos localizados na outra diagonal do quadrado: (1, 1) – (6, 6). Isso significa que se você fizer apostas duplas (dados iguais), só terá uma chance em 36. O caso da modalidade Ordem é mais rígido: não admite variações. Em qualquer lance, as chances são absolutamente idênticas: 1 em 36. Bem, depois dessa breve incursão probabilística, voltemos ao código. Paramos na apuração do lance de dados. Agora, é preciso escrever no placar o resultado da apuração. Isso é feito pela rotina Sub EscreveResultado, que recebe como parâmetro o número de pontos calculado (0, 1 ou 2). Como já vimos, somente o valor 2 conta um ponto no placar. Os outros dois valores possíveis indicam que o jogador fez uma aposta errada. A rotina EscreveResultado vai acumulando o número de lances de cada partida. Quando esse número chega a 13, o jogo é interrompido e o resultado final é informado ao jogador, numa mensagem. A acumulação do número de lances, assim como os outros resultados, é mostrada em caixas de texto. Nesse caso, melhor seria usar o controle label, que apenas exibe a informação sem dar nenhum acesso ao usuário. No entanto, preferimos usar caixas de texto com a propriedade Locked=True. Isso significa que o jogador pode colocar o cursor na caixa de texto, mas não tem condições de editar seu conteúdo. Outra alternativa impediria até mesmo que o controle recebesse o foco: desligar a propriedade Enabled, fazendo-a igual a False. O defeito desse caminho é que o texto passa a ser mostrado numa cor cinzenta, desmaiada, típica dos controles que estão desativados. Isso conclui o que há para dizer sobre o código. Talvez valha a pena observar ainda alguns detalhes. A caixa de combinação Modalidade (cboModalidade) tem como propriedade Style a opção 2 – fmStyleDropDownList. Isso significa que o controle não aceita digitação: o usuário pode apenas escolher uma das opções da lista. Ainda sobre a caixa Modalidade: sempre que o jogador seleciona novo modo de apuração, o botão Zerar é acionado. Se isso não fosse feito, seriam computados resultados de dois ou mais tipos de jogos diferentes – um procedimento que não faz sentido. 299
Em Álea, para acentuar o grau de realismo do ambiente de jogo, todas as molduras que abrigam dados recebem um fundo verde. A intenção é imitar o tecido que cobre as mesas de jogo nos cassinos. A pintura de verde é definida na sub-rotina UserForm_Initialize. Para definir aquele verde, usei um valor em hexadecimal (&HC000&) copiado do ambiente da linguagem Visual Basic. Mas se você achar estranho esse valor, ele na verdade corresponde à seguinte cor RGB: Verde = RGB(0, 192, 0)
Para ir mais além Como sempre, aqui vão algumas idéias que você pode tomar como ponto de partida para melhorar o jogo de dados, ou mesmo construir outro mais sofisticado.
1. Você pode criar outras modalidades de jogo, além de Comum,
Ordem e Soma. Uma delas seria, por exemplo, o Pelo Menos Um. Por esse modo, contaria ponto em cada lance o jogador que acertasse, pelo menos, o valor de um dos dados. Para incluir novas modalidades, você precisa modificar apenas a lista na caixa Modalidade e a rotina cmdZerar_Click. Além disso, é claro, seria preciso criar mais uma rotina de apuração.
2. Ao desenvolver um algoritmo para apurar essa nova modalidade, aproveite para analisar as probabilidades usando o quadro mostrado neste capítulo.
3. O tipo de design que acabei encontrando para a tela do jogo me levou a
entender que não devia incluir um botão Fechar. Reconheço: esta não é uma decisão correta. Afinal, o jogo Álea é uma caixa de diálogo do Word. E todas as telas desse tipo têm um botão Fechar. Talvez você encontre um design mais adequado. Havia espaço, por exemplo, junto ao botão Rolar os Dados. Mas acredito que ficaria confuso deixar lado a lado o principal botão de operação do programa e o botão de encerramento. Uma eventual solução seria: a) deslocar o quadro Placar para baixo, alinhando-o com a mesa da banca; b) transferir o botão Rolar os Dados para cima; e c) incluir um botão Fechar, na posição antes ocupada pelo botão Rolar os Dados.
300
O segredo está na matriz Na rotina UserForm_Initialize do jogo Álea, você encontra um trecho assim: imgVazio.BackColor = Verde imgBanca1.BackColor = Verde imgBanca2.BackColor = Verde imgAposta1.BackColor = Verde imgAposta2.BackColor = Verde
Observe: os objetos são diferentes, mas a propriedade definida é a mesma: BackColor. Nesse caso, é possível transformar o código em algo menos repetitivo: Assim: Dim c as Variant For Each c In Array(imgVazio, imgBanca1, imgBanca2, _ imgAposta1, imgAposta2) c.BackColor = Verde Next
O segredo, aí, está na construção da matriz (Array) com os cinco controles. Atenção: eu falei os cinco controles, e não os nomes deles. Eis porque os nomes não estão entre aspas – não são strings. Para que esse truque funcione, é obrigatório que a variável c seja declarada como Variant. Conclusão: os elementos de uma matriz definida pela função Array podem ser listados com um loop For Each/Next, da mesma forma que os membros de uma coleção.
301
33 Não fique fora do prazo! Um calendário que faz operações com datas, e sabe contar sábados, domingos e feriados Ficha do projeto Projeto: PrazoCerto, calendário e calculador de datas, com ajuste de prazos O que faz: Usa um calendário gráfico como ponto de partida para efetuar cálculos com datas e prazos, nas seguintes modalidades: 1) dias corridos simples; 2) dias corridos com data final em dia útil; e 3) somente dias úteis. Nas modalidades 2 e 3, o aplicativo manipula um banco de dados de feriados, que pode ser ajustado pelo usuário. Usuários potenciais: Na parte do calendário, o interesse é geral. Nos cálculos de prazos, advogados, juízes, contabilistas e outros profissionais que definem ou controlam prazos em atividades jurídicas ou comerciais. 302
Arquivos do projeto: 1. Objeto-calendário (Mscal.ocx), do Office 97 ou do Office 2000 Professional. 2. Banco de dados Prazos.mdb (incluído no disco anexo a este livro). 3. Modelo Prazos.dot, que contém todo o código do projeto. 4. Formulários frmPrazoCerto, frmFeriados, frmNovosFeriados e frmCalConfig (embutidos em Prazos.dot) Conhecimento técnico: Inclusão e manipulação de objeto ActiveX num projeto VBA. Trabalho com múltiplos formulários (forms) num mesmo projeto. Programação de bancos de dados. Nível de programação Avançado
O objetivo deste projeto é, mais uma vez, lidar com operações de datas para calcular prazos. Mais especificamente, vamos criar uma ferramenta para calcular prazos legais e comerciais, levando em conta se as datas de início e término caem em finais de semana ou feriados. Para isso, vamos construir uma aplicação relativamente complexa, com um objeto ActiveX, um banco de dados Access e vários formulários. Antes de tudo, vamos esclarecer um detalhe fundamental: todos os formulários de Prazo Certo residirão num modelo. Portanto, comecemos por esse arquivo. No Word, crie um arquivo novo e salve-o como o modelo Prazos.dot. Em seguida, passe para o ambiente de programação do VBA (Alt+F11), localize o item Project (Prazos ) e selecione-o. Para criar os formulários, você deverá ir ao menu Inserir e acionar UserForm. Assim, os formulários serão salvos dentro do modelo Prazos.mdb. Passemos ao desenho do formulário principal, frmPrazoCerto. A visualização dos controles nesse formulário ajudará a entender a proposta geral do projeto. O formulário frmPrazoCerto contém os seguintes controles:
Objeto ActiveX Calendar (Cal) Tem duas finalidades. A primeira, obviamente, é um calendário para consulta. Aceita datas de janeiro de 1900 a dezembro de 2100. A segunda função é servir de instrumento para entrada de dados. O usuário não precisa digitar (e errar) datas: basta indicá-las com o mouse, clicando o dia no corpo do calendário e escolhendo mês e ano nas caixas de combinação, localizadas na parte superior do controle. A data indicada é, imediatamente, copiada para a caixa Data Inicial. Vale observar que esse objeto ActiveX faz parte do Office, 97 ou do 2000. Se você tem instalado apenas o Word, é possível que o calendário não esteja disponível em sua máquina. Então, será preciso recorrer ao utilitário de instalação do Office e acrescentar o calendário. 303
F I G U R A 3 3 . 1 O formulário frmPrazoCerto em tempo de
desenvolvimento
Etiquetas Data Inicial (labDataInicial), Data Final (labDataFinal) e Dia da Semana (labDiaDaSemana); caixa de texto Prazo (txtPrazo); botão Calcular Para calcular um prazo, o usuário indica uma data inicial no calendário e digita um prazo, em dias, na caixa Prazo. Depois, aciona o botão Calcular (cmdCalcular) ou a tecla Enter. O programa oferece um resultado nas etiquetas Data Final e Dia da Semana, que fica logo abaixo de Data Final. A primeira exibe a data calculada e a outra, o dia da semana correspondente. Os controles acima constituem o grupo básico que garante a operação do programa. Todos os outros desempenham funções complementares.
Caixas de verificação Final: Dia Útil (chkFinalDiaÚtil) e Só Dias Úteis (chkSóDiasÚteis) Esses controles dão ao usuário opções para modificar o cálculo do prazo. Final: Dia Útil, como o nome indica, pede que o prazo seja determinado de forma que o último dia seja útil. Ou seja, os sábados, domingos e feriados que estiverem dentro do período serão contados normalmente. Já a opção Só Dias Úteis faz a contagem sem incluir os fins de semana, nem os feriados.
Botões de opção Dia (optDia), Semana (optSemana) e Mês (optMes) Esses botões permitem calcular o prazo, tomando, como unidade, dias (o padrão), semanas e meses.
Botão de comando cmdHoje e etiqueta labHoje Abaixo do objeto-calendário, há ainda dois controles: o botão cmdHoje, sem legenda, faz o calendário exibir a data atual; e a etiqueta labHoje exibe a data atual, em formato extenso. Por exemplo, “segunda-feira, 11 de outubro de 1999”. 304
O botão sem legenda revela uma das fragilidades do VBA: você só enxerga a legenda se o botão tiver uma altura mínima de 18. Assim, todo botão de comando de pequena altura não pode ter a propriedade Caption. Para compensar, coloquei a palavra “Hoje” na propriedade ControlTipText desse botão. Assim, se você passar o mouse sobre ele, vai ter uma dica do que ele faz.
Botões de comando Ajuda (cmdAjuda), Copiar (cmdCopiar), Feriados (cmdFeriados), Configurar (cmdConfigurar) e Fechar (cmdFechar) Resta-nos, apenas, indicar a função desses cinco botões no topo do formulário. O botão Ajuda abre uma caixa de mensagem com um texto que orienta o usuário sobre como operar o aplicativo. O botão Copiar armazena, na área de transferência, a data final obtida num cálculo, no formato: “28/08/2000, segunda-feira”. Assim, o usuário pode colar a informação num texto do Word ou em outro documento. Para efetuar cálculos de prazos levando em conta os feriados, é preciso informar ao programa quando caem esses feriados. O botão Feriados abre outro formulário (frmFeriados), que exibe uma lista das datas cadastradas como feriados fixos (válidos em qualquer ano) e como feriados móveis (variam, conforme o ano). Esse form, por sua vez, permite que o usuário inclua no banco de dados novos feriados, fixos e móveis, o que é feito com o formulário frmNovosFeriados. Vem agora o botão Configurar. Clicado, ele abre o formulário frmCalConfig, cuja função é definir modificações no objeto calendário Cal. Por fim, o botão Fechar (cmdFechar) dispensa comentários.
Construção do formulário principal Esclarecidas as funções de cada objeto no formulário principal, podemos avançar agora para a construção desse formulário. Naturalmente, não será necessário mostrar, passo a passo, o desenho de cada objeto. O grande destaque, nesse formulário, é a inclusão de um objeto ActiveX, o calendário.
F I G U R A 3 3 . 2 O formulário frmPrazoCerto, em operação
305
Acione Inserir/UserForm para criar o formulário. Ajuste-o para o tamanho que achar mais adequado e, na caixa de propriedades, mude o nome dele para frmPrazoCerto. Veja, agora, como incluir o calendário. Clique no formulário para garantir que o foco está nele. Deve aparecer a Caixa de Ferramentas. Se isso não acontecer, vá ao menu e acione Exibir/Caixa de Ferramentas. Agora, clique com o botão direito do mouse nessa caixa e escolha Controles Adicionais. Abre-se nova janela, na qual você deve localizar e selecionar o item Calendar Control 8.0 – ou Controle Calendário 8.0, conforme o idioma do Office.
F I G U R A 3 3 . 3 Ativação do controle calendário
O ícone do Calendário aparece na Caixa de Ferramentas. Clique nele e desenhe um controle no formulário. Ajuste o tamanho do calendário para que os nomes dos meses e as datas fiquem bem visíveis. Se você usa o Office 97, vai notar que existe um pequeno bug na caixa de combinação onde estão listados os meses. O mês de maio é apresentado como “mai”, sem o “o” final. Alguns controles, como Data Inicial e Data Final, são etiquetas cuja cor de fundo é igual à cor do formulário. Isso foi adotado para estabelecer um contraste com a cor branca da caixa Prazo. Assim, o usuário já percebe, visualmente, que é possível digitar em Prazo, mas as informações nos outros controles são apenas para leitura.
O código É hora de passar ao código que executa o cálculo dos prazos. Na rotina UserForm_Initialize, definem-se alguns padrões: 306
Me.Caption = “Operações com Datas” intUnid = 1 Cal.Today ‘ ou: Cal.Value=Date AtualizaDataInicial labHoje = Format$(Date, “Long Date”) txtPrazo.SetFocus
Primeiro, o título do formulário – que também poderia ser definido na caixa Propriedades. Depois, a variável intUnid, referente à unidade do prazo (1 para dia, 2 para semana e 3 para mês. Em seguida, força-se o calendário a exibir a data atual. Isso pode ser definido pelo método Today ou pela propriedade Value. Portanto, as duas linhas abaixo são equivalentes: Cal.Today Cal.Value = Date
Definida a data no calendário, é preciso que a label DataInicial também exiba essa data. Para isso chama-se a rotina AtualizaDataInicial. Essa rotina resolve-se em, apenas, uma linha: labDataInicial = Format$(Cal.Value, “dd/mm/yyyy”)
Por fim, coloca-se o foco em txtPrazo, para que o usuário possa digitar um número. Ao evento KeyPress (pressionamento de tecla) de txtPrazo está associado um código que só aceita a digitação de números, do sinal menos (–) e do botão de retrocesso. Isso diminui as possibilidades de erro. Vale lembrar que a definição da propriedade ValueIsNull do calendário para o valor False, nos eventos NewMonth e NewYear, é de fundamental importância. Se isso não for feito, o objeto calendário ficará sem valor quando o usuário escolher um mês ou um ano nas caixas de combinação. Também é útil observar que não funciona tentar eliminar a linha Cal.ValueIsNull = False daqueles dois procedimentos e colocá-la, por exemplo, na sub UserForm_Initialize. Muito bem. O usuário escreve um prazo e clica no botão Calcular. O que acontece? O procedimento básico de cálculo é a função CalculaPrazo, que recebe a data inicial, o prazo e a unidade de prazo e devolve a data final. Em essência, essa função soma a data inicial ao prazo. Isso é obtido mediante a função intrínseca DateAdd. Exemplo: DataFinal = DateAdd(“d”, n, DataInicial)
O argumento “d” indica que o número n – o prazo – é expresso em dias. Assim, no exemplo, n dias são somados à DataInicial para se obter DataFinal. Nesse caso, em que a unidade é dia, a operação poderia ser simplificada para:
307
DataFinal = DataInicial + n
Vale lembrar que, se n for negativo, a data final será anterior à final. Em outras palavras, tem-se aí uma subtração. Todo o cerne do problema resume-se a essa operação. Só que, na prática, não é tão simples. E o que complica são as opções. Aqui, é importante discutir, com mais detalhes, os critérios de ajuste da data final usados para cada opção. A opção Final: Dia Útil é estabelecida como padrão. Assim, se a data calculada cair num fim de semana ou num feriado, deverá ser empurrada para o primeiro dia útil à frente. Ao mesmo tempo, o prazo deve sempre ser contado a partir de um dia útil. Por exemplo, se hoje é dia 3 e o prazo é de cinco dias, então a data final será o dia 8. Isso é óbvio, mas atenção para o detalhe: se o dia 4, o primeiro da contagem, não for útil, o início do prazo deve ser transferido para o próximo dia. Se este também não for útil, o primeiro dia do intervalo vai sendo deslocado. Exemplo: se 3 é sexta-feira, o dia 1 do prazo deverá ser a próxima segunda-feira. Se esta for feriado, terça-feira. A sucessão de testes, no início e no fim do prazo, constitui o verdadeiro busílis desse cálculo. Há ainda outro complicador, que surge quando o usuário escolhe a opção Só Dias Úteis. Nesse caso, além de evitar fins de semana e feriados no início e no fim da contagem, esses dias devem ser desconsiderados também no meio do intervalo considerado. Vamos acompanhar, passo a passo, o trabalho da função CalculaPrazo. Primeiro, ela define a string correspondente à unidade de prazo escolhida pelo usuário: “d” para dia; “ww” para semana; e “m” para mês. Essa string é armazenada na variável sIntervalo. Em seguida, executa o cálculo para opção Só Dias Úteis. Aqui, a estratégia é a seguinte. Para começar, determina-se qual é o dia 1 do prazo. A partir dele, testa-se cada dia para saber se é especial (sábado, domingo ou feriado). Coloca-se em ação um contador, que vai acumulando os dias não especiais. Para cada dia não especial, soma-se 1 ao contador. Quando esse contador for igual ao prazo, chegamos ao final. Para a opção Final: Dia Útil, faz-se o teste para o primeiro dia e depois aplica-se a soma normal data + prazo. Por fim, checa-se o resultado para garantir que caia num dia útil. Em todas esses cálculos, falamos mais de uma vez em checar se a data cai num dia especial, ou seja, fim de semana ou feriado. Como isso é feito? Duas funções fazem esse teste: E_DiaEspecial e E_Feriado. Como foi dito no início, precisaríamos de um banco de dados para armazenar os feriados. Para isso, criamos, no Access, o arquivo Prazos.mdb, que contém duas tabelas: tabFeriadosFixos e tabFeriadosMóveis. Feriados fixos, só para lembrar, são aqueles que ocorrem todos os anos na mesma data. São oito no ano inteiro:
308
FERIADO FIXO
DATA
1
Confraternização Universal
1o de janeiro
2
Tiradentes
21 de abril
3
Dia do Trabalho
1o de maio
4
Independência
7 de setembro
5
N.Sra. Aparecida
12 de outubro
6
Finados
2 de novembro
7
República
15 de novembro
8
Natal
25 de dezembro
A estes feriados fixos, o usuário deve acrescentar os feriados estaduais ou municipais válidos em sua região. No Estado de São Paulo, por exemplo, deve-se incluir o 9 de Julho, data da Revolução Constitucionalista de 1932. Já os usuários baianos deverão incluir no banco de dados o 2 de Julho, data em que se comemora a independência da Bahia (1823). Os feriados móveis, como o nome sugere, mudam ano a ano e caem em datas calculadas com base em eventos astronômicos e religiosos. A data básica é a Páscoa, festa cristã que comemora a ressurreição de Jesus e cai no primeiro domingo após a primeira lua cheia, a contar de 21 de março. O domingo de Carnaval e a festa de Corpus Christi, outros feriados móveis, caem, respectivamente, 49 dias antes e 60 dias depois da Páscoa. Portanto, são três feriados móveis a cada ano: Carnaval (terça-feira), Sexta-Feira Santa (a sexta-feira antes do domingo de Páscoa) e Corpus Christi. O cálculo dessas três datas é bastante complexo e não está incluído neste projeto. No banco de dados Prazos.mdb você encontra todos os feriados fixos nacionais e todos os móveis, de 1995 até 2005. Para usar o programa adequadamente, você precisa incluir os feriados locais de sua região. Assim, dada uma data, a função E_DiaEspecial verifica se: n
é sábado;
n
é domingo;
n
é feriado fixo ou feriado móvel.
Para isso, E_DiaEspecial conta com a ajuda da função E_Feriado, que se encarrega do terceiro item, acima. O teste para sábado e domingo é muito simples. Basta verificar se o dia da semana corresponde a 1 (domingo) ou a 7 (sábado): If intDia = 1 Or intDia = 7 Or E_Feriado(umaData) Then E_DiaEspecial = True
309
A função E_Feriado, por sua vez, abre o banco de dados Prazos.mdb e verifica se a data consta na tabela tabFeriadosFixos ou na tabela tabFeriadosMóveis. Todo o resto, digamos assim, são detalhes de acabamento do programa. Eis alguns exemplos: Quando o usuário escolhe a opção Só Dias Úteis e, em seguida, define a unidade de prazo como semana ou mês, automaticamente a caixa Só Dias Úteis é desligada. Seria muito demorado executar o cálculo com combinação dessas opções. Observe que a label labHoje não acompanha as mudanças de data que o usuário define no calendário. Ela mostra, sempre, a data atual. Quando a data final obtida foi empurrada para a frente porque no primeiro cálculo caiu em fim de semana ou feriado, o dia da semana, abaixo da data, exibe um asterisco. A opção Final: Dia Útil é padrão. Se o usuário a desligar, o cálculo será feito de modo simples. Ou seja: o número de dias corridos entre a data inicial e a final. Quando se liga a opção Só Dias Úteis, ativa-se também, automaticamente, a opção Final: Dia Útil. Do mesmo modo, ao se desligar a opção Final: Dia Útil, desliga-se também a outra. O botão Ajuda apresenta uma caixa de mensagem com um texto que resume as orientações necessárias para operar o programa:
F I G U R A 3 3 . 4 Ajuda: todo o texto numa caixa de mensagem
Merece destaque a rotina associada ao clique no botão Copiar. Ela mostra como armazenar informações na Área de Transferência do Windows (Clipboard). Observe o trecho de código a seguir. Primeiro, cria-se um objeto strCopy, pertencente à classe DataObject. Em seguida, define-se um texto para esse objeto, com o uso do método SetText. No caso, o texto é uma combinação do conteúdo da label Data Final com o da label associada, labDiaDaSemana. Por fim, 310 usa-se, então, o método PutInClipboard a fim de copiar o texto para a memória.
Dim strCopy As DataObject Set strCopy = New DataObject sClipboard = labDataFinal & “, ” & strDia strCopy.SetText sClipboard strCopy.PutInClipboard Set strCopy = Nothing
Formulário frmFeriados Quando o usuário clica no botão Feriados, no formulário principal, abre o formulário frmFeriados. Este tem duas tarefas básicas a cumprir. A primeira é exibir as listas dos feriados, fixos e móveis, cadastrados no banco de dados Prazos.mdb. A segunda é permitir que o usuário cadastre novos feriados.
F I G U R A 3 3 . 5 As tabelas de feriados: fixos e móveis
A estrutura desse formulário é bastante simples. Consta apenas de duas caixas de listagem e três botões. As caixas, Fixos (lstFixos) e Móveis (lstMoveis), recebem as listas dos feriados cadastrados. Os botões são Fechar (cmdFechar), Novo Fixo (cmdNovoFixo) e Novo Móvel (cmdNovoMóvel). No evento de inicialização do formulário, entram em ação os procedimentos ListaFixos e ListaMóveis, que abrem o banco de dados Prazos.mdb, lêem as tabelas tabFeriadosFixos e tabFeriadosMóveis e preenchem, no formulário, as caixas de listagem lstFixos e lstMóveis. Com isso, frmFeriados cumpre sua primeira tarefa. A outra tarefa é permitir a inclusão de novos feriados. Para isso, deve entrar em cena o terceiro formulário do projeto, frmNovosFeriados. Este servirá para cadastrar tanto feriados fixos como móveis. Mas há um porém: no banco de dados, as tabelas para esses tipos de datas têm estruturas diferentes. De fato, tabFeriadosFixos tem três campos: Dia, Mês e Descrição, como mostrado a seguir. 311
Dia
Mês
Descrição
1
1
01/01: Confrat. Universal
1
5
01/05: Dia do Trabalho
2
11
02/11: Finados
A outra tabela, tabFeriadosMóveis, organiza-se com apenas duas colunas: Feriado
Descrição
07/03/2000
Carnaval
21/04/2000
Paixão
22/06/2000
Corpus Christi
Por causa dessas diferenças, é preciso avisar ao formulário frmNovosFeriados qual o tipo a ser cadastrado, a fim de que ele ofereça os campos adequados. Então, quando o usuário clica no botão Novo Fixo, antes de abrir o formulário frmNovosFeriados, define-se o valor de uma variável que avisa sobre a intenção de cadastrar um feriado fixo. Processo idêntico ocorre quando o usuário clica no botão Novo Móvel. Há dois caminhos para passar essa informação de um formulário para o outro. O tradicional seria criar um módulo de programação e lá declarar três variáveis públicas: Public Const FER_FIXO As Integer = 1 Public Const FER_MOVEL As Integer = 2 Public p_intTipoFeriado As Integer
No procedimento associado ao clique no botão Novo Fixo, antes de abrir o formulário frmNovosFeriados, seria definido o valor de p_intTipoFeriado. Assim: p_intTipoFeriado = FER_FIXO
No clique do botão Novo Móvel, a variável assumiria o outro valor: p_intTipoFeriado = FER_MOVEL
Desse modo, como p_intTipoFeriado teria abrangência pública, bastaria, ao abrir o outro formulário, verificar se essa variável teria o valor de feriado fixo (FER_FIXO) ou de feriado móvel (FER_MOVEL). 312
Em termos de lógica e funcionamento, essa solução está perfeita. No entanto, os especialistas em programação aconselham que se evite usar variáveis globais, sempre que possível. Variáveis que criam vínculos de um módulo para outro acabam tornando o código mais difícil de ler e de atualizar. O ideal é que cada módulo funcione como uma entidade independente. Veja, então, uma solução mais moderna. Para fugir das variáveis públicas, vamos lançar mão de um recurso de orientação a objeto, tal como implementado no Visual Basic. A idéia é criar uma propriedade a mais para o formulário frmFeriados. Chamemos essa propriedade de TipoFeriado. Para criá-la, faça o seguinte. Primeiro, na área de declarações do formulário, dimensione a variável m_TipoFeriado: Dim m_TipoFeriado As Integer
Agora, inclua no módulo do formulário um procedimento Property Get: Property Get TipoFeriado() As Integer TipoFeriado = m_TipoFeriado End Property
Para ter certeza de que criou a propriedade, basta ir à janela Imediata e digitar o nome do formulário, seguido de um ponto. Você vai ver que na lista pop-up de métodos e propriedades aparece TipoFeriado. O valor dessa propriedade é definido quando o usuário clica num dos botões Novo Fixo ou Novo Móvel. Lá, m_TipoFeriado assume o valor 1 ou 2. Resultado: tudo se mantém, internamente, no formulário. Ao mesmo tempo, a propriedade pode ser lida de fora, por causa do procedimento Property Get.
Formulário frmNovosFeriados A primeira tarefa do formulário frmNovosFeriados é verificar o valor da propriedade TipoFeriado do formulário que o chama, frmFeriados. Isso é feito da seguinte maneira. Primeiro, são declaradas duas constantes: Const FER_FIXO As Integer = 1 Const FER_MOVEL As Integer = 2
Em seguida, os testes são feitos contra os valores dessas constantes: If frmFeriados.TipoFeriado = FER_MOVEL Then
Conforme o valor, o formulário muda de cara, para acomodar a entrada de dados para a tabela tabFeriadosFixos ou para a tabela tabFeriadosMóveis. Seu título também muda: “Novo Feriado Fixo” ou “Novo Feriado Móvel”. Além disso, na hora de salvar as informações digitadas, há dois procedimentos: SalvarNovoFixo e SalvarNovoMóvel. 313
Pode-se objetar que a construção de uma propriedade para o formulário frmFeriados não é a melhor solução nesse caso. Correto. Aproveitei o exemplo como uma oportunidade para mostrar esse recurso. Mas ele de fato não é o melhor momento para a inserção de uma propriedade, criada pelo programador. Outra solução, mais tradicional, seria a seguinte. No form frmFeriados, o clique no botão Novo Fixo ou Novo Móvel já definiria o título (e, portanto, a configuração) do form frmNovosFeriados. Assim: Load frmNovosFeriados frmNovosFeriados.Caption = “Novo Feriado Fixo” frmNovosFeriados.Show
A primeira linha carrega, mas não exibe, o formulário frmNovosFeriados. A segunda define um título para esse formulário: “Novo Feriado Fixo” ou “Novo Feriado Móvel”. A terceira, por fim, exibe-o. Agora, no evento de inicialização do formulário, a configuração da tela será definida em função do título: If Me.Caption = “Novo Feriado Fixo” Then
F I G U R A 3 3 . 6 O formulário, para feriados fixos
F I G U R A 3 3 . 7 O mesmo formulário, para feriados móveis 314
O formulário frmNovosFeriados contém três caixas de texto (txtDiaData, txtMês e txtDescrição) e dois botões de comando: Salvar (cmdSalvar) e Cancelar (cmdCancelar). Quando o objetivo é cadastrar um feriado fixo, as três caixas de texto aparecem, normalmente, com os rótulos Dia, Mês e Descrição. Quando o feriado é móvel, opera-se a metamorfose: oculta-se a caixa de texto Mês e a caixa Dia recebe o rótulo Data, ficando mais comprida e descendo para a posição da caixa Mês. Na hora de salvar a informação digitada pelo usuário, executa-se um teste para verificar a validade da data. Depois de registrada a nova informação no banco de dados, a lista de feriados correspondente no formulário Feriados é atualizada. Aqui há dois pontos a observar. Primeiro: para atualizar as caixas de lista no outro formulário, usam-se procedimentos que também estão lá: frmFeriados.lstFixos.Clear frmFeriados.ListaFixos
Isso não acontece por acaso. Observe que as sub-rotinas ListaFixos e ListaMóveis são registradas como procedimentos públicos. Somente assim elas poderiam ser chamadas a partir de outro módulo. O outro ponto refere-se também a essas rotinas. No formulário frmFeriados, elas poderiam muito bem ser reunidas numa só. Mas, assim, quando se incluísse um novo feriado fixo, seria necessário atualizar as duas caixas. Com as rotinas separadas, atualiza-se somente a lista afetada. O quarto formulário do projeto, frmCalConfig, não introduz nenhum tipo de modificação nos procedimentos de cálculo. Ele tem apenas função cosmética. Seu objetivo é definir como o objeto-calendário se apresenta. Especificamente, se ele exibe o domingo (que é o padrão) ou a segunda-feira como o primeiro dia da semana. Essa configuração é definida mediante a propriedade FirstDay do objeto: frmPrazoCerto.Cal.FirstDay = vbMonday
Observe, aí, a presença das constantes intrínsecas do Visual Basic: vbSunday, vbMonday, vbTuesday, correspondentes aos números dos dias da semana: 1 a 7. No controle calendário, a semana pode começar em qualquer dia. No entanto, o formulário frmCalConfig oferece apenas duas opções: domingo e segunda-feira. Algumas pessoas preferem os calendários com a segunda-feira como dia inicial porque, nesse layout, os dias dos fins de semana ficam providencialmente juntos. A estrutura de frmCalConfig é bem enxuta. Tem dois botões de comando, OK (cmdOK) e Cancelar (cmdCancelar), e dois botões de opção, Domingo (opt1) e Segunda-feira (opt2). Além de definir a forma de apresentação do calendário, esse formulário também armazena no Registro do Windows a configuração escolhida pelo usuário. Assim, na próxima vez que ele abrir o projeto, o calendário será exibido no padrão antes selecionado. Tudo isso é feito no evento associado ao botão OK do formulário: 315
F I G U R A 3 3 . 8 Tela para configuração do calendário Dim intDia As Integer If opt1.Value = True Then intDia = vbSunday ‘ domingo Else intDia = vbMonday ‘ segunda-feira End If If frmPrazoCerto.Cal.FirstDay intDia Then frmPrazoCerto.Cal.FirstDay = intDia SaveSetting “WordCalendar”, “Configs”, _ “DiaInicial”, Trim$(Str$(intDia)) End If Unload Me
O que ocorre aqui? Primeiro, captura-se o dia inicial escolhido pelo usuário. Depois, verifica-se se ele não é o que já está aplicado ao calendário. Em caso negativo, modifica-se a propriedade FirstDay do calendário e armazena-se a informação no Registro do Windows, com a ajuda da instrução SaveSetting: SaveSetting “WordCalendar”, “Configs”, _ “DiaInicial”, CStr(intDia)
A sintaxe para essa instrução é a seguinte: SaveSetting aplicação, seção, chave, valor
O parâmetro aplicação indica o nome do programa. Em nosso caso, colocamos WordCalendar. Portanto, cria-se no Registro uma entrada com esse nome. Dentro dela, cria-se também um nome de seção (“Configs”) e, dentro dessa seção, uma chave (DiaInicial) e o seu valor – aqui representado pelo número do dia da semana escolhido. Se houvesse outras configurações, o procedimento seria idêntico. Bastaria mudar o nome da chave e, naturalmente, o valor. As informações ficam guardadas na chave HKEY_CURRENT_USER\Softwa316 re\VB and VBA Program Settings\WordCalendar\ Configs.
F I G U R A 3 3 . 9 O Registro: onde fica armazenada a
configuração do calendário
Para que o calendário exiba a configuração armazenada no Registro, o formulário frmPrazoCerto, que abriga o calendário, precisa saber qual é essa configuração. Por isso, no procedimento de inicialização desse formulário há algumas linhas de código marcadas com a observação de que estão ligadas ao form frmCalConfig. Elas são as seguintes: Dim sDiaIni As String ‘ Lê o arquivo Calendar.ini sDiaIni = GetSetting(“WordCalendar”, _ “Configs”, “DiaInicial”, “1") Cal.FirstDay = Val(sDiaIni)
Aqui entra em ação a função GetSetting, que faz o papel inverso de SaveSetting. Ela envia os parâmetros aplicação, seção e chave e recebe de volta o valor da chave. O quarto parâmetro – “1”, no exemplo – é opcional e funciona como padrão. Ou seja, se não existir a informação, a função retorna “1”. Esse número foi escolhido porque é também o valor-padrão do calendário. Um detalhe: as informações enviadas ao Registro ou lidas nele com SaveSetting e GetSetting são sempre seqüências de caracteres. Daí o “1”, entre aspas. Agora, falta apenas um pequeno toque para fechar a aplicação. Você se lembra: construímos um programa dentro de um arquivo modelo. Então, é bom que, quando o usuário dê um duplo clique nesse arquivo, o programa se abra. Como se obtém isso? É simples. Na janela de projeto, observe que, além dos formulários, o projeto Prazos exibe o item ThisDocument. Dê um duplo clique nele. Abre-se a janela do módulo ThisDocument. Nela, clique na caixa Geral e depois na opção Document. O VBA cria, automaticamente, o espaço para o procedimento Document_New. O que você incluir nesse procedimento deverá acontecer toda vez que for criado um novo documento com base no modelo Prazos.dot. Como toda a aplicação está contida em três formulários, vamos então comandar para que seja aberto o formulário principal. Então, a rotina, com apenas uma linha, fica assim:
317
Private Sub Document_New() frmPrazoCerto.Show End Sub
Missão cumprida. Agora, sempre que você criar um novo documento a partir de Prazos.dot, abrirá, imediatamente, o formulário frmPrazoCerto, com o calendário e a interface para o cálculo de prazos.
Para ir mais além 1. Prazo Certo é uma aplicação genérica e, portanto, pode ser executada
em qualquer aplicação do Office. Ou, melhor ainda, em qualquer aplicação que suporte as versões do VBA do Word 97 ou do 2000. Você pode experimentá-la no Excel, por exemplo. Para isso, no ambiente do VBA, selecione os três formulários da aplicação (um de cada vez) e exporte-os (Arquivo/Exportar Arquivo) para o disco. Eles produzirão arquivos FRM, com os mesmos nomes dos módulos: frmPrazoCerto.frm, frmFeriados.frm e frmNovosFeriados.frm. Para cada um desses arquivos, haverá também um homônimo com a extensão FRX. Os arquivos FRX contêm informações binárias sobre o form. No Excel, faça a operação inversa: use o comando Arquivo/Importar Arquivo para incluí-los numa planilha. Detalhe: a única linha de código exclusiva do Word é ActiveDocument.Close
na rotina cmdFechar_Click. Elimine essa linha e você terá uma aplicação prontinha para rodar no Excel. No Office 2000, você pode fazer o mesmo para o Access e para o PowerPoint.
2. Ao clicar no botão Fechar do formulário principal, você fecha o
aplicativo e, ao mesmo tempo, o documento que lhe dá suporte. Agora, a título de teste, clique no outro botão Fechar, no canto superior direito do formulário. O que ocorre? O formulário se fecha, mas o documento que o contém, não. Deixei essa brecha no aplicativo para que você, enquanto o estuda, possa fechar o formulário principal sem fechar, também, o documento (mas, para isso, não esqueça: clique no canto do formulário). Se quiser fechar formulário e documento nos dois botões de saída, retire a linha ActiveDocument.Close do procedimento cmdFechar_Click e transfira-a para UserForm_QueryClose.
3. Como Prazo Certo entrega ao usuário um banco de dados com todos os
318
principais feriados fixos do país e todos os feriados móveis até 2010, entendi que a interface de acesso ao banco de dados deveria ser mínima e dar direito apenas a consulta e, em raros casos, à inclusão de novas datas. Você pode mudar isso, criando formulários que permitam visualizar e atualizar os feriados, da forma que achar mais interessante.
4. O controle Calendário tem muito mais métodos e propriedades que os
mostrados neste projeto. Para estudar melhor esse objeto, selecione-o no formulário e acione a tecla F1. Será aberto um arquivo de ajuda com todas as informações sobre o objeto.
5. Também é interessante acessar a página de propriedades do controle
Calendário. Selecione esse controle no formulário e, na caixa Propriedades, clique na linha Personalizado. Em seguida, clique no botão que tem três pontos como legenda. Surgirá a caixa mostrada abaixo.Ela mostra muitas das propriedades do calendário.
F I G U R A 3 3 . 1 0 Caixa de propriedades do objeto calendário
6. Agora que você já conhece as propriedades do calendário, modifique o
formulário frmCalConfig para dar ao usuário outras opções de configuração do objeto. Por exemplo, mudar cores e fontes dos nomes dos dias e meses; ou exibir ou não as caixas de combinação com os anos e meses.
7. Remodele o formulário frmPrazoCerto e arranje lugar para incluir dois
novos botões com a finalidade de ir para o mês anterior ou para o próximo. Os métodos são muito simples: Cal.NextMonth e Cal.PreviousMonth. Se quiser, também pode fazer o mesmo para saltar anos: Cal.NextYear, CalPreviousYear.
8. Atenção: criei os primeiros esboços iniciais deste projeto com aplicativos do Office 97 – primeiro com o Access e, depois, com o Word. Nessa versão, na caixa de combinação ao alto do calendário, Maio aparecia como “Mai”, enquanto os outros meses exibiam a grafia por extenso. O problema foi corrigido na versão 2000.
319
Caçando com gato perdigueiro... Você certamente já viu: em alguns programas, quando se vai escrever uma data numa caixa de texto, abre-se um calendário para o usuário indicar a data com o mouse, em vez de digitá-la. Aquilo exige um objeto específico para mostrar o calendário pop-up ou muita programação com a API do Windows. Esses recursos não estão ao nosso alcance. Mas podemos fazer um quebra-galho. Veja como.
F I G U R A 3 3 . 1 1 Os formulários: o segundo aparece quando
se digita nas caixas Data1 e Data 2
Crie um formulário chamado frmPrincipal, que deve conter, entre outros objetos, uma ou mais caixas de texto, representando campos de data. Acrescente, em seguida, outro formulário, frmCalendário. O form frmPrincipal tem vários controles, mas nos interessam somente as caixas de texto txtData1 e txtData2. Para facilitar a comunicação entre os dois forms, vamos agregar nova propriedade para frmPrincipal. Essa propriedade se chamará CampoData e indicará qual campo de data foi acionado para chamar o segundo formulário. A criação dessa propriedade é dada pelos seguintes passos. Primeiro, declare a variável m_CampoData: Dim m_CampoData As Integer
Depois, adicione esta rotina ao código do formulário:
320
Property Get CampoData() As Integer CampoData = m_CampoData End Property
Agora, na rotina correspondente ao evento KeyPress das caixas de texto txtData1 e txtData2, escreva: m_CampoData = 1 frmCalendário.Show
Naturalmente, em txtData2, m_CampoData deve ser igual a 2. Tudo pronto no primeiro form. Passemos a frmCalendário. Este deve ter apenas o controle Calendário. Ao evento Click desse objeto faça corresponder a seguinte rotina: Dim i As Integer i = frmPrincipal.CampoData frmPrincipal.Controls(“txtData” & i) = Calendar1.Value Unload Me
Demonstração concluída. Abra frmPrincipal e comece a digitar qualquer coisa numa das caixas de data. O que acontece? Ao acionar a primeira tecla, o calendário aparece, com o título “Indique a data no calendário”. Escolhida uma data, esta é transferida para o objeto a partir do qual o calendário foi chamado e o formulário frmCalendário se fecha. Não é a mesma coisa que o objeto calendário pop-up, mas vale a pena. Quem não tem cão caça com gato. Para arredondar esta demonstração, colocamos os dois formulários dentro do modelo Calendar.dot. Abra o modelo para ver como o projetinho funciona.
Anos bissextos Depois de toda a celeuma em torno do ano 2000 e seu famigerado bug, agora ninguém mais tem dúvida: o ano 2000 é bissexto. Mas como se verifica isso com um código em Visual Basic? Há várias formas de fazer esse cálculo. A primeira atém-se à definição, estabelecida pelo Calendário Gregoriano, de 1582: bissexto é todo ano múltiplo de 4, exceto os múltiplos de 100 que não sejam, também, múltiplos de 400. Exemplos: 1800, 1900, 2100 não são; mas 2000 e 2400 são bissextos. Uma função que aplica essa definição é dada por: Function AnoBissexto(Ano As Integer) As Boolean If (Ano Mod 4 = 0 And Ano Mod 100 0) _ Or (Ano Mod 400 = 0) Then AnoBissexto = True End If End Function
321
Como essa definição já está embutida no calendário interno do VBA, pode-se armar uma outra função ainda mais simples, com apenas uma linha: Function AnoBissexto1(Ano As Integer) As Boolean AnoBissexto1 = IsDate(“29/2/” & CStr(Ano)) End Function
Neste caso, a função intrínseca IsDate checa se existe o dia 29/02 do ano dado. Se existir, o ano é bissexto. Uma terceira opção usa a função DateSerial, partindo da idéia de que o dia 29/02 de um ano não bissexto recai em 01/03: Function AnoBissexto2(Ano As Integer) As Boolean AnoBissexto2 = DateSerial(Ano, 2, 29) _ DateSerial(Ano, 3, 1) End Function
O primeiro do três algoritmos acima tem a vantagem de ser universal. Pode, portanto, ser empregado, com as devidas adaptações, em qualquer linguagem de programação. Os outros dois são específicos do Visual Basic.
322
34 Mago de casa também faz milagre Crie, você mesmo, um Assistente para extrair informações de bancos de dados Ficha do projeto Projeto: Assistente de Bancos de Dados O que faz: Este assistente (wizard) guia o usuário, passo a passo, para abrir um banco de dados Access qualquer e extrair dele as informações que desejar. As informações são apresentadas como tabela do Word ou planilha do Excel. Portanto, este projeto envolve o Word com dois outros integrantes do pacote Office. Arquivos e requisitos do projeto: 1. Os objetos de acesso a dados têm de estar instalados no micro e ativos no Word. 2. Modelo Assistdb.dot (incluído no disco anexo a este livro). 3. Arquivos de banco de dados Access (MDB) para testar o assistente.
Conhecimento técnico: Desenho e programação de formulários (forms) e controles. Molduras para exibir e ocultar objetos. Programação de bancos de dados. Controle lógico da interface do programa. Nível de programação: Avançado
O Assistente de Banco de Dados é o último e mais complexo projeto deste livro. A maior dificuldade que ele apresenta não está exatamente no nível da programação, que é mais ou menos similar ao que você viu até agora. O grande problema, aqui, reside na lógica necessária para acompanhar a estrutura de uma aplicação que tem, apenas, um formulário, mas funciona como se tivesse seis. Antes de avançar para a construção do projeto, vamos caracterizar o problema. O que é um assistente? É um programa que ajuda o usuário a executar determinada tarefa, guiando-o, passo a passo, por um caminho lógico. Devido a esse caráter de programa “facilitador”, a Microsoft chamou o assistente original de wizard – palavra inglesa para mago e, por extensão, especialista, mestre ou autoridade num assunto, um ás, um gênio. Especificamente, a mágica dos assistentes consiste em dar ao usuário um rumo ou roteiro para a execução de uma operação qualquer. Do ponto de vista da interface, os assistentes em geral são aplicações de uma única tela. Na verdade, mesmo os mais simples têm várias janelas, mas fazem o usuário acreditar que existe apenas uma. Afinal, o objetivo é transformar a tarefa em algo fácil e rápido. Nosso Assistente de Banco de Dados contém apenas um formulário com seis passos lógicos, cada um representado por uma “página” – camada de objetos que só são exibidos no momento adequado. Veja na Figura 34.1 a página inicial do Assistente.
324
F I G U R A 3 4 . 1 Tela de abertura do Assistente de Bancos de Dados: passo 1
Basicamente, o programa vai auxiliar o usuário a realizar a seguinte lista de tarefas:
1. Localizar e abrir um banco de dados Access. 2. Escolher, no banco de dados, a tabela da qual os dados serão extraídos. 3. Selecionar, na tabela, os campos em que estiver interessado. 4. Montar um filtro para extrair somente os registros desejados (opcional). 5. Classificar o registro por um ou mais campos (opcional). 6. Escolher o destino dos dados: tabela Word ou planilha Excel. Observe que esta série obedece a uma lógica que vai do geral para o particular. O número de cada tarefa é também o número da página que a representa. Até agora você deve estar perguntando o que vem a ser essa “página”. Vamos esclarecer. Visto por dentro – não na perspectiva do usuário, mas na do programador –, o formulário do Assistente (frmAssistente) contém, resumidamente, os seguintes objetos: Os botões na barra inferior (veja a Figura 34.1) e molduras (frames) na área que fica acima do traço horizontal. As molduras, que são seis, correspondem às “páginas” do wizard. Elas ficam superpostas, todas na mesma posição. No passo 1, mostra-se a moldura 1 e todas as outras permanecem ocultas. No passo 2 é revelada a moldura 2, enquanto as outras são subtraídas da vista do usuário. E assim por diante. Em todos os passos, os botões da barra inferior continuam os mesmos. A Ajuda é associada ao contexto. Se, no passo 3, o usuário clicar no botão Ajuda, obterá informações específicas sobre a tarefa 3. O botão Avançar só permite passar para a etapa seguinte se a atual já tiver sido completada. No passo 1, por exemplo, nada acontece se o usuário clicar no botão Avançar, sem antes ter indicado um banco de dados. Para que ele indique o banco de dados com que deseja trabalhar, o assistente exibe a caixa de diálogo Abrir, do Word (Figura 34.2). Aqui vale colocar um parêntese importante. Se você estiver trabalhando com o Word 2000, tudo bem. A versão do motor de bancos de dados correspondente ao Office 2000 é a 3.6 (Microsoft DAO 3.6 Object Library). Se, no entanto, você usar o Word 97, a versão dessa biblioteca é a 3.5. A questão é: programas que usam um motor mais antigo não conseguem abrir bancos de dados baseados em versão mais nova. Portanto, se alguém lhe passa um banco de dados do Access 2000, você não consegue abri-lo com o Access 97. Para evitar esse problema, todos os bancos de dados que acompanham este livro foram criados com a versão 97. Assim, poderão ser manipulados, sem problemas, pelos programas da série 97 e da série 2000. O botão Voltar dá liberdade ao operador para revisitar uma etapa já cumprida e modificar as escolhas que fez: incluir mais um campo, modificar a lógica do filtro e até indicar outro arquivo MDB. 325
F I G U R A 3 4 . 2 O passo 2 do assistente: escolha da tabela de
dados
326
Agora, vamos pôr a mão na massa. Primeiro, a construção do formulário. No ambiente de programação do VBA, comece desenhando um formulário com os quatro botões inferiores. Os nomes desses botões são: cmdAjuda, cmdVoltar, cmdAvançar e cmdCancelar. Acima deles, coloque a linha divisória horizontal. Essa linha é, na verdade, uma caixa de imagem (controle Image). Para ter a aparência de um sulco escavado no formulário, essa caixa precisa ter a seguinte propriedade: SpecialEffect=2. Acima da linha divisória, coloque a primeira moldura e chame-a de fra1. Em seguida, desenhe fra2, fra3 etc. Para que a moldura não apareça, é necessário ajustar duas de suas propriedades: SpecialEffect=0 e Caption="" (texto vazio). Aqui você vai precisar de alguns truques. É fácil notar que não há espaço nem jeito para trabalhar com um frame ocupando o mesmo espaço do outro. Além disso, sempre vai existir o risco de, em vez de criar um frame independente, você o colocar dentro de outro. Então, invente um espaço extra: aumente a altura do formulário até ficar, mais ou menos, duas vezes a altura do assistente. Depois, clique num dos botões da linha inferior do assistente e acione, no menu, a opção Exibir/Ordem de Tabulação. Observe que só estão listados, nessa janela, os botões do painel de comando do assistente e as molduras que você já desenhou. Não aparece nenhum dos objetos desenhados dentro das molduras. Agora, uma experiência diferente. Clique numa moldura, deixe-a selecionada e acione outra vez o comando Exibir/Ordem de Tabulação. O que aparece ? Somente os controles que estão dentro do frame selecionado. Essa característica nos dá uma pista interessante. Em condições normais, quando você torna uma moldura invisível, todos os objetos dentro dela assumem o mesmo estado. Quando você a exibe, os objetos reaparecem – a não ser, é claro, que você os programe para ficarem ocultos. Na página 1 do Assistente de Banco de Dados, a etiqueta (label) labArquivoMDB, por exemplo,
pertence ao frame fra1 e fica invisível até quando o usuário indica um arquivo de banco de dados. Muito bem. Mas voltemos ao formulário com a altura ampliada. O espaço adicional, na parte inferior de frmAssistente, vai servir para criar novas ou abrigar molduras antigas para serem editadas. Para fazer uma moldura antiga descer para essa área de trabalho, faça o seguinte. Admita que todas as molduras estão superpostas na parte superior, onde deverão ficar. Você precisa editar fra2, e ela não é a que se encontra visível. Desenhe um controle qualquer – por exemplo, um botão de comando – mais ou menos na posição em que vai ficar a borda superior da moldura. Veja, na caixa Propriedades, o valor da linha Top para esse botão. Anote-o e apague o botão (tecla Del com o objeto selecionado). Agora, clique num dos botões da linha inferior e acione a tecla Tab algumas vezes até selecionar o frame desejado (verifique qual objeto está selecionado na linha Name da caixa Propriedades, do ambiente VBA). Volte à caixa de propriedades e digite, para fra2, o valor Top do botão apagado. A moldura desce e você pode trabalhar com ela mais à vontade. Concluída a edição, devolva a moldura para o lugar, recuperando o valor antigo de sua propriedade Top. No Assistente, todas as molduras têm o mesmo tamanho e posição: ou seja, as mesmas propriedades Width, Height, Left e Top – comprimento, altura e coordenadas esquerda e superior.
F I G U R A 3 4 . 3 Aumentar o formulário: um truque para trabalhar
com várias páginas do assistente
A lógica do Assistente Como já foi dito anteriormente neste capítulo, a maior dificuldade que se pode encontrar ao fazer um assistente desse tipo está em combinar as exigências da in- 327
terface com a programação. A seguir, devo destacar alguns pontos-chave desse aspecto que estão presentes no Assistente de Bancos de Dados. O primeiro item a ressaltar é a manutenção da ordem das tarefas. Sem que a etapa atual esteja satisfatoriamente cumprida, o usuário não pode avançar para a seguinte. Como garantir isso? O controle é feito a partir do evento Click do botão Avançar. Para isso, usamos a variável m_iFrameAtual, de alcance geral no formulário, uma função chamada Mover, que faz a troca de página, e a função blnPodeAvançar, que verifica a possibilidade de prosseguir para o próximo passo. A variável m_iFrameAtual contém, em cada momento, o número da moldura ativa. Se m_iFrameAtual é menor que 6 – número da última etapa –, sempre é possível avançar. Possível, em termos. A função blnPodeAvançar verifica a viabilidade do avanço para cada frame. Se ela dá o sinal verde, então, a página ativa passa a ser a seguinte e chama-se a função Mover, que esconde todas as molduras, menos a atual. Quando se atinge o último passo, 6, o botão Avançar muda de legenda: transforma-se em Concluir. Agora, se o usuário clicar nesse botão Avançar-Concluir, chama a sub-rotina ConcluirTudo, que cuida de ler no banco de dados as informações indicadas e passá-las a uma tabela no Word ou a uma folha de cálculo do Excel. Parte do que está descrito aqui encontra-se na sub-rotina Click do botão Avançar-Concluir: Private Sub cmdAvançar_Click() ‘ Mostra a próxima página do assistente If m_iFrameAtual = 6 Then ConcluirTudo If m_iFrameAtual < 6 Then If blnPodeAvançar(m_iFrameAtual) Then m_iFrameAtual = m_iFrameAtual + 1 Mover m_iFrameAtual End If End If If m_iFrameAtual = 6 Then cmdAvançar.Caption = “Concluir>” End Sub
Como os botões Avançar e Voltar estão ligados a todas as fases de atuação do Assistente, o código a eles associado forma um eixo de referência. No evento Click do botão Voltar, tudo é mais simples: Private Sub cmdVoltar_Click() ‘ Controla o retorno à página anterior
328
If m_iFrameAtual > 1 Then m_iFrameAtual = m_iFrameAtual - 1 Mover m_iFrameAtual
cmdAvançar.Caption = “Avançar>” End If End Sub
Não existem impedimentos para o retorno a uma etapa anterior. Troca-se o valor de m_iFrameAtual e põe-se a página anterior em primeiro plano. Apenas um cuidado: ao sair da página 6 para a 5, é preciso dar ao botão Concluir a etiqueta original: Avançar. Vamos, agora, acompanhar o código do Assistente de Bancos de Dados, página a página.
Página 1 Na página 1, ao clicar no botão Procurar Banco de Dados (cmdProcurar), o usuário traz à tela a caixa de diálogo Abrir, do Word, ajustada para mostrar arquivos do tipo MDB. Indicado o arquivo, o Assistente abre o banco de dados (rotina AbrirBancoDeDados), verifica as tabelas nele existentes e as coloca numa caixa de lista da página 2. Como essa etapa já está cumprida, o programa chama o evento cmdAvançar_Click, a fim de passar à tarefa seguinte. Private Sub cmdProcurar_Click() ‘ Captura nome do arquivo MDB Dim dlg As Dialog Dim sDir As String Set dlg = Dialogs(wdDialogFileOpen) dlg.Name = “*.mdb” If dlg.Display = -1 Then m_sArqNomeCurto = dlg.Name ‘ elimina aspas, se houver If Right(m_sArqNomeCurto, 1) = Chr(34) Then m_sArqNomeCurto = Mid(m_sArqNomeCurto, 2, _ Len(m_sArqNomeCurto) - 2) End If ‘ Elimina a barra (\), se houver sDir = CurDir If Right$(sDir, 1) = “\” Then _ sDir = Left$(sDir, Len(sDir) - 1) m_sArqNomeLongo = sDir & “\” & m_sArqNomeCurto labArquivoMDB.Visible = True labArquivoMDB = “ ” & m_sArqNomeLongo labTitulolstTabelas = “Tabelas em ” & m_sArqNomeCurto AbrirBancoDeDados 329
‘ Passa para a página 2 cmdAvançar_Click End If End Sub
No código associado ao clique no botão Procurar, dois pontos devem ser destacados. O primeiro é que a caixa Abrir, do Word, é apresentada com o método Display. Esse método exibe a caixa de diálogo – qualquer uma – mas não permite executar nenhuma ação. Em outras palavras, o arquivo selecionado na caixa Abrir não vai ser aberto. Portanto, o método Display transforma a caixa de diálogo num instrumento de escolha e configuração de itens, e não de ação. Para realmente levar a termo a função da caixa de diálogo, use o método Show.
F I G U R A 3 4 . 4 Caixa Abrir (Word 2000): para exibir somente
arquivos do tipo MDB
O outro ponto de destaque é a maneira de capturar o diretório no qual se encontra o arquivo indicado pelo usuário. A propriedade Name da caixa de diálogo fornece apenas o nome do arquivo, sem o diretório, e não existe outra propriedade que forneça o caminho completo do arquivo. A saída existe, se você prestar atenção para um detalhe. Ao navegar na caixa de diálogo Abrir para encontrar o arquivo desejado, o usuário está definindo quem é o diretório corrente para o Word. Esse diretório é dado pela função CurDir, do Visual Basic. Há também outros caminhos para ele, que é usar uma das expressões: Options.DefaultFilePath(wdDocumentsPath) Options.DefaultFilePath(wdCurrentFolderPath)
Essa mesma expressão (Options.DefaultFilePath) retorna diferentes diretórios. Para isso, basta trocar as constantes intrínsecas do Word wdDocumentsPath e 330 wdCurrentFolderPath. Por exemplo, wdUserTemplatesPath fornece o diretório onde
são armazenados os modelos, inclusive o Normal; wdProgramPath diz onde está o arquivo executável do Word; e, por fim, wdProofingToolsPath indica a pasta que armazena as ferramentas de correção ortográfica. Na sub-rotina AbrirBancoDeDados, também convocada à ação a partir do clique no botão Procurar, usa-se a instrução For Each/Next para percorrer toda a coleção de TableDefs e listar todas as tabelas existentes no banco de dados escolhido. Observe, no entanto, que a coleção de tabelas do banco de dados inclui uma série de objetos internos do Access, todos com nomes iniciados com MSys, como MSysModules e MSysObjects. São as chamadas tabelas de sistema. Para que elas não apareçam na caixa de listagem lstTabelas, exclui-se todo objeto cujo nome comece com a expressão “MSys”. Dim td As TableDef Set m_db = opendatabase(m_sArqNomeLongo) lstTabelas.Clear ‘ Exclui as tabelas de sistema For Each td In m_db.TableDefs If Left(td.Name, 4) “MSys” Then lstTabelas.AddItem td.Name End If Next td
Em certo sentido, programar é prestar atenção a detalhes. No código acima, antes de listar as tabelas, limpa-se o conteúdo da caixa de listagem lstTabelas com o método Clear. Por quê? Isso evita que o usuário, depois de avançar nas operações, volte ao passo 1 e escolha um banco de dados diferente do primeiro. Ao fazer isso, novas tabelas vão ser listadas na caixa lstTabelas. Sem limpar o conteúdo da caixa, novas e velhas tabelas seriam listadas, gerando confusão e erro. Esse cuidado de fazer uma limpeza prévia de caixas de listagem e caixas de combinação também é adotado em outros momentos do programa.
Página 2 A página 2 exibe a lista das tabelas existentes no banco de dados escolhidos pelo usuário. Quando o usuário escolhe um item na lista, entra em ação a sub-rotina LerCamposNaTabela, que se encarrega de preencher a caixa lstCamposDisponíveis (passo 3) com os nomes dos campos. Aqui, a validação da passagem à etapa seguinte é muito simples. Basta verificar se uma tabela foi escolhida na lista. Ou seja, se o valor de lstTabelas é diferente de uma string vazia: If lstTabelas.Value “” Then blnPodeAvançar = True 331
O trecho de código acima foi retirado da função blnPodeAvançar – que, como vimos, é o centro de validação para a passagem de uma página à próxima.
Página 3 Na página 3, a ação se concentra nos botões de comando inseridos na moldura. Quatro deles – cmdVaiUm, cmdVaoTodos, cmdVoltaUm e cmdVoltamTodos – ocupam-se da tarefa de movimentar itens da lista lstCamposDisponíveis para a lista lstCamposSelecionados. Mover um campo da esquerda para a direita (cmdVaiUm) significa adicioná-lo à caixa de destino e removê-lo da caixa de origem. A operação inversa, com o botão cmdVoltaUm, obedece à mesma lógica. Naturalmente, a transferência só ocorre se houver um campo selecionado.
F I G U R A 3 4 . 5 Escolha dos campos que o usuário deseja
importar
332
Para auxiliar a operação dos botões de transferência total (cmdVaoTodos e cmdVoltamTodos), a sub-rotina LerCamposNaTabela armazena os nomes dos campos disponíveis na matriz TheFields( ), válida para todos os procedimentos do formulário. Portanto, a ação de mover todos os campos, para a direita ou para a esquerda, resume-se em apagar a caixa de origem e preencher a caixa de destino com os itens guardados na matriz. No troca-troca de campos entre as caixas Campos Disponíveis e Campos Selecionados, falta apenas destacar um aspecto. Trata-se do elo deste passo, 3, com o seguinte. Todo item adicionado à lista Campos Selecionados é também adicionado às seis caixas de combinação Campo1, Campo2 até Campo6, da moldura fra4. Do mesmo modo, todo item subtraído da lista deve também ser subtraído das caixas de combinação. Isso faz a ligação entre os passos 3 e 4. Os campos selecionados são também transferidos nessa rotina para as caixas de combinação da página 5.
Os dois outros botões da página 3 chamam-se cmdCampoSobe e cmdCampoDesce. Esses controles permitem redefinir a ordem dos campos na lista lstCamposSelecionados. A ordem ali definida será aquela com que os campos serão apresentados no documento final – ou seja, a tabela do Word ou a planilha do Excel. Uma dica de design: as setas para cima e para baixo nos botões cmdCampoSobe e cmdCampoDesce correspondem, respectivamente, aos caracteres ñ e ò (N minúsculo com til e O minúsculo com acento grave), na fonte Wingdings, padrão do Windows, desde a versão 3.1. Pontas de seta para a esquerda/direita e cima/baixo também podem ser obtidas com os caracteres 3, 4, 5 e 6 da fonte Marlett, que também é padrão no Windows 95, 98, NT e 2000. Outra alternativa para pontas de seta – nesse caso ainda mais vistosas – é oferecido pela fonte Wingdings 3, com os caracteres P, Q, T e U, todos minúsculos. Resta apenas saber se essa fonte tem a mesma universalidade das outras duas. Fazer um campo subir uma posição na lista significa apagá-lo do lugar onde está e inseri-lo, de novo, uma linha acima. É isso que faz o procedimento associado ao evento Click do botão cmdCampoSobe: Private Sub cmdCampoSobe_Click() Dim n As Integer Dim sTxt As String With lstCamposSelecionados n = .ListIndex If n > 0 Then sTxt = .Value .RemoveItem n .AddItem sTxt, n - 1 .ListIndex = n - 1 End If End With End Sub
Observe, no código, o uso da instrução With/End With, evitando a repetição da referência ao objeto lstCamposSelecionados. Dentro dos limites dessa instrução, tudo que aparece precedido por um ponto corresponde a uma propriedade de lstCamposSelecionados ou a um método aplicado a esse objeto. A rotina que faz o campo descer é idêntica. Por fim, o requisito básico para a passagem da página 3 para a 4 é que a caixa Campos Selecionados não esteja vazia. Ou, em código: lstCamposSelecionados.ListCount > 0 333
Página 4 A etapa número 4 é, talvez, a mais complexa do projeto. Ela oferece ao usuário uma interface na qual ele pode indicar critérios para a seleção de um subconjunto dos registros armazenados no banco de dados. Em linguagem mais técnica, nessa etapa o usuário tem a opção de criar uma consulta para trazer do banco de dados somente os resultados dessa consulta. Ele pode querer listar, por exemplo, somente os clientes das cidades de São Paulo e do Rio de Janeiro. O uso dos controles nessa etapa é idêntico ao empregado na manipulação de dados nas malas diretas do Word. Aliás, o jeitão dessa página foi copiado de lá. O objetivo é criar linhas lógicas como: E/OU OU
Campo: Cidade Cidade
Comparação: Igual a Igual a
Comparar Com: São Paulo Rio de Janeiro
Atenção para as conjunções E/Ou. “E” indica uma condição que pode ser aplicada simultaneamente ao mesmo registro: “o cliente é do sexo masculino E mora em Salvador”. Neste exemplo, o E faz sentido. No entanto, “o cliente mora no Rio E mora em São Paulo” produz um resultado vazio, porque certamente a ficha do cliente só apresenta um local de residência. Portanto, para reunir no resultado da consulta os clientes do Rio e de São Paulo, use OU. Nos bastidores, o Assistente transformará as duas linhas acima numa declaração SQL do tipo: SELECT * FROM WHERE Cidade=’São Paulo’ OR Cidade=’Rio de Janeiro’
F I G U R A 3 4 . 6 Passo 4: aqui, o usuário dá indicações para a
filtragem dos dados
334
Se o usuário quiser importar todos os registros do banco de dados, basta clicar no botão Avançar, sem preencher nenhum campo. Se preencher algum e quiser anular o preenchimento, basta clicar no botão Limpar. No início, apenas o primeiro campo da tabela se mostra ativado. À medida que o usuário os vai preenchendo, os campos seguintes também se tornam disponíveis. O sinal verde para a página seguinte é dado após duas seqüências de testes. A primeira testa, digamos assim, aspectos burocráticos. Ou seja, se o preenchimento dos dados foi feito corretamente. Então, o teste é positivo se: 1) nenhum dos campos foi preenchido; ou 2 ) pelo menos a primeira linha do quadro está completamente preenchida: Campo, Comparação e Comparar Com. O segundo teste é mais complicado. Ele verifica se o preenchimento faz sentido do ponto de vista do banco de dados. Para isso, é convocada a função TestaRecordset, que retorna o número de registros existentes no conjunto de dados formado pelas indicações do usuário. Se esse número for zero, não adianta prosseguir. O programa avisa ao usuário e não avança para o passo seguinte.
F I G U R A 3 4 . 7 Aviso ao usuário: não há registros
Caso o teste seja positivo, o usuário passará à etapa 5 e lá verá o número de registros encontrados pela pesquisa. A complexidade da etapa 4 reside no fato de que as indicações feitas são a base para a montagem da cláusula WHERE – a parte das declarações SQL que indica o critério de seleção dos registros.
Página 5 O preenchimento dos três campos da página 5 é opcional. As caixas cboOrdem1, cboOrdem2 e cboOrdem3 permitem classificar os registros segundo os campos nelas escolhidos. O usuário pode escolher de zero a três campos. O botão Limpar anula escolhas feitas anteriormente. O avanço a partir da página 5 é completamente livre, porque nessa etapa o usuário não define nenhum item obrigatório.
335
F I G U R A 3 4 . 8 Passo 5: escolha de campos para classificação
dos registros importados
Página 6 Chegamos, enfim, à última página. Aqui, o usuário precisa apenas indicar se o documento de saída para os dados será uma tabela do Word (o padrão) ou uma planilha do Excel e clicar no botão Concluir, também conhecido como Avançar.
F I G U R A 3 4 . 9 A página final do Assistente: escolha do
documento de saída
Aqui, as coisas começam, realmente, a acontecer. O clique no botão Concluir põe em ação a rotina ConcluirTudo. Esta importante senhora, sem exagero, é a alma do Assistente. Vejamos o que ela faz:
1. Chama a função MontaSQL. Esta lê os campos selecionados no passo 3 336
e, por sua vez, chama a função MontaWhere. Esta última constrói a cláusula WHERE e a fornece a MontaSQL. No final, MontaSQL reúne
tudo e agrega a cláusula ORDER BY, que define a classificação dos registros. Desse emaranhado de operações, resulta que MontaSQL fornece a ConcluirTudo uma declaração SQL completa. Exemplo: SELECT NomeCliente, Empresa, Endereço, CEP, Cidade, Estado FROM WHERE Cidade=’São Paulo’ OR Cidade=’Rio de Janeiro’ ORDER BY NomeCliente, Empresa
Com o comando SQL em mãos, a rotina ConcluirTudo abre o banco de dados e retira de lá o que está indicado. Conforme o documento de saída escolhido pelo usuário, os registros resultantes são direcionados para o Word ou para o Excel.
Saída para o Word No caso padrão (Word), ConcluirTudo lança mão de um conhecido recurso do processador de texto: converter texto em tabela. Para isso, ela lê o valor de cada campo no registro do banco de dados e acumula tudo numa string, colocando sempre, entre um campo e outro, um espaço de tabulação (constante intrínseca vbTab). No final da linha, coloca-se um sinal de parágrafo (vbCrLf). Cada uma dessas linhas é escrita num documento Word, antes vazio. No final, ConcluirTudo fecha o banco de dados e passa à formatação da tabela: ActiveDocument.Select Selection.ConvertToTable
O Word consome algum tempo na operação, mas afinal exibe uma tabela formatada, com os nomes dos campos no cabeçalho das colunas. Tarefa cumprida para o Assistente.
F I G U R A 3 4 . 1 0 Tabela final, transferida do banco de dados
Northwind.mdb para o Word
337
Durante a criação do documento, observe a frenética atividade do Word indicada na barra de status. As mensagens na barra de status são produzidas por comandos como: StatusBar = “Registro ” & nLinha & “...”
Saída para o Excel Falta-nos ver o que acontece quando o usuário define que o documento final é uma planilha Excel. Nesse caso, depois de verificar se a sentença SQL produz algum resultado, a poderosa rotina ConcluirTudo passa a bola para o procedimento ExportarParaXL. Este recebe a declaração SQL e entra em atividade. Ela abre o banco de dados, seleciona os registros segundo os critérios definidos na declaração e salva-os num arquivo TXT do tipo separado por vírgula, chamado Xlimport.txt. Trata-se de um arquivo com o seguinte formato: “Cliente”,"Empresa","Endereço","CEP","Cidade","Estado" “Antonio Gomes”,"Sol e Mar","Rua da Horta, 12","21213-009", “Santos”,"SP" “Maria Alves”,"Alves & Cia.","Rua Nova, 17","01213-000", “Campos”,"RJ"
Fechado o banco de dados, ExportarParaXL abre o objeto Excel.Application e comanda que ele importe o arquivo TXT e o salve no formato de planilha, com o nome Xlimport.xls. Mais uma vez, tarefa cumprida.
F I G U R A 3 4 . 1 1 Tabela final: do Northwind.mdb para o Excel
Note que foram usadas estratégias diferentes para tratar com o Word e o Excel. A solução com a planilha é, sensivelmente, mais rápida. Boa parte do tempo consumido na geração da tabela no Word destina-se a escrever os regis338 tros linha a linha no documento e, depois, transformá-lo em tabela. No caso do
Excel, a gravação do arquivo texto é bastante rápida e a importação, idem. Além disso, a tabela não precisa ser construída.
Detalhes finais Basicamente, isso é tudo que precisa ser dito a respeito do Assistente de Bancos de Dados. No entanto, sempre sobram alguns detalhes que demandam esclarecimento. Ao listar os campos de dados na rotina LerCamposNaTabela, deixamos de fora, de propósito, os campos do tipo Binário Longo (Objeto OLE) e Memorando. O primeiro refere-se a imagens, sons e demais itens de multimídia. Os outros, aos conhecidos campos de memorando, que nos bancos de dados do Access 2000 suportam textos de até 1,2 gigabytes. Mesmo com esse cuidado de afastar potenciais fontes de erro, o Assistente de Bancos de Dados pode gerar importações incorretas até em campos de texto comuns. Isso ocorre quando o conteúdo do campo contém um parágrafo – caractere ASCII 13. Veja um exemplo no banco de dados Northwind, do Access, no campo Endereço (Figura 34.12).
F I G U R A 3 4 . 1 2 O campo Endereço, que contém quebra de
linha, pode causar erro no Assistente
Outro detalhe refere-se às matrizes de controles. Uma das diferenças entre o produto Visual Basic – o pacote autônomo da linguagem – e o VBA – embutido em aplicativos – são as matrizes de controles. No VB, pode-se ter, por exemplo, cinco caixas de texto, todas com o mesmo nome e índice diferente: txtCampo (1), txtCampo(2) etc. Esse recurso facilita bastante a construção do código, quando, por exemplo, se deseja apagar o conteúdo de todas as caixas de texto: For n = 1 to 5 txtCampo(n) = “” Next n
339
No VBA isso não é possível. Pode-se, no entanto, inventar um quebra-galho. Retomando o exemplo, você pode nomear as caixas de texto com nomes seqüenciais: txtCampo1, txtCampo2, e assim por diante. Numa rotina localizada dentro de um módulo de formulário, o código VB, acima, pode assumir o seguinte perfil em VBA: For n = 1 to 5 Controls(“txtCampo” & n).Value = “” Next n
O truque consiste em recorrer à coleção Controls, do form, que reúne todos os controles existentes no form. Os objetos desejados – no caso, as caixas de texto – são indicados pela concatenação da string inicial do nome, “txtCampo”, com o número final. Essa pequena malandragem é usada em vários procedimentos do Assistente do Banco de Dados. Quem não tem cão caça com gato. E, de repente, o bichano se revela um ótimo perdigueiro... Outro ponto de destaque no código está na função FiltraApostrofo. Essa função, válida somente para strings, recebe o valor de um campo de dados e verifica se ele contém um apóstrofo – o sinal de aspa única. Em caso positivo, troca-o por dois apóstrofos e devolve a string recebida com essa modificação. Para quê? Quando se montam comandos SQL, é comum encontrar trechos do tipo: strSQL = “... WHERE Cidade=’São Paulo’ ... ”
As aspas únicas são usadas para delimitar o valor fornecido (São Paulo) . Na atribuição da variável strSQL, servem também para não criar confusão com as aspas comuns, delimitadoras da string total. Mas as aspas únicas produzem um erro quando a string que elas abraçam já contém um apóstrofo. É o caso de expressões em inglês como Baby’s Paradise e Alfredo’s. Nesse caso, o valor do campo analisado pelo motor de banco de dados é algo assim: ‘Alfredo’s’
O erro se configura porque o VBA não consegue analisar onde a string acaba. Para eliminar a ambigüidade, troca-se o sinal interno por dois: ‘Alfredo’’s’
Para o analisador de SQL, os dois sinais juntos valem por um. Dá para entender, agora, o significado da linha a seguir: varValor = “’” & FiltraApostrofo(varValor) & “’”
340
Trata-se de uma linha que modifica o valor de um campo de texto antes de integrá-lo a uma declaração SQL. Observe que a variável original varValor recebe um apóstrofo inicial e outro final e, ao mesmo tempo, é submetida à função FiltraApostrofo para corrigir a eventual existência de um ou mais apóstrofos internos. Observe, também, o trabalho da função GetRecordsetTemp. Ela recebe uma consulta (QueryDef) e verifica se existem registros que correspondam a essa consulta. Em outras palavras, se o número de registros é maior que zero. Se a pesquisa proposta pelo usuário não trouxer de volta nenhum registro, então encerra-se todo o processamento e emite-se um aviso: “não há registros correspondentes ao filtro indicado”.
Para ir mais além Você pode fazer uma série de mudanças no Assistente de Bancos de Dados para melhorá-lo e torná-lo mais completo, ou apenas como exercício de programação. Eis algumas delas:
1. Em lugar de salvar um arquivo texto com os dados e fazer o Excel abrir
esse arquivo, tente escrever o conteúdo de cada linha do registro numa linha da planilha. Com certeza, esse processo será mais lento que o mostrado aqui, mas vale como exercício. Aliás, isso elimina o problema das quebras de linha, citado acima, já que o conteúdo inteiro de um campo será escrito numa célula específica da planilha.
2. Nos campos de texto, elimine o problema da quebra de linha. antes de
escrever no documento Word ou no arquivo-texto para o Excel, verifique se o conteúdo do campo contém o caractere Chr(13) e troque-o por um espaço ou uma barra (/). Para implementar isso, você pode recorrer a uma função semelhante à já discutida FiltraApostrofo.
3. Pense na hipótese de incluir, no passo final do Assistente, uma caixa de
texto na qual o usuário possa digitar um título para a tabela que será importada do banco de dados. Esse título deverá ser inserido como um cabeçalho no documento Word ou na planilha Excel.
4. Inspirado nesse assistente, tente elaborar outros que resolvam problemas diversos.
5. Muitos bancos de dados têm consultas do tipo seleção. Em vez de trabalhar com tabelas, você pode trabalhar com essas consultas. A vantagem é que elas dão mais flexibilidade e podem produzir resultados com dados originários de mais de uma tabela.
6. Em certos casos, o usuário trabalha apenas com um banco de dados.
Nesse caso, você pode simplificar as coisas, fazendo um assistente que vá direto a esse banco de dados. Com isso, ele poderia começar no passo 2 ou no 3. 341
7. Se o banco de dados a ser aberto exige senha de acesso, modifique a rotina AbrirBancoDeDados para acomodar essa situação. Talvez seja necessário incluir no assistente um novo passo, que peça ao usuário para digitar a senha.
8. Se você usa mais o Excel do que o Word, considere a possibilidade de
transpor este projeto para o Excel. A diferença básica é que, aqui, o Word está “em casa” e o Excel é o objeto externo. Nessa proposta acontecerá o inverso. Afora isso, a maior parte do código não precisará sofrer alteração.
Atenção para os detalhes! Programar, em grande parte, equivale a prestar atenção aos detalhes. Uma prova disso você pode encontrar no projeto do Assistente de Banco de Dados. Quando você abre, via VBA, uma caixa de diálogo do Word para que o usuário escolha um arquivo ou diretório, o retorno vem entre aspas se a pasta ou arquivo contém espaço (por exemplo, Meus Documentos). Se você constrói seu programa, usando apenas exemplos que não contêm o tal espaço, corre o risco de enfrentar erros depois. Por isso, antes de usar uma string retornada de caixas de diálogo, teste para ver se ela não contém aspas. Veja um exemplo no procedimento cmdProcurar_Click, de frmAssistente.
O macete dos colchetes Cuidado ao trabalhar com variáveis que representam nomes de campos e nomes de tabelas no banco de dados. Envolva-os sempre com colchetes para evitar erros, aparentemente, inesperados. Esses erros sempre ocorrem quando o nome do campo ou o nome da tabela contém espaço. Admita, por exemplo, uma tabela chamada Turma 1. Para montar com ela uma sentença SQL que selecione todos os seus registros, escreva: strSQL = “SELECT * FROM ” & “[” & strTabela & “]”
Disso vai resultar uma sentença correta: SELECT * FROM [Turma 1]
Sem os colchetes, a expressão Turma 1, solta, provoca um erro. O mesmíssimo princípio vale para os nomes de campos: SELECT [Nome do Aluno], [Código] FROM [Turma C] 342
35 Instale e desinstale seus programas Monte um instalador de aplicações num documento do Word Ficha do projeto Projeto: Instalador de Aplicações O que faz: Instala a aplicação Gerador de Recibos no Word. Com adaptações, pode ser usado para instalar outros programas no ambiente do processador de texto. Arquivos e requisitos do projeto: Modelo Instalgr.dot, contendo: Texto explicativo para a instalação do Gerador de Recibos. Módulo modInstala, com a lógica de instalação do aplicativo. Módulo modReciboGeral, que deve ser copiado para o modelo Normal.
Modelo Recibog.dot, contendo o formulário frmReciboGeral Arquivo Rc_Ajuda.txt, documento de ajuda. Arquivo Rclients.lst, texto com a lista de clientes. Arquivo Rtipserv.lst, texto com os tipos e descrições de serviços prestados. Arquivo Rpessoas.lst, texto com a lista de pessoas que assinam os recibos. Arquivo Recibog.ini, que armazenas as variáveis Empresa e Cidade. Biblioteca Extens32.dll (© 1996 Antonio Augusto Ferreira), programa que fornece valores monetários por extenso. Conhecimento técnico: Abertura e gravação de arquivos texto. Manipulação de arquivos INI. Acesso a bibliotecas da API do Windows. Cópia de módulos de um modelo do Word para outro. Criação de um algoritmo para numeração seqüencial de arquivos. Automatização do preenchimento de documentos com o uso do objeto Selection.Find. Nível de programação: Avançado
Você acabou de desenvolver seu projeto, em casa ou na empresa, e precisa distribuí-lo aos usuários. Em geral, as pessoas não têm a menor idéia de como desligar certas automações inconvenientes do Word. Então você não vai esperar que elas saibam como fazer para instalar seu aplicativo, colando subs e funções no ambiente do VBA ou importando módulos nos formatos BAS e FRM. Está claro que é preciso facilitar a vida deles. Ainda mais se o projeto que você está oferecendo é algo que envolve módulos, arquivos de modelos, arquivos texto e outros, sendo que cada um deve ser instalado numa localização específica. É mais do que justo, portanto, que você crie um mecanismo de instalação para o seu programa. A boa notícia que tenho para lhe dar é que esse recurso pode ser construído com o próprio Word. Duvida? Este capítulo vai fazê-lo acreditar. Para isso, vamos trabalhar com um exemplo concreto. Que tal construir um utilitário de instalação para o Gerador de Recibos, tema do Capítulo 31? A escolha recai sobre esse projeto por uma razão bem simples: ele trabalha com um número de arquivos relativamente grande – oito – e envolve a criação de uma barra de ferramentas e de um módulo VBA no modelo Normal do usuário. O primeiro passo para a construção do Instalador de Aplicações é criar um modelo novo, chamado Instalgr.dot. No documento desse modelo (veja a Figura 35.1), você deve escrever as orientações para ajudar o usuário a instalar o programa. Com certeza, você só vai conseguir dar uma forma final a esse texto depois de concluir todo o projeto de instalação. Passemos, então, para a parte 344 interna de Instalgr.dot.
F I G U R A 3 5 . 1 O documento de instalação: texto informativo e
botões de comando
345
Antes de escrever qualquer linha de código, vamos determinar o que precisamos fazer. Para instalar a aplicação Gerador de Recibos, é necessário: n copiar os diferentes arquivos para os locais adequados; n
criar no modelo Normal uma rotina sub que funcione como disparadora do aplicativo;
n
criar um botão numa barra de ferramentas que ative essa rotina sub.
Concretamente, precisamos copiar sete arquivos, para diferentes locais no disco do usuário, conforme mostrado na tabela abaixo. ARQUIVO
COPIAR PARA
Recibog.dot
c:\solucoes\modelos
Recibog.ini
Idem
Rc_ajuda.txt
Idem
Rclients.lst
Idem
Rpessoas.lst
Idem
Rtipserv.lst
Idem
Extens32.dll
Diretório de sistema do Windows
Para iniciar a construção do utilitário de instalação, vamos criar dois módulos, dentro do modelo Instalgr.dot: modInstala e modReciboGeral. O primeiro conterá o código de instalação propriamente dito. Quer dizer, toda a lógica para copiar arquivos e estabelecer as necessárias configurações no Word. O outro contém código que não entra na instalação: lá está, apenas, para ser transferido para o ambiente VBA, da máquina de destino. A rotina principal em modInstala é a sub InstalaAplicativos. Essa rotina vai funcionar como uma centralizadora dos trabalhos, delegando tarefas específicas a outros procedimentos. A primeira tarefa é copiar os arquivos para os respectivos diretórios. Para executá-la, convoca-se a rotina CopiaArquivos. A estrutura do instalador parte do pressuposto de que todos os arquivos da aplicação estão reunidos na mesma pasta. Assim, a sub CopiaArquivos trabalha com duas matrizes. Numa, strArq, estão os nomes dos arquivos do diretório de instalação. Na outra, strDest, estão os diretórios para onde os arquivos serão copiados. O trecho básico do processo de cópia é o seguinte: m_strDirAtual = ActiveDocument.AttachedTemplate.Path
346
For n = 1 To iNumArqs - 1 strOrigem = m_strDirAtual & “\” & strArq(n) strDestino = strDest(n) & “\” & strArq(n) ‘ se não existe, copia
If Not ArquivoExiste(strDestino) Then FileCopy strOrigem, strDestino Else ‘ programa: copia em qualquer caso If n = 1 Then Kill strDestino FileCopy strOrigem, strDestino End If End If Next n
Para saber em qual diretório estão os arquivos (o usuário pode disparar o utilitário a partir de qualquer ponto no disco), usa-se a propriedade AttachedTemplate.Path, do documento ativo, que contém o instalador. O caminho completo do arquivo de origem é montado, combinando o nome desse diretório com o nome do arquivo. Do mesmo modo, o caminho do arquivo de destino é dado pela concatenação de um item da matriz que contém os diretórios com os mesmos nomes de arquivos. O próximo passo da rotina principal é chamar a sub DesinstalaAplicativos. Não estranhe. O VBA não aceita a criação de dois formulários ou dois módulos com o mesmo nome. Além disso, o Word também apontará erro se você tentar criar duas barras de ferramentas homônimas. Para evitar isso, DesinstalaAplicativos elimina do ambiente todos os vestígios de uma instalação anterior do Gerador de Recibos. Como se trata de um procedimento independente, essa rotina dá ao utilitário, também, o papel de desinstalador. Mas isso só pode acontecer se ela for declarada como um código de abrangência pública. A mesma característica vale para InstalaAplicativos. Feita a desinstalação – se for o caso –, a rotina InstalaAplicativos, coordenadora do processo, chama agora a sub CopiaMódulos. Esse procedimento vai copiar formulários e módulos de programação VBA, contidos no modelo Instalgr.dot, para o modelo Normal no Word do usuário. Na instalação do Gerador de Recibos, só é preciso copiar um módulo, modReciboGeral. Não há formulários, uma vez que toda a lógica do aplicativo encontra-se, internamente, no modelo Recibog.dot. O módulo modReciboGeral tem apenas uma rotina, chamada NovoRecibo, cujo papel é abrir um novo documento com base em Recibog.dot, iniciando o aplicativo. O procedimento NovoRecibo deve ser de abrangência pública, para poder ser chamado de fora do módulo: Public Sub NovoRecibo() Dim ArqModelo As String ArqModelo = “c:\solucoes\modelos\recibog.dot” Documents.Add Template:=ArqModelo End Sub 347
A cópia de modReciboGeral para o Normal.dot do usuário é feita com o método OrganizerCopy, que envolve o nome do modelo de origem, o modelo ou documento de destino, o nome do objeto (módulo ou formulário) e a natureza do objeto. Módulo e formulário pertencem à mesma categoria, wdOrganizerObjectProjectItems. Mas há outros objetos que também podem ser copiados com esse mesmo recurso – as barras de comando, por exemplo, identificadas pela constante wdOrganizerObjectCommandBars. O comando para copiar um módulo, aqui identificado por strNomeMod1, é o seguinte: Application.OrganizerCopy Source:= _ ActiveDocument.AttachedTemplate.FullName, _ Destination:=NormalTemplate.FullName, _ Name:=strNomeMod1, _ Object:=wdOrganizerObjectProjectItems
Até aqui, já copiamos os arquivos para os lugares devidos e instalamos no VBA uma rotina para abrir o aplicativo. O que há mais para fazer? Agora, para facilitar a vida do usuário, vamos criar uma barra de ferramentas chamada Recibos e colocar nela um botão de comando que vai chamar a rotina NovoRecibo e, portanto, executar o Gerador de Recibos. A adição dessa nova barra à coleção de barras de comando do Word se faz com o seguinte código: CustomizationContext = NormalTemplate Set BarraFer = CommandBars _ .Add(Name:=strBarraFer, Position:=msoBarFloating) With BarraFer .Controls.Add Type:=msoControlButton .Controls(1).Caption = strBarraFer .Controls(1).FaceId = 139 CustomizationContext = NormalTemplate .Controls(1).OnAction = “NovoRecibo” .Left = 0.8 * System.HorizontalResolution .Top = 0.25 * System.VerticalResolution .Visible = True End With
A propriedade CustomizationContext, na primeira linha do trecho acima, indica que a barra de ferramenta a ser criada (strBarraFer, que é igual a “Recibos”) deverá ser salva no modelo Normal. Após a criação da barra, usa-se outra vez o método Add para adicionar um botão de comando. Este recebe como ícone (propriedade FaceID) o número 139, correspondente ao desenho da Caixa de Texto, visível em Inserir/Caixa de Texto ou na barra de ferramentas Desenho. Observe a propriedade OnAction do botão. O valor dela passa a ser exatamente o nome da rotina sub que deve ser acionada com o clique no 348 botão de comando. Sempre que se cria uma barra de ferramentas, por defini-
ção ela está invisível. Por isso, é preciso ajustar, explicitamente, sua propriedade Visible para True. Agora podemos discutir, com mais domínio do terreno, a rotina DesinstalaAplicativos. Ela executa duas operações. Ela varre o ambiente do VBA, procurando por um módulo ou formulário que tenha um nome dado e, caso o encontre, apaga-o, usando o método OrganizerDelete: Application.OrganizerDelete “Normal.dot”, _ strNomeMod1, wdOrganizerObjectProjectItems
DesinstalaAplicativos, por sua vez, chama à ação a sub RemoveCommandBar, que também lança mão do método OrganizeDelete. Nesse caso, seria possível usar, simplesmente, o método Delete, aplicado ao objeto. Assim: cBar.Delete
No entanto, em experiências com o Word 2000, observei que ele apresenta falhas com esse método. Embora não apresente nenhum erro, ele não apaga a barra de ferramentas. E mais: estranhamente, deixa conviver duas barras com o mesmo nome. Esse comportamento não se verifica no Word 97. Por isso decidi usar o método OrganizeDelete, que funciona, igualmente, nas duas versões, sem problemas no Word 2000. Com isso, basicamente está concluída a instalação. Todo o resto são detalhes, que não deixam de ser importantes. Um deles é o recurso a uma função da API do Windows, GetSystemDirectory, para determinar o diretório de sistema. Você pode usar a função GetSpecialFolder, ligada ao Windows Scripting Host. Só que, nesse caso, é preciso ter certeza de que o sistema de scripting está instalado na máquina. Agora, o detalhe final. Na Figura 35.1, você notou que o documento inclui dois botões de comando. Qual a função deles? Esses botões são as portas de instalação e desinstalação do Gerador de Recibos. O usuário deve receber – num disquete, por exemplo – todos os arquivos do programa, inclusive o instalador, Instalgr.dot. Com um duplo clique nesse arquivo, ele abre um documento, como o mostrado na Figura 35.1. Com mais um clique no botão Instalar o Aplicativo, a tarefa está concluída. Se, depois, ele resolver eliminar o aplicativo de sua máquina, volta ao modelo, abre um arquivo e clica no botão Desinstalar o Aplicativo. Embora não seja uma operação comum, é fácil inserir botões de comando num documento. Para isso, siga o roteiro.
1. Acione Exibir/Barras de Ferramentas/Visual Basic. 2. Na barra, ligue o botão Modo Estrutura – aquele cujo ícone é o desenho de um lápis, ao lado de uma régua e um esquadro. Surge a barra Caixa de Ferramentas de Controle.
349
3. Clique no objeto botão de comando nessa caixa de ferramentas. Um
botão vai aparecer no documento, mas ainda não no tamanho, nem no local que você deseja. Clique nele com o botão direito do mouse e escolha Objeto Botão de Comando/Editar. O controle passa a exibir alças que lhe permitem redimensioná-lo e arrastá-lo. Também nesse momento você pode selecionar a legenda-padrão, CommandButton1, e trocá-la por outra adequada ao seu projeto.
4. Ainda com o botão direito do mouse sobre o objeto, você acessa o
comando Propriedades, que abre a caixa do mesmo nome. Nela, é possível definir todas as características do botão, incluindo fonte, dimensões, legenda e até uma imagem.
5. Na versão 97, assim que você coloca o botão na página, ele já está
pronto para ser deslocado para qualquer posição. Basta arrastá-lo. No Word 2000, é preciso, antes, clicar nele com o botão direito do mouse, acionar Formatar Controle e, na orelha Layout, escolher uma das alternativas no quadro Disposição do Texto.
6. Para associar o clique no botão a alguma macro, acione no menu de contexto, a opção Exibir Código. No caso do instalador para o Gerador de Recibos, o primeiro botão chama a rotina InstalaAplicativos e o segundo DesinstalaAplicativos.
7. Clique no botão Modo Estrutura (que, aliás, nesse momento se chama Sair do Modo Estrutura) e o documento está pronto.
Para ir mais além 1. Para adaptar este projeto à instalação de outros aplicativos, é preciso alterar a área geral de declarações e mais quatro rotinas, a saber: 1) CopiaArquivos; 2) CopiaMódulos; 3) CriaBarraFer; e 5) DesinstalaAplicativos.
2. Com algum esforço, pode-se tornar esse projeto o mais genérico possível, a fim de se tornar um instalador universal que exija o mínimo de reconfigurações.
3. Uma idéia para dar melhor acabamento ao produto é aperfeiçoar a roti-
na de desinstalação. Ela deve, por exemplo, identificar quando não há nenhum elemento do projeto instalado e avisar ao usuário que não executou nenhuma ação.
350
Copiar e renomear barras de ferramentas No Capítulo 35, você viu como criar e remover barras de ferramentas. Veja, no exemplo abaixo, como copiar uma barra de ferramentas de um modelo para outro e como renomeá-la: Sub CopiaRenomeiaBarraFerramentas() ‘ Copia barras de ferramentas de Normal.dot ‘ para o modelo do documento ativo. ‘ Cria um documento Documents.Add ‘ Salva-o como modelo ActiveDocument.SaveAs “c:\meus documentos\Testedot.dot”, _ wdFormatTemplate ‘ Copia a barra de ferramentas Teste para esse modelo Application.OrganizerCopy _ Source:=Application.NormalTemplate.FullName, _ Destination:=ActiveDocument.AttachedTemplate.FullName, _ Name:="Teste", Object:=wdOrganizerObjectCommandBars ‘ Renomeia, no modelo, a barra de Teste para Teste T Application.OrganizerRename _ Source:=ActiveDocument.AttachedTemplate.FullName, _ Name:="Teste", NewName:="Teste T", _ Object:=wdOrganizerObjectCommandBars ‘ Salva o documento ActiveDocument.Save End Sub
Os métodos OrganizerCopy e OrganizerRename fazem um trio com o já conhecido OrganizerDelete. Para verificar, visualmente se a barra de fato está lá, acione Formatar/Estilo e clique no botão Biblioteca. Depois, traga para o primeiro plano a orelha Barra de Ferramentas.
A coleção da Correção Se você quiser impressionar num bate-papo com seus amigos micreiros, use esta frase: “o VBA é uma coleção de coleções”. É apenas uma meia verdade, mas faz grande efeito. Além disso, há exemplos à beça para prová-la. Aqui vai mais um. No Word, as strings de AutoCorreção (a palavra errada e a que deve substituí-la durante a escrita) também são membros de uma coleção, formada pelos objetos AutoCorrect.Entries (entradas de AutoCorreção). Isso significa que é possível fazer um backup de todas elas e até restaurá-las, na mesma máquina ou em outra.
351
As propriedades desses objetos que nos interessam são, no caso, Name (o texto errado) e Value (a forma corrigida). Há ainda uma terceira, RichText. Booleana (True/False), que indica se há formatação armazenada junto com o texto de substituição. Quando, por exemplo, a entrada serve apenas para corrigir uma troca de letra, a propriedade RichText é falsa. No entanto, quando ela transforma, por exemplo, a seqüência de caracteres --> no sinal –>, então essa propriedade é verdadeira. Esse sinal não faz parte do conjunto de caracteres comuns disponíveis no Windows. Para listar todas as entradas de AutoCorreção em seu Word, use o código: Dim e As AutoCorrectEntry For Each e In AutoCorrect.Entries Selection.TypeText e.Name & vbTab & e.Value & _ vbTab & e.RichText & vbCrLf Next e
Atenção para a sutileza: o objeto, na declaração, é AutoCorrectEntry. A coleção, AutoCorrect.Entries. O ponto faz a diferença. A inclusão de entradas é feita com o método Add, outra figurinha fácil no universo das coleções. O exemplo abaixo adiciona o par (“náo”, “não”): With AutoCorrect .Entries.Add Name:="náo", Value:="não" End With
A eliminação de entradas se faz com o método Delete. Você pode usá-lo para eliminar pares correspondentes a erros que você certamente não vai cometer . Exemplo: (“orijen”, “origem”). Nesse caso, use a propriedade Name como índice: AutoCorrect.Entries(“orijen”).Delete
A linha acima pode ser digitada diretamente na janela Imediata. Veja, no CD que acompanha este livro, no diretório relativo a este capítulo, a rotina ListaAutoCorreção, que prepara um documento com a lista completa de todas as entradas existentes em seu Word.
352