agosto 2012 agosto 2012 índice 04 Android 15 Android - Lendo e Carregando Arquivo Texto Autor: Thiago C. Montebugnoli Delphi C# Editorial 05 Linguagem C# - Criando Classes e Objetos Autor: Marcos César Silva 10 Criando aplicações com FireMonkey e acesso a banco de dados no Delphi XE2 Autor: Lucas Vieira de Oliveira Delphi Delphi Parte V Autor: Luciano Pimenta 21 Desafio The Club Dicas 28 Legenda 30 Iniciante Intermediário Avançado agosto 2012 03 Bem-vindo Salve! Salve! Neste mês a revista The Club comemora no dia 5 de Agosto o Dia Internacional da Cerveja, tendo como propósito principal a possibilidade de unir amigos e familiares para saborear com responsabilidade esta maravilhosa bebida. Tão bom quanto uma cerveja gelada em uma sexta-feira agitada estão os artigos dos colunistas e colaboradores deste mês. Marcos César Silva nos ensina a Criar Classes e Objetos de uma forma simples e prática na Linguagem C#, servindo de base para outros tipos de linguagens. Av. Profº Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150 Informações e Suporte: (14) 3732-1529 Internet http://www.theclub.com.br Cadastro: [email protected] Suporte: [email protected] Informações: [email protected] Skype Cadastro: theclub_cadastro Skype Suporte: theclub_linha1 theclub_linha2 theclub_linha3 www.twitter.com/theclubbr Nosso colaborador mensal Luciano Pimenta traz a quinta parte da seqüência dos artigos do Curso de Delphi XE para Iniciantes, tendo também muitas informações para os que já possuem um pouco mais de experiência com o Delphi. Já o consultor técnico Lucas de Oliveira continua firme e forte abordando assuntos de grande importância e relevância no suporte técnico, sendo que neste mês ele cria exemplos de aplicações com FireMonkey junto com acesso a banco de dados. O FireMonkey é uma VCL turbinada que torna possível criar formulários mais interativos. Eu continuo minha jornada com artigos relacionados ao Sistema Android criando um exemplo prático de como se deve ler e carregar arquivos do tipo texto. A seção de “Dicas Delphi” está sempre nos disponibilizando os melhores macetes e assuntos abordados neste mês de Agosto. Um Forte abraço, Copyright The Club 2012 Diretor Técnico Marcos César Silva Diagramação Eduardo Massud Arte Vitor M. Rodrigues Revisão Eliziane Valentim Colunistas Lucas de Oliveira Luciano Pimenta Thiago Cavalheiro Montebugnoli Marcos César Silva Impressão e acabamento: GRIL - Gráfica e Editora Taquarituba-SP - Tel. (14) 3762-1345 Reprodução Thiago Montebugnoli - Editor Chefe [email protected] 04 agosto 2012 A utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da revista “The Club Megazine” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais. Delphi é marca registrada da Borland International, as demais marcas citadas são registradas pelos seus respectivos proprietários. C# Linguagem C# Criando Classes e Objetos Nesta importante etapa abordo um conceito geral de Classes e Objetos na linguagem C# aproveitando para descrever os tipos mais importantes ao decorrer do nosso aprendizado. Na Programação Orientada a Objetos (POO) tudo é baseado em classes e objetos, sendo que o conceito é universal e imprescindível, qualquer que seja a linguagem em que esta seja aplicada. A POO procura estabelecer um modelo de programação que aproxima o desenvolvedor do mundo real. Seguiremos enfatizando os principais conceitos e posteriormente um exemplo prático do uso de Classes e Objetos. Conceito Geral de Classes e Objetos Uma classe é uma unidade do sistema e dentro dela estão definidos atributos e métodos, que são respectivamente as informações que uma classe pode armazenar e ações que elas podem desempenhar. Para ficar mais fácil o entendimento, um atributo possui as mesmas funcionalidades de uma variável, assim como um método o mesmo que um procedimento ou função. A diferença fundamental entre classe e objeto reside no fato da classe conter as definições do que essa nova unidade irá fazer e o objeto ser um caso especial de uma classe. É importante saber que enquanto exista apenas uma definição de classe, podem existir diversos objetos baseados numa classe. agosto 2012 05 mente. Podemos então encapsular estas propriedades com a ajuda do “Refactor”, clicando com o botão direito em “nome” escolhendo “Refactor/ Encapsulate Field...” Ver Figura 02. Logo após aparecerá uma janela com os seguintes dizeres. Ver Imagem 03. São as funções e Procedimentos dentro da classe. Realizam operações sobre as informações contidas nos atributos de uma classe. Os métodos podem ser entendidos como mensagens trocadas entre diferentes objetos. Assim como os atributos, os métodos também podem ser do tipo público ou privado. Exemplo: Figura 01: Classe e Objeto. //Declaração do método Cadastrar(), do tipo “void” e “public” A imagem 01 ilustra de uma forma mais didática o que estou descrevendo. Conceito de Atributos Na realidade, os denominados atributos nada mais são do que as variáveis da classe, os locais que guardarão as informações referentes aquela classe. Quando um objeto é criado a partir de uma classe, neste momento os atributos passam a ter informações específicas assumindo estados especiais, ou seja, de uma forma geral este é o momento onde são atribuídos os valores. Existem dois tipos de atributos, os que permitem acesso externo ao da classe e os que não permitem, são denominados respectivamente de “Públicos” e “Privados”. Aproveitando este tópico, falaremos de um recurso importante da POO, o Encapsulamento de dados, que significa uma proteção às suas propriedades. De uma forma geral, estes conceitos são melhores compreendidos seguindo o exemplo a seguir. Exemplo: //Declaração do atributo do tipo “string” e “private” private string nome; A linguagem C#, junto com o Visual Studio possui inúmeros macetes que nos auxilia diaria- Figura 02: Refactor/Encapsulate Field. 06 agosto 2012 Figura 03: Encapsulate Field. Por padrão a Propriedade “nome” virá com a primeira letra em maiúsculo, isto serve para diferenciar da propriedade privada criada anteriormente. Clique no botão “Ok” para prosseguir com o código a seguir. //Encapsulamento do atributo Público Nome public string Nome { get { return nome; } set { nome = value; } } Deixamos o atributo “Nome” dinâmico, pois poderemos retornar e atribuir valores dinamicamente com os operadores “Get” e “Set” respectivamente, permitindo criar regras e lógicas para acesso a dados e público permitindo uma visibilidade e acessibilidade externa à Classe. Usaremos este recurso para fins didáticos, sem nenhuma regra adicional. Conceito de Métodos public void Cadastrar() { MessageBox. Show("Cadastrando sócio... "); } Métodos do tipo “void” podem ser comparados aos procedimentos, ou seja, não retornam nenhum valor. Já para retornar algum dado veremos o exemplo a seguir. Exemplo: //Declaração do método ConfirmarCadastro(), do tipo “bool” e “public” public bool ConfirmarCadastro(int tipo) { if (tipo == 1) return true; else return true; } O método criado anteriormente está retornando um valor do tipo Booleano utilizando a cláusula “return”, que significa retorno. Construtores e Destrutores Toda classe criada deverá possuir dois métodos: o Construtor (Construct) que é chamado no momento quando instanciamos a Classe e o Destrutor (Destruct) quando liberamos o objeto criado por esta classe da memória. No C# temos o denominado “Garbage Collector”, trocando em miúdos seria um “Coletor de Lixos”. Ele é responsável pela destruição de todo objeto que não é mais utilizado, sendo um recurso capaz de oferecer uma solução automatizada ao gerenciamento de memória. Exemplo: //Construtor public Socio() { MessageBox. Show("Objeto Criado com sucesso!"); } //Destrutor ~Socio()da classe { } do pelo nível de acesso (public) seguindo pela palavra chave “class”. Exemplo: public class Socio { // Códigos da Classe } Neste caso temos uma classe pública chamada “Socio”. Privada (Private) Seguindo a mesma lógica dos atributos e métodos, as classes privadas não permitem acesso externo. A sintaxe de criação pode ser conferida a seguir. Exemplo: O método Construtor será invocado quando instanciamos a Classe “Socio” e para fins de aprendizado achei interessante demonstrar a sintaxe do Destrutor, que normalmente não precisamos nos preocupar, pois contamos com o recurso “Garbage Collector” citado anteriormente. Tipos de Classes Na Programação Orientada a Objetos contamos com diversos tipos de classes como: Públicas, Privadas, Protegidas, Estáticas, Abstratas, Seladas, Herdadas entre outras, sendo que todas possuem recursos que se encaixam ao decorrer da análise do projetista. Nesta etapa abordarei os principais conceitos referentes a este assunto Pública (Public) As classes públicas, assim como os atributos e métodos, permitem que qualquer pessoa instancie objetos. O nome da classe é precedi- private class Socio { // Códigos da Classe } Ou class Socio { // Códigos da Classe } Existem duas maneiras para declaração de classes privadas, a primeira utilizando a palavra “private” e a segunda sem nenhuma referência. Public class Socio { //atributos estáticos private string nome; public string Nome { get { return nome;} set { nome = value;} } } Exemplo de utilização: Socio soc = new Socio(); soc.Nome = "Thiago Montebugnoli"; Estáticas Quando utilizamos estes tipos de classes, deveremos por obrigação, possuir todos os atributos como estáticos. A principal característica destas classes é não permitir realizar a instância de um objeto, ou seja, quando for utilizá-la basta fazer referência aos membros para poder trabalhar com os mesmos. Exemplo de criação: Public static class Socio { //atributos estáticos private static string nome; public static string Nome { get { return Socio.nome;} set { Socio.nome = value;} } Com o exemplo citado anteriormente fica mais fácil a visualização. Instanciáveis Este tipo de classe é o mais utilizado, ou seja, toda vez que precisarmos criar um objeto, é necessário instanciá-lo, podendo assim criar vários objetos desta mesma classe. Exemplo de criação: } Exemplo de utilização: Socio.nome = “Thiago Montebugnoli” agosto 2012 07 Na sua utilização basta atribuir o valor desejado. Como foi dito anteriormente, a instancia já é criada automaticamente quando executamos o programa, podendo ser utilizada em todos os pontos do software. A principal vantagem no uso desta classe é a automatização na criação da instância. Criando um Exemplo prático Crie uma aplicação “Windows Forms” e adicione um botão no formulário. A tela deverá ficar idêntica a Imagem 04. //Namespaces utilizados using System; using System.Collections. Generic; using System.Linq; using System.Text; using System.Windows. Forms; namespace Classes { //Classe pública public class Socio { //Construtor da classe public Socio() { MessageBox. Show("Objeto Criado com sucesso!"); } //Destrutor da Figura 04: Tela de Exemplo. A partir de agora vou detalhar todas as etapas de como se deve criar uma classe e usá-la logo em seguida, para isto clique em cima da solução e com o botão direito escolha “Add/New Item...” e na região esquerda em “Visual C# Items” escolha “Code” e em seguida “Class”. Aproveite e clique no botão “Add”. Ver Imagem 05. Figura 05: Adicionando uma Classe no projeto. O Visual Studio por padrão cria apenas o namespace “Classes”, e a classe “Socio”. Vou criar uma classe pública e logo em seguida instanciá-la. A Imagem 01 nos ilustra de uma forma clara de como irá ficar nossa classe “Socio” e o código abaixo abrange de uma forma prática todas as etapas explicadas anteriormente. 08 agosto 2012 classe ~Socio() { string Cpf { get { return cpf; } set { cpf = value; } } public string Rg { get { return rg; } set { rg = value; } } public DateTime Nascimento { get { return nascimento; } set { nascimento = value; } } public string Telefone { } //Atributos/ Propriedades Privadas Encapsuladas private string nome; private string cpf; private string rg; private DateTime nascimento; private string telefone; private string endereco; //Atributos/ Propriedades Públicas Encapsuladas public string Nome { get { return nome;} set { nome = value;} } public get { return telefone; } set { telefone = value; } } public string Endereco { get { return endereco; } set { endereco = value; } } //Métodos públicos public void Cadastrar() { MessageBox.Show(string. Format("Cadastrando sócio...\r\nNome: {0}, - Rg: {1}, Cpf: {2}",nome,rg,cpf)); } public bool ConfirmarCadastro(int tipo) Parse("15/11/1984"); soc.Telefone = "(14) { if (tipo == 1) return true; 9999-9999"; soc.Endereco = "Rua Fulano de Tal, 999"; else return //Invocando métodos soc.Cadastrar(); true; } public bool ValidarSocio(int tipo) { if (tipo == 1) return true; else return true; } if (soc. ConfirmarCadastro(1)) MessageBox. Show("Cadastro Confirmado!"); Figura 07: Passo 02 – Atribuindo Valores e Executando Métodos. if (soc. ValidarSocio(1)) MessageBox. Show("Sócio Validado!"); soc.Alterar(); public void Alterar() { MessageBox. Show("Alterando sócio..."); } } O primeiro passo foi criado o objeto “soc” usando a palavra reservada “new”, logo em seguida definimos os atributos e invocamos os métodos. A Figura 06, 07 e 08 nos dá uma boa idéia do funcionamento de uma classe em “Run-Time” . } } //Inserindo atributos soc.Nome = "Thiago Montebugnoli"; soc.Rg = "41.999.9999"; soc.Cpf = "312.999.999-99"; soc.Nascimento = DateTime. Neste artigo procurei separar em duas etapas, a primeira com conceitos básicos de Programação Orientada a Objetos (POO) e a outra com um exemplo prático e de fácil entendimento. Vou continuar com conceitos e dicas sobre este assunto nos próximos artigos. No formulário criado iremos instanciar um objeto da classe “Socio” e logo em seguida o código comentado correspondente. private void button1_ Click(object sender, EventArgs e) { //Instanciando um objeto Socio soc = new Socio(); Conclusão Abraços e até o mês que vem! Figura 06: Passo 01 - Construtor. Sobre o autor Marcos César Silva Consultor de Sistemas na consultoria de sistemas DataSmart e Consultor Técnico do The Club, Bacharel em Ciência da Computação, MBA em Gestão Empresarial, Certificações MCAD (Microsoft Certified Application Developer) e MCSD. NET (Microsoft Certified Solution Developer .NET) [email protected] agosto 2012 09 Criando aplicações com FireMonkey e acesso a banco de dados no Delphi XE2 Este artigo tem como foco, criar uma aplicação de exemplo utilizando a plataforma FireMonkey, a tecnologia LiveBindings, a tecnologia DBExpress e o Servidor de banco de dados FireBird. Serão abordados com mais profundidade os temas FireMonkey e LiveBindings que são novidade da nova IDE do Delphi, a XE2. Vamos então conhecer um pouco sobre essas novas tecnologias que foram acopladas ao Delphi. O que é o FireMonkey? O Delphi XE2 chegou ao mercado repleto de novidades. E uma das mais cobiçadas pela comu10 agosto 2012 nidade Delphi é o FireMonkey. Este framework chamou a atenção dos usuários Delphi por suas excelentes características. O FireMonkey é uma nova plataforma de desenvolvimento do Delphi XE2, com ela é possível criar aplicações e distribuí-las para plataformas Windows 32 bits, Windows 64 bits, Mac OS X e iOS (iPhone). Além disso, o FireMonkey possibilita criar uma aplicação mais rica em visual, além é claro de criar gráficos em 3D, similares as animações criadas em Flash que são utilizadas em muitas páginas da web. O FireMonkey não deixa nada a desejar, nem se tratando em desempenho de aplicações, pois utiliza de todo o poder da CPU e também da GPU (Unidade de Processamento Gráfico) para aplica- ções em 3D, assim sendo, não precisa se preocupar com o desempenho de suas aplicações FireMonkey. São quatro tipos de projetos FireMonkey para o Delphi XE2, são eles: 1. 2. 3. 4. FireMonkey 3D Application; FireMonkey 3D iOS Application; FireMonkey HD Application; FireMonkey HD iOS Application. Tendo em vista estas opções de projetos, percebe que FireMonkey HD Application dentre as demais é a que mais coincide com a tradicional VCL, os projetos para aplicações iPhone se percebe facilmente, e os projetos para aplicações com gráficos em 3D também fica fácil se perceber. Neste artigo será exemplificado mais a frente, a forma que mais se coincide com a VCL. Conexão a dados através da DBExpress Na VCL, tradicional plataforma de desenvolvimento mais utilizada pelos usuários Delphi em aplicações com acesso a dados, utiliza-se, entre outras tecnologias, a DBExpress para acessar um banco de dados pela aplicação. Dentro de um DataModule distribui-se os componentes utilizados para este acesso, que são sequencialmente o TSQLConnection e o TSQLDataSet ou TSQLQuery. Para garantir melhor desempenho, utiliza-se também a combinação de alguns componentes da palheta DataAccess, o TDataSetProvider e o TClientDataSet. Com estes componentes devidamente configurados, já terá uma conexão persistente com um banco de dados. Continuando a linha de raciocínio, na VCL, depois de configurados os componentes de acesso aos dados, é necessário utilizar um componente TDataSource dentro do formulário onde será feita a manipulação dos dados, este que será a origem dos dados no formulário. Para exibir os dados no formulário faz-se o uso de componentes DataWares, como DBEdit, EDbText entre outros da palheta DataControls. LiveBindings é baseado em expressões relacionais, é um novo recurso de ligação de dados que foi adicionado a IDE do Delphi XE2 para apoiar tanto a VCL quanto o FireMonkey, utiliza-se de expressões para fazer a ligação de um objeto a outro por meio de suas propriedades. O conceito principal destas ligações está em identificar o objeto de origem e o objeto de controle (o destino dos dados), além é claro de configurar as expressão de origem do objeto de origem e a expressão de controle. Estas configurações podem ser feitas no pelo ObjectInspector nas propriedades do objeto de controle. Tendo em vista que estas ligações envolvem um objeto de origem e um objeto de controle, podemos ter três situações sobre o fluxo dos dados, sabendo que Source e Control são respectivamente os objetos de origem e de controle: 1. 2. 3. dirSouceToControl; dirControlToSouce; dirBidirectional. Estes têm em comum duas propriedades, onde se configuram o DataSource ao qual estará vinculado e o DatField que é propriamente dito o Campo da tabela que o componente irá exibir e manipular no formulário. Estando tudo isso configurado, o formulário está pronto para exibir e manipular os dados em tempo de execução. As opções acima são configuradas na propriedade Direction do componente BindExpression relacionado ao objeto de controle, não se atente em entender isso agora, no exemplo será explicado esta ligação com mais detalhes, o importante agora é saber que o fluxo de dados de uma ligação LiveBindings pode ser tanto da origem para o controle quanto do controle para a origem, ou ainda os dois ao mesmo tempo, marcando a opção dirBidirectional. Até aqui não tem nenhuma mudança, como foi dito esta é uma das formas mais tradicionais de conexão a banco de dados utilizada em Delphi. Esta seria uma conexão para uma VCL, mas e se fosse uma aplicação FireMonkey, o que mudaria? A resposta é bem simples, não mudaria nada que se refere à conexão de banco de dados, ou seja, poderá continuar utilizando forma tradicional. Ao criar uma expressão de ligação pelo ObjectInspector, é adicionado ao formulário automaticamente um componente BindingList, que armazenará e gerenciará todas as expressões presentes neste formulário. Outro componente importante é o BindScope, o qual gera um escopo de ligação entre os componentes. No caso de ligação a dados, ou seja, a um TDataSouce, usa-se o BindScopeDB. O que muda realmente, é que o FireMonkey não conta com os componentes DataWares da palheta DataControls, o DBEdit, o DBText, etc... Sendo assim, para exibir os dados num formulários serão utilizados os componentes da palheta Standard. Neste ponto surge a necessidade de se utilizar a outra novidade da versão XE2 do Delphi, a tecnologia LiveBindings (Ligações ao vivo). Um exemplo simples de LiveBindings seria passar o texto digitado em um TEdit para ao Caption de um TLabel, neste caso o TEdit é a origem dos dados, ou seja, a configuração de ligação entre os componentes será feita toda no TLabel pela propriedade LiveBindings. O que é LiveBindings? Neste ponto se for um projeto montado na plataforma FireMonkey, listará nesta propriedade duas opções, ‘New LiveBinding’ e ‘Link To DB Field’, se for uma projeto em VCL apenas aparecerá a primeira opção. Seguindo o exemplo, escolha a opção ‘New LiveBinding’, aparecerá uma janela para selecionar o tipo de componente que deseja criar, selecione TBindExpression e clique em ‘Ok’. Aparecerá mais uma propriedade no ObjectInspector do TLabel, denominada BindExpressionLabel11. Agora será definido a ligação entre os objetos, marque os seguintes valores nas seguintes propriedades do Label1: 1. ControlExpression = Text (Na plataforma FireMonkey não é mais Caption); 2. Direction = dirSourceToControl; 3. SourceComponent = Edit1; 4. SourceExpression = Text. Até aqui já está configurado a ligação, porém resta agora apenas notificar o mecanismo de expressão, que irá atualizar os objetos conforme as alterações em suas propriedades, para isso basta apenas uma linha de código. No evento onChange do Edit1, coloque a seguinte codificação: BindingsList1. Notify(Edit1, ‘Text’); O método Notify, irá notificar aos mecanismos de expressões as mudanças que ocorreram na propriedade Text, o segundo parâmetro do método, do componente Edit1 que é o Primeiro parâmetro do método. Pronto este primeiro exemplo já está funcional, pode executá-lo e ver o que acontece com acontece com a Label1 depois que digitar algo na caixa de texto. Veja na figura 1 o exemplo em execução. Figura 1 – Exemplo de LiveBindings na plataforma VCL Com este pequeno exemplo, espero ter esclarecido a funcionalidade do recurso LiveBindings, agora podemos ter exemplo base do artigo onde será criado um projeto FireMonkey HD Application que fará conexão a um banco de dados FireBird através da tecnologia DBExpress. agosto 2012 11 Exemplo prático do FireMonkey com ligação ao banco de dados FireBird Criação do projeto FireMonkey HD Application - Delphi Agora descreverei os passos para a criação do exemplo prático deste artigo, serão divididos em etapas para facilitar o entendimento e a possibilidade do leitor pular uma etapa em que já possua o conhecimento específico como a conexão à base de dados. O próximo passo é abrir o Delphi XE2 e criar um novo projeto FireMonkey HD Application - Delphi. Salve na pasta ‘Fontes’ que está dentro da pasta ‘Escolar’ o projeto com o nome ESCOLAR.dproj, e salve a primeira Unit com o nome de unAlunos. pas. Esta será a Unit que manipulará os dados da tabela Alunos presente no banco de dados. Altera a propriedade Name do Form1 para ‘frmAlunos’. Criação do banco de dados Por motivo de organização, crie uma pasta com o nome do projeto ‘Escolar’ e dentro dela outra pasta onde será salvo o banco de dados, pode nomeá-la como ‘Banco’, se necessário pode até criar uma pasta para organizar as fontes do projeto, mas isto fica a critério de cada um, uma boa organização agiliza e muito na localização de arquivos pertinentes ao projeto. Crie um banco de dados firebird com o nome ESCOLAR, que será usado a fim de exemplificar este artigo, e uma tabela ALUNOS com os seguintes campos: CREATE TABLE ALUNOS ( ID_ALUNO INTEGER NOT NULL, NOME VARCHAR (60) CHARACTER SET NONE COLLATE NONE, DATA_NASCIMENTO DATE, CPF VARCHAR (15) CHARACTER SET NONE COLLATE NONE, CIDADE VARCHAR (60) CHARACTER SET NONE COLLATE NONE, UF CHAR (2) CHARACTER SET NONE COLLATE NONE, TELEFONE VARCHAR (15) CHARACTER SET NONE COLLATE NONE); ALTER TABLE ALUNOS ADD CONSTRAINT PK_ALUNOS PRIMARY KEY (ID_ALUNO); CREATE UNIQUE INDEX PK_ALUNOS ON ALUNOS (ID_ ALUNO); Listagem 1: Banco de dados Escolar 12 agosto 2012 Conexão da aplicação à base de dados através de um DataModule Para fazer a conexão da aplicação criada com a base de dados, crie um DataModule pelo menu ‘File/New/Other’ e na janela New Itens seleciona no menu à esquerda ‘Delphi Projects/Delphi Files’, serão listados à direita os itens a serem adicionados ao projeto, escolha DataModule e clique em Ok. Altere a propriedade Name do DataModule1 para DM e salve a Unit com o nome unDm. Através do mecanismo DataExplorer, localizado à direita da tela, crie uma nova conexão FireBird clicando com o botão direito em seguida New Connection, de o nome de ESCOLAR. Para especificar o caminho do banco de dados, clique com o botão direito na conexão que acabou de criar e selecione a opção Modify Connection, informe a localização do arquivo do banco de dados, o nome de usuário e a senha de acesso do mesmo, e teste a conexão. Caso de algum erro verifique se o caminho está certo, caso seja necessário antes do caminho especifique o IP ou o nome do computador, exemplo ‘NomedoPC-PC:C:\ESCOLAR\Banco\ESCOLAR.FDB’. Configurando os componentes DBExpress Coloque um componente TSQLConnection da palheta DBExpress e configure a propriedade ConnectionName para ESCOLAR, marque false em LoginPrompt e de o nome de CONEXAO. Agora coloque no DM um componente TSQLDataSet também da palheta DBExpress, altere seu Name para sdsAlunos, defina em ConnectionName a conexão Escolar, e coloque em CommandText a SQL ‘SELECT * FROM ALUNOS’. Carregue os Fields do sdsAlunos e configure suas propriedades ProvidersFlags. Adicione mais um componente ao DM, agora um TDataSetProvider da palheta DataAccess, dê o nome de dspAlunos, em UpDateMode selecione upWhereKeyOnly e para finalizar as configurações deste componente selecione a propriedade opções e marque True em poAllowCommandText. Agora para finalizar e possibilitar o acesso dos dados em memória local, de forma desconectada com o servidor de banco de dados, coloque um componente TClientDataSet da palheta DataAccess, altere a propriedade Name para cdsAlunos e selecione em ProviderName o dspAlunos. Até este ponto, o mecanismo de acesso e manipulação aos dados está pronto. O que resta fazer é carregar os Fields do cdsAlunos e configurá-los da mesma forma como os Fields do sdsAlunos. Como o cdsAlunos irá trabalhar com os dados em memória local, para poder gravar no banco de dados às atualizações feitas nele é preciso chamar o método ApplyUpDates passando o parâmetro -1 ou 0 (zero) nos eventos AfterPost e AfterDelete, veja na listagem 2 um exemplo: procedure TDM.cdsAlunos AfterDelete(DataSet: TDataSet); begin cdsAlunos. ApplyUpdates(-1); end; Listagem 2 – Método ApplyUpDates do cdsAlunos Criando um layout para o frmAlunos e ligando-o ao DM Até este ponto do exemplo não tem novidades para quem já trabalha com a tecnologia DBExpress, pois a conexão com o banco de dados, como foi dito antes, continua sendo feita da forma tradicional. Porém, agora temos que vincular os Fields do cdsAlunos, que se encontra no DM, a um componente TDataSource, que será adicionado no frmAlunos, ou seja, o frmAlunos terá que ter a declaração da unDm em sua cláusula Uses, isto pode ser feito visualmente pelo menu ‘File/Use Unit’, depois basta selecionar a unDm e confirmar clicando em Ok. Logo após adicione um DataSource ao frmAlunos e altere o Name dele para dsAlunos, este será a origem dos dados deste formulário, na propriedade DataSet selecione pelo combo a opção DM.cdsAlunos. Na VCL isto garantiria que qualquer componente DataWare, da palheta DataControls, ligado ao dsAlunos poderia manipular um Field sem maiores complicações, bastaria apenas configurar no componente o dsAlunos e o Field que ele representaria. Como estamos criando um projeto pela plataforma FireMonkey, isso já não é possível devido ao fato de não contarmos com estes componentes DataWares, por isso teremos que fazer o uso do novo recurso de ligações LiveBindings, para podermos configurar os componentes visuais comuns para estarem gerenciando os dados a partir de expressões. Para poder fazer um vinculo das expressões LiveBindings com o dsAlunos teremos é claro que definir um escopo para estas ligações, sendo assim adicione ao formulário um TBindScopeDB, altere seu Name para BindScopeDB e na propriedade DataSource selecione o dsAlunos, com isso qualquer componente visual através da propriedade LiveBinding terá acesso aos Fields do cdsAlunos e poderá manipulá-los. Agora vamos definir um layout para o frmAlunos, veja na figura 2 uma sugestão, neste exemplo será utilizado um BindNavigator da palheta LiveBindings, que tem a mesma funcionalidade de um DBNavigator, neste componente é necessário apenas configurar a propriedade BindScope selecionando o recém-criado BindScopeDB. Será necessário também adicionar os componentes de edição e visualização dos campos da tabela de alunos, neste caso iremos adicionar seis componentes TEdit lembrando de alterar a propriedades Name de cada um seguindo um padrão, por exemplo edtIdAluno, edtNome, edtDataNascimento, edtCPF, edtCidade e edtTelefone. Assim ficará mais fácil fazer a chamada deste componentes nas linhas de código. Coloque também um componente TComboEdit da palheta Additinonal, este tem a mesma função do combobox comum, onde se preenche uma lista de itens, estes que serão os valores a serem armazenados no banco de dados, altere o Name deste componente para cbeUF, e adicione as siglas das UFs na propriedade item separando cada uma por uma quebra de linha com o botão Enter, lembrando que o campo UF foi definido com o tipo Char (2), limitando o armazenamento a apenas 2 caracteres. Para listar os registros cadastrados no banco, em VCL usa-se um DBGrid, neste exemplo em Firemonkey será usado um TStringGrid, aletre seu Name para sgAlunos, mais a frente explicarei como fazer sua ligação com os dados. Para finalizar o layout do frmAlunos, restam apenas distribuir os rótulos (Labels) de cada componente. Agora vamos fazer as configurações LiveBin- dings dos componentes, para que possibilite o acesso aos dados. Figura 2 – Sugestão de layout para o frmAlunos Configurando as ligações dos componentes com o recurso LiveBindings Tendo todos os componentes visuais ajustados ao layout do formulário, agora vamos fazer os vínculos com os componentes de acesso aos dados via LiveBindings. Baseando-se na nomenclatura sugerida dos componentes, selecione o componente edtIdAluno e na janela ObjectInspector localize a propriedade LiveBinding, ao clicar na combo serão listadas duas opções como mostra a figura 3, a opção NewLiveBinding seria para o caso de fazer uma ligação manualmente como foi mostrado no primeiro exemplo onde se informava a ControlExpression, a Direction, o SourceComponent e a SourceExpression, porém como neste exemplo as ligações serão feitas aos Fields do cdsAlunos escolheremos a segunda opção, Link to DB Field, abrirá uma janela listando os Fields vinculados ao BindScopeDB, ver figura 4, agora basta selecionar, neste caso, o Field ID_ALUNO e pressionar Ok. Faça isso para todos os componentes TEdit, definindo corretamente seus BindLinks para os Fields correspondentes. No cbeUF (TComboEdit) depois de inserido as siglas das UFs na lista de itens, basta criar um BindLink para o campo UF da tabela, da mesma forma que foi feito com o edtIdAluno. Figura 3 - Link to DB Field Ao criar o primeiro DBLink, automaticamente é criado no formulário um componente TBindingsList, este é responsável por listar todas as expressões Binding presentes no formulário, pode-se também através deste componente editar as expressões listadas, alterando suas propriedades Figura 4 - New DB Link de forma desvinculadas aos componentes de controle. Entenda que o BindingList utiliza o mesmo mecanismo de uma ActionList, ele agrupa todos os controles Bindings facilitando a visualização dos mesmos, veja na figura 5 a lista dos componentes do frmAlunos que é aberta logo após dar dois cliques no componente BindingsList. Figura 5 - Lista de componentes Bindings Para configurar a sgAlunos (TStringGrid) muda um pouco, mas continua seguindo a mesma linha de raciocínio dos BindLinks, porém a opção será Link to DB DataSource. Ao selecionar esta opção será aberta novamente a janela New DB Link para selecionar em qual o escopo está ligado o dsAlunos, neste exemplo temos apenas um escopo, mas poderiam ser mais conforme as necessidades forem surgindo. Ao criar o BindLink para o sgAlunos, já está praticamente pronto nosso projeto, basta apenas realizar alguns ajustes para melhorar a forma de exibição dos dados na grid, logo abaixo da propriedade LiveBinding do componente sgAlunos, surgiu uma nova propriedade chamada DBLinksgAlunos1, esta propriedade será criada em todos os componentes, ela se refere de fato ao BindLink vinculado ao objeto selecionado, neste caso o sgAlunos, iremos utilizar a propriedade Columns. Da mesma forma que no tradicional DBGrid da VCL, pode-se adicionar os campos do cdsAlunos na grid de forma manual, podendo assim definir a largura e o título da coluna de cada Field. Então agosto 2012 13 clique na propriedade Columns e abrirá a janela para o gerenciamento das colunas da grid, basta clicar no botão inserir e selecionar os campos um a um, definindo suas propriedades, ver tabela 1. Tabela 1 – Propriedades das colunas da sgAlunos Ao preencher todas as propriedades das colunas da sgAlunos, nosso exemplo está quase pronto, basta alguns ajustes para uma melhor performance. O ideal é que a conexão ao banco de dados seja aberta apenas uma vez ao abrir o formulário principal e fecha quando o mesmo seja encerrado. Como neste exemplo temos apenas uma tela, esta fará a função de abrir e fechar a conexão e também abrir e fechar o ClientDataSet utilizado na manipulação dos dados do formulário, o cdsAlunos. Para que isso seja possível, basta apenas duas linhas de códigos no evento onCreate e duas no evento onClose do frmAlunos. Veja a listagem 3. procedure TfrmAlunos. FormClose(Sender: TObject; var Action: TCloseAction); begin DM.cdsAlunos.Close; DM.CONEXAO.Connected := False; Figura 6 - Projeto em tempo de execução Estes códigos garantem o desempenho do projeto em execução, mas temos que ter em mente a ordem de criação formulários na aplicação, acesse o menu do Delphi ‘Project/Options’ selecione a opção Forms e veja à direita a ordem em que será feito o auto-create, se o DM estiver em segundo lugar no seu projeto, arraste-o para a primeira posição, para que seja criado antes do frmAlunos e pressione Ok. Isso é feito para prevenir um futuro erro de execução. Veja que é feito o uso do DM no formulário de alunos, para abrir e fechar a conexão e o cdsAlunos, sendo assim isto não seria possível se o formulário de alunos fosse criado antes do DM, por isso o DM deve ser criado antes do formulário. Agora sim, nosso exemplo está funcionando, pode fazer alguns cadastros de teste, note que a cada alteração nos componentes a grid vai sendo atualizada automaticamente como se fosse uma DBGrid mesmo. Veja na figura 6 a aplicação sendo executada. end; procedure TfrmAlunos. FormCreate(Sender: TObject); begin DM.CONEXAO.Connected := True; DM.cdsAlunos.Open; Lucas Vieira de Oliveira Consultor Técnico The Club. [email protected] 14 agosto 2012 Procurei mostrar neste artigo, de uma forma introdutória, um exemplo de como trabalhar com duas novas tecnologias do Delphi XE2, o FireMonkey e o recurso LiveBindings, para fazer uma aplicação com acesso à banco de dados. Vimos que mesmo num projeto FireMonkey a conexão via DBExpress mantém a mesma forma de trabalhar, alterando somente a forma de ligar os componentes visuais com os de acesso aos dados. Além disso, ainda é possível fazer muitas outras coisas com o Firemonkey, como foi dito na introdução, é uma tecnologia bastante inovadora e cheia de recursos para os usuários distribuírem em suas aplicações. E está abrindo novas portas para os desenvolvedores adeptos ao Delphi. Espero que tenham gostado do artigo, um abraço a todos e até a próxima. Sobre o autor end; Listagem 3 – Códigos de manipulação da conexão e do cdsAlunos na aplicação Conclusão Android Lendo e Carregando Arquivo Texto Neste mês abordarei um pouquinho de como podemos criar e carregar arquivos do tipo texto no Android armazenando estas informações no cartão SD do dispositivo. Montaremos um exemplo prático abordando recursos como: Criar e utilizar o evento Onclick() do botão, Utilização da classe “Java.IO” para manipulação de arquivos, entre outros recursos que poderão ser acompanhados ao decorrer da leitura. Opções de Armazenamento O Android nos fornece diversas formas para salvar os dados, sendo que a solução depende da necessidade, podendo assumir as seguintes formas: SharedPreferences: Armazenar dados particulares primitivos em pares chave-valor. Internal Storage: Armazenar dados privados na memória do dispositivo. External Storage: Armazenar dados públicos sobre o armazenamento externo compartilhado. SQLite DataBases: Armazenar dados estruturados em um banco de dados. Como foi dito no início do artigo, usaremos a opção “External Storage”, que significa Armazenamento externo. External Storage – Armazenamento Externo agosto 2012 15 Os dispositivos compatíveis com o sistema Android suporta uma memória externa compartilhada que podemos utilizar para diversas tarefas, como por exemplo manipular arquivos do tipo texto. Podendo ser um cartão SD ou uma memória interna não removível. Os arquivos salvos para o armazenamento externo são de leitura para todos podendo ser modificado pelo usuário. Comandos úteis: Environment.getExternalStorageState() : Comando necessário para verificar se a mídia está disponível. Environment.getExternalStorageDirectory(): comando para abrir um diretório que representa a raiz do armazenamento externo, usando o diretório: /Android/dados/<nome do pacote>/arquivos Figura 01: Android Manifest Permissions. Criando um exemplo prático O lay-out da aplicação se dividirá em duas partes, a primeira iremos criar um arquivo do tipo texto e salvá-lo no cartão SD, já a segunda escolheremos o arquivo para posteriormente carregá-lo em um EditText. Para isto abra seu Eclipse e clique em “File/New/Android Project” e crie um projeto em Android, recomendo a criação na versão 2.2 ou 2.3. Adicionando permissões Um detalhe importante para esse projeto é que será necessário habilitar a permissão “WRITE_EXTERNAL_STORAGE” no arquivo manifest, pois graças a essa permissão que seu aplicativo pode gravar arquivos no SD Card. Dê um duplo clique no arquivo “AndroidManifest.xml” e na aba “Permissions” clique no botão “Add” para adicionar o código a seguir. Ver Imagem 01. <activity android:name=”. Android_txtActivity” android:label=”@string/ app_name”> <intent-filter> <action android:name=”android.intent.action.MAIN” /> <category android:name=”android.intent. category.LAUNCHER” /> </intent-filter> </activity> </application> <uses-permission android:name=”android. permission.WRITE_EXTERNAL_STORAGE”/> </manifest> Criando a interface gráfica android.permission.WRITE_EXTERNAL_STORAGE Esta permissão indica que podemos salvar dados em um dispositivo externo, o código deverá ficar parecido com o a seguir. <?xml version=”1.0” encoding=”utf-8”?> <manifest xmlns:android=”http://schemas. android.com/apk/res/android” package=”pct.Android_txt” android:versionCode=”1” android:versionName=”1.0”> <uses-sdk android:minSdkVersion=”8” /> <application android:icon=”@drawable/icon” android:label=”@string/app_name”> 16 agosto 2012 Trabalharemos com os componentes padrões do Android, como: TextView, EditText, Button e Spinner. A imagem 02 nos dá uma noção melhor de como os componentes deverão estar dispostos em nosso arquivo principal “main.xml”. Podemos conferir o código XML correspondente logo em seguida. <?xml version=”1.0” encoding=”utf-8”?> <LinearLayout xmlns:android=”http:// schemas.android.com/apk/res/android” android:orientation=”vertical” android:layout_width=”fill_parent” android:layout_height=”fill_parent” android:weightSum=”1”> parent” android:singleLine=”false” android:gravity=”top” android:lines=”5” android:layout_height=”120dp”/> <Button android:text=”Salvar txt” android:onClick=”click_Salvar” android:layout_height=”wrap_ content” android:layout_width=”115dp” android:layout_gravity=”center_ horizontal”/> Figura 02: Disposição dos componentes. <TextView android:text=”Diretório:” android:id=”@+id/txtRoot2” android:layout_width=”wrap_ content” android:layout_height=”wrap_ content”/> <TextView android:text=”Nome do arquivo txt:” android:layout_width=”wrap_ content” android:layout_height=”wrap_ content”/> <EditText android:id=”@+id/edtNomeArq” android:text=”” android:layout_width=”match_ parent” android:layout_height=”wrap_ content”/> <TextView android:text=”Texto do arquivo a ser salvo:” android:layout_width=”wrap_ content” android:layout_height=”wrap_ content”/> <EditText android:id=”@+id/edtSalvar” android:text=”” android:layout_width=”match_ <TextView android:text=” “ android:id=”@+id/txtPreencher” android:layout_width=”fill_parent” android:layout_height=”wrap_ content”/> <LinearLayout android:layout_ width=”fill_parent” android:gravity=”center_ vertical” android:layout_height=”wrap_ content”> <Spinner android:id=”@+id/ spListarArquivos” android:layout_ width=”220dp” android:layout_weight=”10” android:layout_ height=”50dp”/> <Button android:layout_ weight=”1.0” android:text=”Carregar txt” android:onClick=”click_ Carregar” android:layout_ width=”115dp” android:layout_ height=”wrap_content”/> </LinearLayout> <TextView android:text=”Texto do arquivo a ser carregado:” android:layout_width=”wrap_ content” android:layout_height=”wrap_ content”/> agosto 2012 17 <EditText android:id=”@+id/edtLer” android:text=”” android:layout_width=”match_ parent” android:singleLine=”false” android:gravity=”top” android:lines=”5” android:layout_ height=”120dp”/> </LinearLayout> Podemos rodar o exemplo e teremos uma tela parecida com a da Figura 03. Criaremos também algumas variáveis ao decorrer do desenvolvimento, confiram a seguir: private TextView txtRoot; private TextView txtNomeArq; private TextView txtSalvar; private TextView txtLer; private Spinner SpnListarArquivos; private ArrayList<String> Arquivos = new ArrayList<String>(); No evento OnCreate() faremos atribuições às variáveis e invocaremos o método Listar() seguido de um Try..Catch. public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { setContentView(R.layout.main); Figura 03: Lay-Out da aplicação. Codificando o Exemplo Usaremos alguns pacotes adicionais, sendo necessário adicioná-los ao projeto. import import import import import import import import import import import 18 java.io.BufferedReader; java.io.File; java.io.FileOutputStream; java.io.FileReader; java.util.ArrayList; android.os.Environment; android.view.View; android.widget.ArrayAdapter; android.widget.Spinner; android.widget.TextView; android.widget.Toast; agosto 2012 txtRoot = (TextView) findViewById(R.id.txtRoot2); txtNomeArq = (TextView) findViewById(R.id.edtNomeArq); txtSalvar = (TextView) findViewById(R.id.edtSalvar); txtLer = (TextView) findViewById(R.id.edtLer); SpnListarArquivos = (Spinner) findViewById(R.id.spListarArquivos); txtRoot.append(ObterDiretorio()); Listar(); } catch (Exception e) { Mensagem(“Erro : “ + e.getMessage()); } } } Achei necessário criar uma função Padrão Mensagem (), a qual será responsável notificar o usuário. Esta classe “Toast” se difere da “AlertDialog. Builder” pois a mesma apenas informa no rodapé inferior da tela do Android o ocorrido e logo em seguida desaparece, achei interessante usar este tipo de recurso para fins de aprendizado, podemos conferir a seguir o método que recebe como parâmetro uma String. private void Mensagem(String msg) { Toast.makeText(getApplicationContext(), Ao clicarmos no botão Salvar executaremos o método “Click_Salvar”, usaremos um Try..Catch, onde transformamos o texto digitado em um “Array de Bytes” e com o método “Write” inserimos os dados seguido de uma notificação ao usuário. Com o método Listar() atualizaremos o Spinner com o nome dos arquivos “.txt”. Ver Imagem 04. msg, Toast.LENGTH_SHORT).show(); } O próximo método nos retorna o diretório de armazenamento externo. private String ObterDiretorio() { File root = android.os.Environment. getExternalStorageDirectory(); return root.toString(); } O método Listar() preencherá o componente Spinner com os arquivos do tipo “.txt” salvos no diretório externo. Usamos os tipos de variáveis File e File[], sendo respectivamente responsáveis por obter o diretório e os arquivos deste diretório. Adicionamos os arquivos em um “Array” para posteriormente utilizá-los. public void Listar() { File diretorio = new File(ObterDiretorio()); File[] arquivos = diretorio. listFiles(); public void click_Salvar(View v) { String lstrNomeArq; File arq; byte[] dados; try { lstrNomeArq = txtNomeArq. getText().toString(); arq = new File(Environment. getExternalStorageDirectory(), lstrNomeArq); FileOutputStream fos; dados = txtSalvar.getText().toString(). getBytes(); fos = new FileOutputStream(arq); fos.write(dados); fos.flush(); fos.close(); Mensagem(“Texto Salvo com sucesso!”); if(arquivos != null) { int length = arquivos.length; for(int i = 0; i < length; ++i) { File f = arquivos[i]; if(f.isFile()) { Arquivos.add(f. getName()); } } ArrayAdapter<String> arrayAdapter = new Ar rayAdapter<String>(this,android.R.layout. simple_dropdown_item_1line, Arquivos); SpnListarArquivos. setAdapter(arrayAdapter); } } Criando o método Salvar() Figura 04: Salvando o Arquivo .txt. agosto 2012 19 Listar(); } catch (Exception e) lstrNomeArq = SpnListarArquivos. getSelectedItem().toString(); txtLer.setText(“”); { Mensagem(“Erro : “ + e.getMessage()); } } Criando o método Carregar() Já o método carregar, continuaremos a utilizar o Try..Catch, sendo que de primeiro momento pegamos o item que está selecionado no componente Spinner, limpamos o campo “txtLer” e logo em seguida efetuamos uma leitura linha a linha do arquivo carregando para a caixa de texto toda a informação lida. Ver Imagem 05. public void click_Carregar(View v) { String lstrNomeArq; File arq; String lstrlinha; try { arq = new File(Environment. getExternalStorageDirectory(), lstrNomeArq); BufferedReader br = new BufferedReader(new FileReader(arq)); while ((lstrlinha = br.readLine()) != null) { if (!txtLer.getText(). toString().equals(“”)) { txtLer.append(“\n”); } txtLer.append(lstrlinha); } Mensagem(“Texto Carregado com sucesso!”); } catch (Exception e) { Mensagem(“Erro : “ + e.getMessage()); } } Conclusão Este artigo nos demonstrou os conceitos básicos e essenciais para trabalhar com arquivos textos no Sistema Android. Procurei demonstrar alguns recursos diferentes dos abordados nos meses anteriores a fim de aumentar nosso leque de aprendizado. Vou ficando por aqui, um forte abraço e até o mês que vem! Sobre o autor Thiago Cavalheiro Montebugnoli Thiago Cavalheiro Montebugnoli é tecnólogo, formado pela Faculdade de Tecnologia de Botucatu – SP (FATEC) foi consultor técnico do The Club, já desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco de Dados SQL Server e Firebird. Atualmente trabalha no Centro de Processamento de Dados da Prefeitura Municipal de Itaí-SP. Possui as seguintes certificações: MCP - Microsoft Certified Professional, MCTS - Microsoft Certified Technology Specialist, MCAD - Microsoft Certified Application Developer e MCSD - Microsoft Certified Solution Developer. [email protected] Figura 05: Carregando o Arquivo .txt. 20 agosto 2012 Delphi Parte V Vimos no artigo anterior, o inicio dos exemplos sobre banco de dados. Nesse artigo veremos mais dicas sobre desenvolvimento de aplicações cliente/server com dbExpress no Delphi, onde a principal será a parametrização dos cadastros. Imagine o nosso cadastro de clientes tenha algo em torno de 500 registros, o que não é nada de tão absurdo. Pelo que construímos até agora, teríamos o DBNavigator para navegar entre os registros, o que não tem nenhuma facilidade. Vamos alterar o projeto para que as consultas sejam parametrizadas e possamos modificar o cadastro. Teremos assim, performance da aplicação pois teremos em memória apenas os registros necessários para a edição dos dados. Parametrizando as consultas agosto 2012 21 Figura 1. Configurando o parâmetro do cadastro de cliente Para parametrizar as consultas precisamos modificar a consulta SQL adicionando o parâmetro que desejamos. Veja no código a seguir, como ficará a consulta para o cliente. select nCdCliente, sNmCliente, tDtNascimento, nVlLimite from CLIENTE where nCdCliente = :nCdCliente Assim, criamos um parâmetro para a consulta (basta ter dois pontos antes). Ao fechar o editor, a propriedade Params do SQLDataset mapeia os parâmetros adicionados no SQL (Figura 1). Configure o parâmetro para o tipo correto, nesse exemplo, ftInteger. Precisamos “repassar” esse parâmetro para o ClientDataSet. Para isso, basta clicar com o botão direito no componente e escolher a opção Fetch Params. Pronto. Para conferir, acesse a propriedade Paras do cdsCliente, ela deve ter a mesma configuração do dsCliente. Agora, se abrirmos a aplicação você notará que nenhum registro é mostrado. Obviamente, devido a mudança do SQL. Como podemos então mostrar os dados? Precisamos ter uma consulta auxiliar para buscarmos o registro que precisamos mudar. Isso já introduz um conceito importante no nosso projeto, o de performance. Não precisamos retornar todos os agosto 2012 Figura 2. Usando abas no cadastro de clientes lário e insira dois TabSheets. Crie uma aba com o texto: “Dados gerais” e outra como “Pesquisa”. Na aba Dados gerais adicione os controles de tela (podemos remover o DBNavigator). Veja na Figura 2 como ficou o cadastro. Figura 3. Aba de pesquisa no cadastro de cliente registros do cadastro (imagine 5, 10 mil registros) para modificar um apenas. Vamos retornar para o cadastro, apenas o registro que desejamos alterar. Modificando o cadastro de cliente Precisamos mudar o layout do cadastro de clientes. Precisamos adicionar uma pesquisa para que possamos retornar o registro que o usuário vai alterar. Vamos criar abas para o cadastro, onde teremos uma para o cadastro em si e outra para um grid de pesquisa. Adicione um PageControl no formu- Veja que nosso cadastro ficou mais bonito. Agora, precisamos configurar a aba de pesquisa. Adicione um Edit, um Grid e um DataSource. Faça a ligação do DataSource com o grid. Veja na Figura 3 a aba de pesquisa. Precisamos configurar uma consulta para ser usado na pesquisa do cadastro. Nesse tipo de pesquisa, não precisamos retornar uma quantidade grande de dados, precisamos apenas o código e um campo referente a descrição do cadastro (neste caso, o nome do cliente). Vamos criar outro Data Module para conter os componentes para as pesquisas que vamos usar na aplicação. Fica a seu critério para usar apenas um Data Module, apenas atente para colocar o novo Data Module a ser criado na inicialização do projeto. Pesquisas auxiliares Crie um novo Data Module e adicione um SQLDataSet, um DataSetProvider e um ClientDataSet. Veja os componentes dispostos na Figura 4. Figura 4. Data Module para os components de pesquisa from CLIENTE where UPPER(sNmCliente) like :sNmCliente Veja que retornamos apenas dois campos e usamos o like, pois vamos fazer uma pesquisa parcial pelo nome do cliente. Configure o parâmetro para o tipo ftString. Pesquisa parcial Aperte Alt + F11 para abrir o editor Use unit. Escolha DM. Você deve estar perguntando: “Nos componentes de pesquisa, também preciso ter um trio para cada pesquisa?”. E a resposta é? Não. Se vamos utilizar o ClientDataSet para pesquisar, não precisamos do trio, pois os outros componentes tem a responsabilidade de incluir dados no banco, como já comentamos. Precisamos apenas configurar os componentes, como fizemos antes (vincular os componentes via propriedades) e modificar uma propriedade do DataSetProvider para que o ClientDataSet possa gerenciar a consulta SQL para trazer os dados para o componente. Altere para true a propriedade Options > poAllowCommandText do dspPesquisa. Agora, a consulta SQL para retornar os dados, vamos escrevê-la na propriedade CommandText do cdsPesquisaCliente. Note que não temos um editor como o SQLDataSet, mas podemos digitar o SQL. Digite o seguinte comando: select nCdCliente, sNmCliente Agora vamos mudar nossa tela de cadastro. Configure o DataSource para o cdsPesquisaCliente. No evento OnKeyDown do Edit, digite o código da Listagem 1. Listagem 1. Executando a consulta com parâmetros if (Key = VK_RETURN) then begin DMPesquisa. cdsPesquisaCliente.Close; DMPesquisa. cdsPesquisaCliente. Params[0].AsString := UpperCase(‘%’+ edtPesquisa.Text + ‘%’); DMPesquisa. cdsPesquisaCliente.Open; end; Veja que verificamos se o usuário usou a tecla ENTER, para executar o código. A seguir, fechamos o ClientDataSet e preenchemos o parâmetro configurado. Note que como usamos o comando Upper no SQL, também precisamos repassar o valor digitado em maiúsculo para uma perfeita comparação. É importante notar, o tipo do parâmetro (string). Precisamos configurar o tipo correto, então usamos o AsString. No ClientDataSet do cadastro, o parâmetro é do tipo inteiro, então, quando fizermos essa filtragem precisamos usar a configuração correta (AsInteger). Por fim, chamamos o Open para mostrar os dados retornados. Execute a aplicação e faça pesquisas na aba. Ao entrar na aba e apertar ENTER, todos os registros são retornados. Poderíamos fazer uma restrição para que o usuário não possa fazer pesquisa sem digitar uma quantidade mínima de caracteres. Isso auxilia, pois caso o cadastro possua muitos registros, o trafego da rede seria grande. O código seria: if (Key = VK_RETURN) then begin if Length(edtPesquisa. Text) < 3 then ShowMessage(‘Digite no mínimo 3 caracteres.’) else ... Digite algum valor e aperte ENTER e visualize os registros que atendem ao parâmetro digitado (Figura 5). Figura 5. Cadastro com pesquisa auxiliar Agora, precisamos configurar para que ao usuário dar um duplo clique no grid, ao escolher um registro, possamos filtrar os dados para o cadastro e mostrar o mesmo ao usuário. No evento OnDblClick do grid, adicione o código da Listagem 2. Listagem 2. Filtrando o registro escolhido no grid if (DMPesquisa. cdsPesquisaCliente. RecordCount > 0) then begin DM.cdsCliente.Close; DM.cdsCliente. Params[0].AsInteger := agosto 2012 DMPesquisa. cdsPesquisaCliente. FieldByName(‘nCdCliente’). AsInteger; DM.cdsCliente.Open; pgCadastro.ActivePage := tabDados; end; Primeiro, verificamos se existem registros no grid para executar a filtragem. Preenchemos o parâmetro do componente de cadastro, com o valor do código presente no componente de pesquisa. Por fim, mostramos a aba de cadastro. Execute e teste a aplicação (Figura 6). Figura 6. Pesquisa e filtragem do cadastro Modificando o cadastro de empregados Você pode pensar, as modificações no cadastro de empregados têm as mesmas características do cadastro de clientes. Em parte sim. Precisamos criar a pesquisa, alterar a consulta do cadastro, criar parâmetro e alterar o layout. Mas se você recordar, no cadastro de empregados, temos um relacionamento com o setor, onde adicionamos um ComboBox para o usuário escolher o setor. OK, o cadastro de setor é pequeno, não valeria a pena a modificação, mas nesse caso, vou mostrar a alteração para fins de conhecimento. 24 agosto 2012 Vamos remover o controle (ComboBox) e vamos criar uma consulta auxiliar (com uma nova tela) para o usuário pesquisar o setor que deseja. Assim, nosso cadastro estará parametrizado, otimizando a performance do mesmo. Precisamos fazer uma modificação na consulta de empregados, para trazer, o nome do setor, pois temos que mostrar o nome (caso o cadastro tenha relacionamento) para o usuário. A Listagem 3 mostra o novo comando SQL do dsEmpregado no DM. Listagem 3. Alterando a consulta de empregado para retornar o setor select EMPREGADO. nCdEmpregado, EMPREGADO.nCdSetor, EMPREGADO. nPcComissao, EMPREGADO. sNmEmpregado, EMPREGADO. tDtAdmissao, SETOR.sNmSetor from EMPREGADO INNER JOIN SETOR ON SETOR. nCdSetor = EMPREGADO. nCdSetor where EMPREGADO. nCdEmpregado = :nCdEmpregado Nota: Qualquer modificação feita no editor do SQL ou mesmo diretamente no comando, através da propriedade CommandText, o parâmetro deve ser reconfigurado, tanto no SQLDataSet, quanto no ClientDataSet. Agora, precisamos configurar corretamente o Field do novo campo (sNmSetor), para que o dbExpress não tente atualizar o mesmo quando for salvar o registro. Desmarque todas as opções marcadas para o TField sNmSetor, assim, o dbExpress não tentará inserir/atualizar o mesmo. Na tela de cadastro, adicione um DBText para mostrar o nome do setor (Figura 7). Figura 7. Alterando o campo Setor no cadastro de empregado Após as alterações, faça testes na aplicação para visualizar o nome do setor na pesquisa (Figura 8). Figura 8. Mostrando o nome do setor no cadastro Agora, e quando quisermos alterar o setor? Vamos criar uma tela de pesquisa de setor. Crie um novo formulário e adicione os seguintes componentes: RadioGroup, DBGrid, Edit, Labels. Monte o formulário conforme a Figura 9. Crie um componente para pesquisa de setor, semelhante aos de pesquisa de cliente e empregado. Vincule o componente ao DataSource da tela, e esse ao DBGrid. No evento OnKeyDown do Edit, digite o código da Listagem 4. Listagem 4. Código para pesquisa de setores if Key = VK_RETURN then begin Result := true; except Result := false; end; end; Qual a lógica da função. Executamos o comando que transforma uma string em número, dentro de um bloco try...exception, que caso ocorra erro, o código que será executado estará dentro do except. Assim, se nenhum erro ocorrer, o result será verdadeiro, senão, será falso. Figura 9. Tela de pesquisa de setor Agora, no OnDblClick do grid para fecharmos o formulário com o seguinte código: DMPesquisa. cdsPesquisaSetor.Close; DMPesquisa. cdsPesquisaSetor. CommandText := ‘’; if RadioGroup1.ItemIndex = 0 then begin if IsNumero(edtPesquisa.Text) then DMPesquisa. cdsPesquisaSetor. CommandText := ‘select nCdSetor, sNmSetor from SETOR where nCdSetor = ‘ + edtPesquisa.Text else ShowMessage(‘Digite um número para pesquisar por código’); end else begin if Length(edtPesquisa. Text) < 3 then ShowMessage(‘Digite no mínimo 3 caracteres.’) else DMPesquisa. cdsPesquisaSetor. CommandText := ‘select nCdSetor, sNmSetor from SETOR where UPPER(sNmSetor) like ‘ + UpperCase(QuotedStr(‘%’ + edtPesquisa.Text + ‘%’)); end; DMPesquisa. cdsPesquisaSetor.Open; end; if DMPesquisa. cdsPesquisaSetor. RecordCount > 0 then begin Close; ModalResult := mrOk; end; Note que de acordo com o que for escolhido no RadioGroup, mudamos o conteúdo do SQL passado para a propriedade CommandText. Para a pesquisa parcial (pelo nome do setor), implementamos uma validação como já mostramos anteriormente. O comando QuotedStr, adiciona aspas simples (‘’) na consulta SQL, pois estamos trabalhando com string. Verificamos se existem registros no componente de pesquisa, e depois o primeiro código fecha o formulário de pesquisa, já o segundo, indica o resultado que será verificado no formulário que chamará a tela de pesquisa. Para o código, criamos uma function que retorna se o valor digitado é um número para que não ocorra erro quando pesquisarmos por código, sendo o que foi digitado é uma string. Veja na Listagem 5 o código do IsNumero. Listagem 6. Código para chamar o formulário de pesquisa Listagem 5. Função para validar se o valor é número function IsNumero(sNumero: string): boolean; begin try StrToInt(sNumero); Para entender, adicione um botão no cadastro de empregados. Adicione o código da Listagem 6. Application.CreateForm (TfrmPesquisarSetor, frmPesquisarSetor); try frmPesquisarSetor. ShowModal; if frmPesquisarSetor. ModalResult = mrOk then begin DM.cdsEmpregado.Edit; agosto 2012 25 ficará o layout dos cadastros. Na Listagem 7 temos o código dos botões. Listagem 7. Código dos botões Novo DM.cdsEmpregado.Insert; Figura 10. Alterando o setor com a pesquisa auxiliar DM.cdsEmpregadosNmSetor. AsString := DMPesquisa. cdsPesquisaSetor. FieldByName(‘sNmSetor’). AsString; DM.cdsEmpregadonCdSetor. AsInteger := DMPesquisa. cdsPesquisaSetor. FieldByName(‘nCdSetor’). AsInteger; end; finally frmPesquisarSetor.Free; end; Salvar try DM.cdsEmpregado.Post; ShowMessage(‘Registro salvo com sucesso.’); except on E: Exception do ShowMessage(‘Ocorreu um erro: ‘ + E.Message); end; Excluir if (MessageDlg(‘Deseja excluir?’, mtConfirmation, [mbYes, mbNo], 0) = mrYes) Após a chamada do formulário (ShowModal) verificamos o retorno do formulário através da propriedade ModalResult. Se o usuário não escolher nenhum registro, fechar o formulário, não precisamos executar o código para alterar o nome do setor. Caso o mesmo escolha um registro no grid, vamos chamar o método Edit do ClientDataSet de cadastro e atribuindo os valores para o nome do setor e o código do setor do cadastro de empregados. Veja na Figura 10 temos a aplicação em execução. Finalizando as alterações Pra finalizar, precisamos implementar os botões para inserir um novo, salvar ou excluir os registros da tela. Veja na Figura 11 como 26 agosto 2012 Figura 11. Botões de cadastro then DM.cdsEmpregado.Delete; O botão Novo simplesmente chama o Insert do ClientDataSet. Já no Salvar, colocamos um bloco try...except para que caso ocorra algum erro, uma mensagem com a descrição do erro, seja mostrada ao usuário. O Excluir pede a confirmação da exclusão para o usuário e caso afirmativo, exclui o registro. Para excluir o registro no banco, precisamos chamar o mesmo código que usamos no evento AfterPost visto no artigo anterior. No DM, adicione o seguinte código no evento AfterDelete do cdsEmpregado (faça o mesmo para o cdsCliente): cdsEmpregado. ApplyUpdates(0); Faça todos os testes na aplicação: novo registro, salve e exclua. Faça as modificações no outro cadastro. Novo cadastro e o que vem pela frente Agora que já aprendeu como fazer um cadastro simples parametrizado e como usar pesquisa auxiliar no cadastro, implemente o mesmo para a tabela PRODUTO. Adiantando o que teremos no próximo artigo, vamos criar um cadastro máster/detail, com o cadastro de vendas e seus respectivos itens. Lembrando, para incluir os registros da tabela ITENS, precisamos ter o código da venda, presente na tabela VENDA, ou seja, precisamos incluir uma venda, recuperar seu código e inserir o mesmo quando salvar o item. Como fazer isso? Dados em memória com o ClientDataSet. Além disso, precisamos de uma transação nisso, pois não podemos ter ITENS sem a VENDA e nem a VENDA sem seus ITENS. Veja que temos muito a aprender ainda. Você deve estar perguntando: “Luciano, você sempre fala que o Delphi é produtivo, tem facilidades da programação OO, mas pelo que já vi até agora, tivemos dois cadastros (e teremos o terceiro), onde precisamos refazer layout e algumas validações, tipo replicando Conclusão código”. Veremos nos próximos artigos algo bastante interessante e produtivo para aplicações com Delphi, a facilidade que temos em criar telas de cadastros: a herança visual. Apenas para adiantar a ideia: teremos uma tela de cadastro padrão, com um layout (por exemplo, as abas e botões) comum para qualquer cadastro. Nessa tela padrão, teremos código bastante genérico para ser usado independente de estarmos no cadastro de cliente, empregado ou qualquer outro. Vale a pena esperar. Vimos nesse artigo, como utilizar de maneira correta, a criação de cadastros parametrizados no Delphi para aplicações cliente/ server. No próximo artigo, vamos criar uma característica muito comum em aplicações, o cadastro máster/detail. Um grande abraço a todos e até a próxima! Sobre o autor Luciano Pimenta É Técnico em Processamento de Dados, desenvolvedor Delphi/C# para aplicações Web com ASP.NET e Windows com Win32 e Windows Forms. Palestrante da 4ª edição da Borland Conference (BorCon). Autor de mais de 60 artigos e de mais de 300 vídeos aulas publicadas em revistas e sites especializados. É consultor da FP2 Tecnologia (www.fp2.com.br) onde ministra cursos de programação e banco de dados. É desenvolvedor da Paradigma Web Bussiness em Florianópolis-SC. www.lucianopimenta.net agosto 2012 27 Dicas DELPHI DBGrid com o scroll do mouse Esta dica como o próprio título já propõem, se trata de trazer a funcionalidade do scroll do mouse para o componente DBGrid, facilitando assim a visualização dos registros, o que se torna um atrativo a mais para a aplicação aos olhos do usuário final. Outra boa prática a ser utilizada, é a de criar esta funcionalidade num formulário base, ou seja, um formulário que servirá de base a outros formulários da aplicação, isto irá garantir que qualquer formulário que descender do formulário base tenha esta funcionalidade herdada. Vamos então à codificação, iremos criar um procedimento para manipular uma mensagem que é enviada da aplicação ao Windows. Vamos partir da premissa que já tenha no formulário do Delphi um DBGrid conectado a um DataSource, este que por sua vez está ligado a um ClientDataSet que já esteja alimentando com os registros de uma tabela. Então declare na sessão private da unit o procedimento. private { Private declarations } procedure MsgWmKeyDown(var Msg: TMsg; var Handled: Boolean); Após declarar o procedimento MsgWmKeyDown pressione as teclas (Crtl + Shift + C) para gerar o corpo do procedimento onde será implementado o seu código. Neste procedimento verifica se a mensagem é WM_MOUSEWHEEL e logo em seguida verifica se o controle ativo é da classe TDBGrid. Depois vem os comandos para a ação do scroll do mouse. Faça a chamada deste procedimento no evento onCreate do formulário da seguinte forma: procedure TForm1.FormCreate(Sender: TObject); begin Application.OnMessage := MsgWmKeyDown; end; Após adicionar o procedimento ao evento OnMessage da aplicação o exemplo está pronto e basta agora fazer seus testes, execute a aplicação e clique na DBGrid para ver a funcionalidade do exemplo. Texto em 3D no Delphi com canvas Com esta dica você poderá desenhar um texto em 3D no formulário com o canvas. Declare na sessão Private o seguinte código: procedure imgPaintCanvas(RefCanvas: TCanvas; Texto: String; TamFonte, pHorizontal, pVertical: Integer); Depois tecle Ctrl + Shift + C para gerar o corpo da procedure, e digite o seguinte código dentro do bloco begin e end: procedure TForm1.MsgWmKeyDown(var Msg: TMsg; var Handled: Boolean); var s: Smallint; begin if Msg.message = WM_MOUSEWHEEL then if (ActiveControl is TDBGrid) then begin Msg.message := WM_KEYDOWN; Msg.lParam := 0; s:= HiWord(Msg.wParam); if s > 0 then Msg.wParam := VK_UP else Msg.wParam := VK_DOWN; end; end; 28 agosto 2012 procedure TfrmTexto3D. imgPaintCanvas(RefCanvas: TCanvas; Texto: String; TamFonte, pHorizontal, pVertical: Integer); Begin RefCanvas.Brush.Style := bsClear; RefCanvas.Font.Style := [fsBold]; RefCanvas.Font.Name := ‘Courier New’; RefCanvas.Font.Size := TamFonte; RefCanvas.Font.Color := clBlack; RefCanvas.TextOut(pHorizontal, pVertical, Texto); RefCanvas.Font.Color := clGray; RefCanvas.TextOut(pHorizontal - 1, pVertical - 1, Texto); RefCanvas.Font.Color := clSilver; RefCanvas.TextOut(pHorizontal - 2, pVertical - 2, Texto); RefCanvas.Font.Color := clBlack; RefCanvas.TextOut(pHorizontal - 3, pVertical - 3, Texto); end; Como podemos ver os parâmetros são de fácil compreensão, o RefCanvas se refere a qual propriedade de formulário será preenchida com o desenho, TamFonte se refere ao tamanho da fonte, pHorizontal e pVertical se referem sucessivamente ao ponto horizontal e vertical ao qual irá iniciar o desenho. Para fazer o desenho em 3D, basta agora chamar este procedimento em algum evento do formulário passando os parâmetros corretamente, neste exemplo isso foi feito através de um clique de um TButton, veja o como foi feito a sua chamada na listagem abaixo: procedure TfrmTexto3D.Button1Click(Sender: TObject); begin Texto3DCanvas(frmTexto3D.Canvas, ‘THE CLUB O MAIOR CLUBE DE PROGRAMADORES DO BRASIL’,15, 50, 50); end; Ao visualizar com mais atenção o código do procedimento, fica claro que o mesmo pode ser customizado para atender qualquer cenário, espero que esta dica ajude de alguma forma até mais. Converter número inteiro para ordinal por extenso Esta dica se resume em uma função que recebe um parâmetro do tipo inteiro de no máximo três caracteres, para transcrevê-los em números ordinais por extenso, estes que serão o retorno da função do tipo String. Declare a função antes da diretiva Private. function NumOrdinal(Numero: Integer): String; private Tecle Ctrl + Shift + C para gerar o corpo da função. Serão declaradas algumas variáveis e constantes para poder manipular os dados corretamente, veja na seguinte listagem a implementação da função. function TfrmNumerosOrdinais. NumOrdinal(Numero: Integer): String; var Conta : smallint; StrNumero, Texto : string; const Unidades: array[0..9] of string = (‘’, ‘Primeiro ‘, ‘Segundo ‘, ‘Terceiro ‘, ‘Quarto ‘, ‘Quinto ‘, ‘Sexto ‘, ‘Sétimo ‘, ‘Oitavo ‘, ‘Nono ‘); Dezenas: array[0..9] of string = (‘’, ‘Décimo ‘, ‘Vigésimo ‘, ‘Trigésimo ‘, ‘Quadragésimo ‘, ‘Quinquagésimo ‘, ‘Sexagésimo ‘, ‘Setuagésimo ‘, ‘Octogésimo ‘, ‘Novagésimo ‘); Centenas: array[0..9] of string = (‘’, ‘Centésimo ‘, ‘Ducentésimo ‘, ‘Tricentésimo ‘, ‘Quadringentésimo ‘, ‘Quingentésimo ‘, ‘Sexcentésimo ‘, ‘Septingentésimo ‘, ‘Octingentésimo ‘, ‘Noningentésimo ‘); begin StrNumero := trim(IntToStr(Numero)); for Conta:= length(StrNumero) downto 1 do Begin if length(StrNumero) - Conta = 0 then Texto := Unidades[strtoint(copy(StrN umero, Conta, 1))]; if length(StrNumero) - Conta = 1 then Texto := Dezenas[strtoint(copy(StrNu mero, Conta, 1))] + texto; if length(StrNumero) - Conta = 2 then Texto := Centenas[strtoint(copy(StrN umero, Conta, 1))] + texto; if length(StrNumero) - Conta > 2 then Texto := ‘Não sabe contar tanto, ainda. / ‘ + Texto; end; result:=texto; end; Para exemplificar a utilização desta função será adicionado no formulário um TLabel, um TEdit e um TButton, veja na listagem Abaixo como fica a chamada da função: procedure TfrmNumerosOrdinais.Button1Click(Sender: TObject); begin if (Edit1.Text = ‘’) then Abort; Label1.Caption := NumOrdinal(StrToInt(Edit1.Text)); end; Esta dica facilita o manuseio de números inteiros convertendo de maneira bem fácil para uma String de maneira ordinal. Espero ter ajudado com esta dica, até a próxima. agosto 2012 29 Horizontal 30 agosto 2012 Vertical agosto 2012 agosto 2012