Outubro 2009 Outubro 2009 índice Editorial Delphi InfoNews 04 Qual desenvolvedor nunca se apavorou só com a hipótese de ter perdido uma base de dados do cliente? E quando este pesadelo se tornou realidade, quantas horas de... Delphi Rapidinhas Autor: Vitor Manuel Rodrigues 05 Autor: Mateus André Chies Delphi 12 06 Sistema de contatos com mala direta .NET 16 20 Journaling System - A solução definitiva contra perda de dados parte I Explorando Records em Delphi 2009 Herança Visual de formulários em Windows Forms Autor: Felipe Santos Autor: Antonio Spitaleri Autor: Luciano Pimenta .NET Dicas Delphi 26 Acesso a dados com JQuery Autor: Djonatas Tenfen 29 - Alterar propriedade buttonstyle das toolbars ligadas ao actionmanager em tempo de execução: - Criando uma... Desafio The Club - Sudoku Legenda 30 Iniciante Intermediário Avançado Outubro 2009 03 Bem-vindo Olá amigos Qual desenvolvedor nunca se apavorou só com a hipótese de ter perdido uma base de dados do cliente? E quando este pesadelo se tornou realidade, quantas horas de trabalho e às vezes até de sono formam perdidas, muitas vezes sem sucesso, tentando recuperar os precisos dados de nosso cliente. Assim neste mês em nossa matéria de capa “Journaling System – A solução definitiva contra perda de dados”, Felipe Santos aborda no caso de falhas ambientais que possam corromper nossos banco de dados, a prevenção, recuperação e restauração do banco de dados Interbase o uso da tecnologia Journaling System. Nosso consultor Antonio Spitaleri com seu artigo “Explorando Records em Delphi 2009”, dá uma aula conceitual sobre as estruturas do tipo records, discutindo desde detalhes técnicos de sua criação e alocação de memória até comparações com o uso de classes. Av. Profº Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150 Informações: (14) 3732-1529 Suporte: (14) 3733-1588 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 Agora para os programadores iniciantes que sempre pedem projetos de exemplo, algo que possam trazer para o mundo real, o nosso colunista Mateus André Chies, criou um projeto de exemplo que se transformou no artigo “Sistema de contatos com mala direta”, neste artigo Mateus mostra, posso a posso, desde a criação do banco de dados ate a configuração dos componentes, além é claro de como criar uma solução para organizar nossos contatos. Copyright The Club Megazine 2009 E nossa sessão .Net Luciano Pimenta mostras uma técnica muita usado pelos programadores Delphi agora em .net, que é o uso do conceito de Herança da Programação Orientada a Objetos (POO), para o reaproveitamento de formulários com características semelhantes como é o caso de formulários de cadastro, neste seu artigo “Herança Visual de formulários em Windows foms”, Luciano dá uma ótima contribuição para os programadores novatos em .Net que pretendem iniciar novos projetos com herança visual. Revisão Tassiane Fileto Também em nossa sessão .Net temos também o artigo “Acesso a dados com JQuery” de nosso colunista Djonata Tenfen, que segue na sua seqüência de artigos abordando a tecnologia JQuery. Diretor Técnico Marcos César Silva Diagramação e Arte Vitor M. Rodrigues Colunistas Antonio Spitaleri Neto Djonatas Tenfen Felipe Santos Luciano Pimenta Mateus André Chies Vitor M. Rodrigues Impressão e acabamento: GRIL - Gráfica e Editora Taquarituba-SP - Tel. (14) 3762-1345 Reprodução Boa leitura e até o próximo mês, abraço a todos. Marcos César Silva - Editor Chefe [email protected] 04 Outubro 2009 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. InfoNews Rapidinhas Nota Fiscal Eletrônica A vilã do momento é a NFe. Alguns já estão utilizando, outros estão implantando e muitos ainda terão de implantar. A dica é simples, não deixe para última hora, se o seu sistema não está integrado com a NFe, e se nenhum cliente solicitou essa é hora de correr atrás. Os contadores estão avisando os empresários em cima da hora sobre a obrigatoriedade de implantação da NFe, e ainda estão jogando toda a responsabilidade em cima dos desenvolvedores, tirando o corpo fora da questão. E como já bem sabemos, para o cliente “tudo é fácil, é só um botãozinho”, e sabemos que a coisa não é assim. Delphi no Windows 7 Estamos testando já há algum tempo o Delphi no Windows 7 e os resultados são positivos, ele se mostra um sistema estável, ágil e segundo nossos técnicos está rodando o Delphi sem problema. Diferente do que acontece no Windows Vista. Portanto se alguém ainda estava com medo de instalar o Windows 7, pode rodar sem medo. Então se prepare, já avise seus clientes sobre como será o processo de implantação, custos adicionais e informações sobre a NFe. 1º Delphi Conference Corre a informação de que a Embarcadero promoverá um encontro para os desenvolvedores Delphi será o “1º Delphi Conference”, até o fechamento desta edição ainda não havia sido divulgado data nem local, o que se sabe é que será no mês de novembro de 2009 e que tem vários nomes de peso para apresentação das palestras. Então se prepare, pois esse evento promete muito. Outubro 2009 05 Delphi Sistema de Contatos com Mala Direta Listagem 1: Criação do Banco de Dados Neste artigo irei demonstrar uma maneira fácil e útil de se armazenar os contatos dos cartões pessoais que recebemos de empresa e pessoas. Muitas vezes guardamos em gavetas, onde muitos são extraviados, ou os mais organizados compram porta cartões para guardar os cartões de visitas. Criando o Banco de Dados Vamos lá, a base do sistema será em banco de dados Firebird 1.5, utilizando Delphi 2007 para o desenvolvimento, vamos modelar conforme a listagem 1, onde temos a criação e definição do GENERATOR, posteriormente temos a tabela e seus atributos com seus respectivos tipos de dados, ainda temos a criação da chave primária e do gatilho que é acionado no evento de Inserir os dados na tabela. 06 Outubro 2009 ****Generated by IBExpert****/ SET SQL DIALECT 3; SET NAMES NONE; SET CLIENTLIB ‘fbclient. dll’; CREATE DATABASE ‘127.0.0.1:C:\BANCO\ CONTATOS.FDB’ USER ‘SYSDBA’ PASSWORD ‘masterkey’ PAGE_SIZE 4096 DEFAULT CHARACTER SET NONE; /****Generators****/ CREATE GENERATOR GEN_ CONTATOS_ID; SET GENERATOR GEN_CONTATOS_ ID TO 8; /****Tables****/ CREATE TABLE CONTATOS ( CODIGO INTEGER NOT NULL, NOME VARCHAR(100), ENDERECO VARCHAR(200), NUMERO BAIRRRO CEP CIDADE UF FONE FAX CELULAR EMAIL SITE VARCHAR(250), INTEGER, VARCHAR(30), VARCHAR(9), VARCHAR(60), VARCHAR(2), VARCHAR(13), VARCHAR(13), VARCHAR(13), VARCHAR(75), ATIVIDADE VARCHAR(200) ); /****Primary Keys****/ ALTER TABLE CONTATOS ADD CONSTRAINT PK_CONTATOS PRIMARY KEY (CODIGO); /****Triggers for tables****/ CREATE TRIGGER CONTATOS_BI FOR CONTATOS ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.CODIGO IS NULL) THEN NEW.CODIGO = GEN_ ID(GEN_CONTATOS_ID,1); END ^ SET TERM ; ^ Construindo nossa aplicação Crie uma nova aplicação no Delphi 2007 através do menu File, New, VCL Forms Application, defina o width para 850 e o Heigth para 456 junto a palheta Object Inspector, agora salve sua aplicação utilizando a teclas de atalho Ctrl + Shift + S, para o .pas salve com uPrincipal, e para o projeto Contatos. Vamos começar montar o Layout da nossa aplicação, adicione 2 TPanel da palheta Standart da aba Tool Palete, remova o caption das duas Panel, e para uma defina a propriedade Align para alTop e a outra para alBottom, adicione um componente TPageControl da paleta Win32 e defina a propriedade Align para alClient, clique no centro PageControl e adicione duas páginas, clicando de direita e acessando o menu New Page, para a primeira altere o caption para “Contatos” e a segunda para “Dados”. Adicione um componente Tlabel da paleta Standart no panel superior, e defina a fonte através da propriedade Font da paleta object Inspector para tamanho 16pt, e estilo da fonte para negrito e altero o Caption para “Lista de Contatos”. Selecione a página de contatos e adicione uma Tpanel, remova o caption e altere o align para alTop, adicione 4 Tlabel, 1 TEdit e mais um BitBtn da paleta Additional, altere o align do BitBtn para alRigth e o caption para “Imprimir Mala Direta”, para o primeiro label altere o caption para “Dados da Consulta:”, posicione o Edit logo abaixo do primeiro label, e altere o name para “edt_consulta”, posicione a segunda label logo abaixo do Edit e altere o caption para “Consultando por:”, posicione o terceiro label logo a direta o label anterior, altere o caption para “Selecione uma coluna clicando no título”, o name para “lbl_consulta” e a Font para cor vermelho, e estilo para negrito. Posicione o quarto label para próximo ao BitBtn de Imprimir, altere o name para “lbl_total”, nesse label vamos mostrar o total de registros. No centro dessa página vamos adicionar um TDbGrid da paleta Data Controls, e altere o align para alClient. Muito bem, agora vamos configurar nossa conexão, adicione um TsqlConnection da paleta dbExpress, altere o name para “CON”, altere a propriedade LoginPrompt para “False”, e depois configure a conexão com o banco de dados criado. Adicione TSQLDataSet da mesma paleta, altera a propriedade name para “SQL”, configure o SQLConnection para “CON”, e na propriedade CommandText adicione o código da listagem 2, que é a instrução SQL para selecionarmos os dados do bando de dados criado. Listagem 2: Command Text select * from CONTATOS Feito essas configurações, vamos adicionar os campos no nosso SQL, duplo clique sobre o componente abrira uma janela, nessa janela clique de direita e acione o menu Add All Fields, agora é possível visualizar todos os campos da nossa tabela no banco de dados, selecione o campo CODIGO, acesse a propriedade ProviderFlags, pfInKey e altera para “true”, nosso SQL está pronto. Agora adicione um TDataSetProvider da paleta Data Access, altere o name para “DSP” e configure o DataSet para “SQL”. Também adicione um TClientDataSet, altere o name para “CDS”, relacione a propriedade ProviderName com o DSP, duplo clique sobre ele e botão direito do mouse, Add All Fields, agora já temos todos os campos no nosso CDS, selecionando um campo de cada vez é possível altera a propriedade DisplayLabel para o Label que você deseja que aparece na DbGrid e junto aos campos, mas tome cuidado, não altere a propriedade FieldName. Agora ative o nosso CDS. E por último adicione um TDataSource da paleta DataAccess, altere o name para “DS” e configure a propriedade DataSet para “CDS”. Agora basta ligar a propriedade DataSource da DBGrid com o nosso DataSouce DS e já é possível visualizar os nosso campos na DBGrid. Vamos automatizar nosso operações, adicione um componente TImageList da paleta Win32, duplo clique sobre ele e através do botão Add adicione uma imagem relacionada a cada uma das seguintes operações Inserir, Editar, Cancelar, Salvar e Excluir. Mais tarde iremos utilizar essas imagens, para fins visuais. Adicione um componente TActionList da paleta Standart, relacione a propriedade Images a nossa Lista de Imagens TImageList1. Através dessa lista de ações é que vamos acionar nossas transações com o banco de dados. Duplo clique sobre o Outubro 2009 07 componente, abrirá uma nova janela, acesse New Standart Action, vamos adicionar as nossa ações, abrirá uma nova janela, role até encontrar o nó Dataset, com a tecla Ctrl pressionada selecione os seguintes itens, TDataSetInsert, TDataSetDelete, TDataSetEdit, TDataSetPoste e TDataSetCancel, clique em Ok. Agora temos a lista das nossas ações, selecione DataSetInsert1 altere o caption para Inserir, relacione a propriedade DataSource ao DS, altere a propriedade Hint para Inserir e na propriedade ImageIndex selecione a imagem relacionada ao Inserir, agora basta repetir essas configurações para as demais ações, adequando-as as suas respectivas funcionalidade, nome e imagem. Figura 1: Formulário Adicione 5 TBitBtn na panel inferior, configure todos com Width 100 e heigth para 47, e respectivamente da esquerda para direta, altera a propriedade Action para as ações da lista de ações para Inserir, Editar, Cancelar, Salvar e Excluir. Salve o projeto. Ainda nessa panel inferior adicione um componente TDBNavigator, relacione o DataSource ao DS, e na propriedade VisibleButtons configure para true apenas os botões First, Prior, Next e Last, que são Primeiro, Anterior, Próximo e Último respectivamente, os demais altere para false. O formulário ficou similar a figura 1. Veja a Figura 1: Formulário Figura 2: Campos Selecione o PageControl e clique na aba Dados, agora vamos adicionar os campos ao formulário, duplo clique sobre o CDS e selecione todos os campos, e os arraste para o formulário e organize a disposição conforme a figura 2. Veja a Figura 2: Campos Mas você deve estar se perguntado, - Mas não vamos programar nada? Calma, vamos agora fazer as implementações. Selecione o Form acesse o evento FormShow e vamos abrir nosso CDS, forçar a sempre abrir com o PageControl na página, que tem o DBGrid e adicionar a label lbl_total o total e registro que tem selecionado no DBGrid, implementado a listagem 3. 08 Outubro 2009 Listagem 3: FormShow DS.DataSet.Open; PageControl1.ActivePage := TabSheet1; PageControl1. ActivePageIndex := 0; lbl_total.Caption := IntToStr(cds.RecordCount)+’ Regsitro(s)’; Selecione o ClientDataSet, no evento AfterPost, implemente a codificação da listagem 4, e no evento AfterDelete relacione com o evento AfterPost, essa implementação tem a função de grava no banco de dados cada vez que se aciona o botão Salvar ou Excluir. Listagem 4: AfterPost CDS.ApplyUpdates(0); CDS.Close; CDS.Open; Vamos fazer as implementações na DBGrid, no evento DblClick apenas codifique para acionar o botão de alterar, para quando estiver navegando sobre os registros e der duplo clique nele automaticamente entrará em modo de edição. Ainda na DBGrid no evento TitleClick, vamos implementar a listagem 5, para índex os dados pela coluna que foi selecionada, ainda preencher a lbl_consulta com o nome do campo selecionado, que servirá no filtro das informações. Listagem 5: TitleClick try (DS.DataSet as TClientDataSet). IndexFieldNames := Column. FieldName; lbl_consulta.Caption := Column.FieldName; edt_consulta.SetFocus; except ShowMessage(‘Essa coluna não pode ser ordena!!!’); end; Selecione o TEdit edt_consulta e vamos programar nossa consultas personalizadas, que sempre que o algo é alterado em um TEdit é acionado o evento Change, utilizaremos esse evento para aplicar nosso filtros. Conforme Listagem 6. Listagem 6: Filtros try if edt_consulta.Text <> ‘’ then //Verifica se o campo não etsa vazio begin cds.Filtered := False; //Desativa o filtro CDS.Filter := lbl_consulta.Caption+’ like ‘’%’+edt_consulta. Text+’%’’’;// Mosta o filtro // que sempre que encontrar algo na coluna do DbGrid que foi selecionado cds.Filtered := True; //Aplica o filtro lbl_total.Caption := IntToStr(cds.RecordCount)+’ Regsitro(s)’;//Conta quanot regisro tens filtrado end else begin cds.Filtered := False; //Desativa o filtro lbl_total.Caption := IntToStr(cds.RecordCount)+’ Regsitro(s)’;//Conta quanot regisro tens filtrado end; except ShowMessage(‘A consulta não pode ser realizada!!!’+#13+’Selecione uma coluna na grade.’); end; Agora vamos programar os nosso controles entre as abas do PageControl, esse controles serão implementados no evento StateChange do DataSource, DS. Sempre que o DS estiver em modo de Insert ou Edit vamos ativar a aba Dados e jogar o foco para o primeiro campo, caso contrário, voltamos o foco para a aba Contatos. Conforme a listagem 7. Listagem 7: StateChange if DS.DataSet.State in [dsEdit,dsInsert] Then begin PageControl1.ActivePage := TabSheet2; PageControl1. ActivePageIndex := 1; TabSheet1.Enabled := False; TabSheet2.Enabled := True; DBEdit2.SetFocus; end else begin PageControl1.ActivePage := TabSheet1; PageControl1. ActivePageIndex := 0; TabSheet1.Enabled := True; TabSheet2.Enabled := False; end; Feito todas essas implementações, basta compilar e começar a inserir, alterar e excluir os dados do nosso banco de dados. Vamos montar nossas etiquetas de Mala Direta dos nossos contatos, através do RaveReports, adicione um componente TRvProject altere o name para “RVREL”, adicione um TRvSystem e altere o name para RVSYS, relacione o componente RVREL na propriedade Engine com o “RVSYS”, e por último adicione um componente TRvDataSetConnection altere o name para “RVDSCDS”, na propriedade DataSet selecione o ClientDataSet “CDS”. Salve o projeto e de um duplo clique sobre o RVREL, o Rave abrira. Já com o RaveReports aberto, salve o projeto com o nome de ETIQUETAS.rav, agora acesse o menu File – New Data Object, selecione Direct Data View, Next, aparece o componente RVDSCDS, selecione e Finish. Agora já temos uma conexão do RaveReposts com a nossa aplicação, que se conecta com o banco de dados. Agora adicione um DataView1Region, configure do tamanho útil da página, adicione um Outubro 2009 09 DataView1DataBand relacione a propriedade DataView com a DataView1, altere o Heigth para 0,800 e ainda altere a propriedade Columms para 2, dessa maneira o relatório será gerado em duas colunas. Agora vamos adicionar nossos campos. Pressionando a tecla Ctrl selecione os campos NOME, ENDERECO, NUMERO, BAIRRO, CIDADE, CEP e UF, arraste para dentro da DataBand e os disponha conforme a figura 3. Pressione F9 e reja o resultado do relatório, não esqueça que é necessário já ter dados cadastrados para poder melhor visualizar, salve o projeto. Figura 3. Relatório Veja a Figura 3. Relatório De volta no Delphi, selecione o componente RVREL, e na propriedade ProjectFile selecione o arquivo ETIQUETAS.rav, e no botão de Imprimir Mala Direta vamos acionar o relatório, conforme Figura 4: Resultado Final listagem 8, conforme os dados filtrados na DBGrid, então dessa maneira quando quiser imprimir etiquetas para determinados registros, basta filtrar na DBgrid e imprimir. as etiquetas automaticamente. Veja resultado na figura 4. Veja a Figura 4: Resultado Final Listagem 8. Imprimir Conclusão RVREL.Open; RVREL.SelectReport(‘Etiqu etas’,False); RVREL.Execute; Assim chegamos ao fim, agora basta executarmos o sistema, cadastrar vários registros e imprimir O principal objetivo desse artigo e apresentar uma solução para organizar os cartões pessoais que guardamos, geralmente organizadamente, agora têm sempre a mão, basta acessar o sistema e consultar, quando desejar encontrar alguém que atue em determinada atividade, ou ramo. E ainda de forma automatizada pode se gerar etiquetas para enviar Mala Direta. Sobre o autor Mateus André Chies Técnico em Informática, bacharelando em Sistemas de Informação. [email protected] 10 Outubro 2009 Outubro 2009 11 Olá pessoal, Esse mês quero apresentar a vocês uma tecnologia realmente revolucionária da família de bancos de dados InterBase – o Sistema Journaling. Uma maneira definitiva de prevenir, recuperar e restaurar ambientes inteiros em caso de corrupção, perda e até mesmo desastres contra nossos bancos de dados. Que atire a primeira pedra quem nunca passou por algum problema de corrupção de bancos de dados InterBase/Firebird? Ou mesmo outro SGDB? Noites e noites mal dormidas, recuperando bases de dados com validações, backups, restores ou mesmo com procedimentos ultra-sofisticados, como pumps de tabelas, extração de scripts e inserções manuais. E não estou falando apenas de grandes empresas, grandes bancos de dados. Os pequenos consumidores de bancos de dados sofrem também com dias ou mesmo meses de dados perdidos por conta de uma corrupção. Então poderíamos pensar: O InterBase/Firebird não oferece segurança alguma! Precisamos de um gerenciador mais moderno, mais robusto! É certo e comprovado que a grande maioria 12 Outubro 2009 dos problemas de corrupção de bancos de dados está relacionada à infra-estrutura. Seja por conta de falhas de discos, memória, gerenciamento de Sistemas Operacionais, panes elétricas, enfim. E por mais que possamos investir uma pequena fortuna preparando ambientes extremamente seguros, estaremos sempre sujeitos a esse tipo de problema. Mas sim, cabe ao gerenciador de banco de dados oferecerem uma solução, uma opção mais segura aos seus usuários. E é nesse ponto que entra o Sistema Journaling. Uma alternativa para aumentar significativamente a seguranças de nossos ambientes e, em especial, nossos bancos de dados, com baixo investimento. Pois só quem perdeu sabe dizer quanto custa um dia de trabalho perdido. E apesar de ser revolucionário, o Sistema Journaling não é tão recente assim. Está disponível nos bancos de dados InterBase desde sua versão 2007, lançado há quase 3 anos. Vamos durante esse artigo, que também dividiremos em duas partes, mostrar a fundo como funciona e como aplicar o Sistema Journaling na prática. JOURNALING SYSTEM Pois bem, o Sistema Journaling é uma tecnologia embarcada no gerenciador de banco de dados InterBase que cria um ambiente para prevenção e recuperação possíveis falhas nos bancos de dados, causadas, como disse anteriormente, quase sempre devido à falhas de ambiente. Para o seu melhor funcionamento, o Sistema Journaling atua em duas partes. A recuperação de curto tempo, também conhecida como Short-Term-Recovery, e a recuperação de longo tempo – o Long-TermRecovery. Vamos então entender e estudar suas principais características e aplicações. SHORT-TERM-RECOVERY Vejam como a idéia é bem simples. Ao ativar o Sistema Journaling, o InterBase irá desativar (se assim estiver configurado) a opção Forced Writes do banco de dados, forçando assim a gravação assíncrona dos dados. Então o Sistema Journaling irá utilizar um espaço em disco, indicado por nós, para armazenar em tempo real todas as informações, todo o conteúdo de todas as transações aplicadas sobre o banco de dados, em arquivos chamados Journal Files. Sim, todas as transações e seus Inserts, Updates, Deletes, Creates, Drops, Alters e etc., antes de serem aplicadas no banco de dados, seus conteúdos serão gravados nos Journal Files. Independentemente se essas transações serão depois confirmadas (comitts) ou canceladas (rollbacks). E para que? Mais simples ainda! Em caso de qualquer parada inesperada de sistema, como um pique de energia, um restart abrupto do servidor, uma queda do serviço que seja, o InterBase e o Sistema de Journaling irá, automaticamente, aplicar no banco de dados, o conteúdo das transações que estavam “no ar” no momento da pane, recuperando as informações que estavam sendo gravadas, atualizadas ou excluídas, garantindo assim a integridade do banco de dados. E são essas pequenas paradas, pequenas falhas que, em pouco tempo, tornam nosso banco de dados tão corrompido que sua recuperação se torna um verdadeiro desafio. Abaixo trago dois links importantes que tratam sobre as principais causas de corrupção de bancos de dados: esse disco não seja o mesmo disco onde está localizado o banco de dados de produção. Podemos então separar um novo disco, ligado ao mesmo computador, mas que esteja conectado à outra controladora, ou, de preferência, um outro disco ligado a outro computador, conectado ao nosso servidor InterBase através de uma conexão de rede estável e segura. Essa escolha é fundamental para que o Sistema Journaling não comprometa o desempenho de nosso ambiente. Por ser algo novo, algo que nosso banco de dados não possui hoje, para ativar o sistema de Journal precisamos programar uma parada extremamente rápida do banco de dados que desejamos aplicar. É importante salientar que essa parada acontecerá apenas uma única vez. Então, uma vez que estamos com o banco exclusivo para nós, podemos utilizar comandos DDL ou mesmo o utilitário IBConsole para criação do Journal. http://edn.embarcadero.com/article/29515 h t t p : / / w w w. i b p h o e n i x . c o m / m a i n . nfs?a=ibphoenix&page=ibp_err_problem Mas se iremos gravar e criar arquivos Journal concorrendo com nosso banco de dados que está em produção, isso não irá trazer perda de performance? Não se criarmos de maneira correta. CRIANDO O SISTEMA JOURNAL Relembrando: o Sistema Journaling irá utilizar um espaço em disco indicado para criação de arquivos de Journal. Está aqui um ponto chave do processo. Definir esse espaço. Antes mesmo de começar a trabalhar, precisamos definir uma unidade, um disco onde serão criados os arquivos de Journal (Journal Files). E é imprescindível que Segue abaixo um exemplo de comando para essa criação: CREATE JOURNAL ‘d:\ databases\journal\employee\ emp’ LENGTH 65000 CHECKPOINT LENGTH 10000 CHECKPOINT INTERVAL 120 PAGE SIZE 8192 PAGE CACHE 2500 PREALLOCATE 325000 TIMESTAMP NAME Enabled; • O comando Create Journal é seguido de um caminho. Esse caminho indica o local onde serão gravados, gerados os arquivos Journal Files. No caso de nosso exemplo, esse caminho é o D:\ databases\Journal\Employee. Na sequência apara a palavra Emp. Essa palavra servirá como prefixo aos nomes dos arquivos que serão gerados nessa pasta. • O LENGTH (65000) define a geração de um novo arquivo de journal a cada 500MB (65000x8kb). O LENGTH default 500 gera um novo arquivo a cada 8MB. O tamanho de cada arquivo de journal vai depender do tamanho do banco de dados. É claro que se os arquivos de journal forem muito pequenos, mais arquivos serão gerados, o que pode gerar um problema de performance. Uma dica para definir o tamanho dos arquivos Journal é medir quanto cresce seu banco de dados por dia. Quanto maior for o crescimento/dia de seu banco, maior também deve ser o tamanho de cada Journal File. Pessoalmente não indico que seja inferior a 100 Mb. • O CHECKPOINT LENGTH em 10000 significa que o checkpoint do banco irá ocorrer a cada 80MB (10000x8k). O CHECKPOINT LENGTH default é 500, isso significa uma checagem a cada 4M (500x8k). Este parâmetro é muito particular, pois define o número de bytes que será aplicado no banco após um crash do servidor e influenciará no tempo da recuperação automática em caso de pane. Sua definição dependerá se o Journal está sendo criado no mesmo servidor ou em outro, se local ou remoto. • O CHECKPOINT INTERVAL é semelhante ao parâmetro anterior, porém determina o checkpoint em segundos e não em páginas. Você pode escolher uma das duas opções. Se as duas opções estiverem marcadas, o InterBase validará a que acontecer primeiro. Outubro 2009 13 • O PAGE SIZE determina o tamanho da página interna de gravação dos arquivos de Journal, semelhando ao pagesize do próprio banco de dados. O valor desse parâmetro deve ser sempre o dobro do tamanho do pagesize do banco de dados. • O PAGE CACHE determina o número de páginas de Journal que serão armazenadas em cache. Esse parâmetro também é muito importante, pois um cache pequeno pode ser tornar um problema de performance. • O PREALLOCATE é a opção que temos de pré-alocar um espaço no disco de Journal. Basta multiplicar quantos arquivos de Journal desejamos pré-criar e multiplicar pelo valor indicado na opção LENGTH. Nesse exemplo iremos pré-alocar 5 arquivos de 500 Mb. Esse parâmetro é especialmente particular se o disco reservado para Journal também for utilizado por algum outro processo, evitando que acabe o espaço em disco inesperadamente. Quando esses cinco arquivos forem totalmente preenchidos, então novos arquivos serão criados um a um. Imagem 1 – Caminho para o Menu de criação do Sistema Journal • E por fim o TIMESTAMP NAME Enable indica que os arquivos de Journal terão em seu nome uma gravação indicando o dia/mês/ano/ hora/minuto e segundo em que foram criados. Podemos também utilizar o utilitário IBConsole para realizar essa ativação do Sistema Journal: Veja a Imagem 1. Imagem 3 – Visualização do diretório onde foram gerados os Journal Files Imagem 2 – Tela para configuração dos parâmetros de ativação do Sistema Journaling Veja a Imagem 3. Logo após a criação do Journal, seu ambiente já poderá ser liberado para uso e, assim, instan14 Outubro 2009 taneamente, seus Journal Files começaram a ser utilizados. No exemplo acima podemos verificar que no diretório reservado foi criado: • Um arquivo chamado IB_JOURNAL, que será o responsável pelo gerenciamento o sistema Journal, • Um arquivo chamado EX.2009-1005T23-02-20Z.1.JOURNAL que é nosso primeiro Journal File já criado com o tamanho determinado no passo anterior. • 5 a r q u i v o s EX.PREALLOCATED.X.JOURNAL que são os cinco arquivos que já deixamos pré-alocados. Quando o Sistema Journal começar a utilizar esses arquivos, os mesmo serão automaticamente renomeados para o padrão do primeiro arquivo. Agora, e daqui para frente, todas as transações desse banco de dados irão alimentar nossos arquivos de Journal. Em caso de uma eventual falha o Sistema Journal irá executar o processo de Short-Term-Recovery Imagem 4 – Arquivo InterBase.Log contendo um trecho onde o Sistema Journaling foi utilizado. para aplicar as últimas transações que foram perdidas no processo. A imagem abaixo mostra trecho do arquivo de Log do InterBase (Interbase.Log) que exibe o momento em que o Sistema Journal realizou a recuperação de curto tempo: Veja a Imagem 4. São apenas três passos: 1 - Iniciando o processo. 2 - Aplicando o Journal File e 3 - Encerrando o processo. Simples, rápido e seguro. Dessa forma fechamos a implementação da primeira etapa do Sistema de Journal. Só com esse procedimento já aumentamos significativamente a segurança de nossos bancos de dados, independentemente se seu porte. A segunda etapa consiste na criação de um ambiente para o arquivamento dos arquivos de Journal e do banco de dados. Mas esse é um assunto para nossa segunda parte do artigo, no próximo mês. Não percam!!! CONCLUSÃO zando todos os tipo de recursos. E tudo isso sem precisar investir em sofisticadas plataformas de infra-estrutura, como Storages, diversos servidores, espelhamento de discos, etc. Basta um HD a mais e uns minutinhos para ativação. Se você já utiliza o InterBase versão 2007 ou superior, não perca mais tempo e comece já a utilizar esse fabuloso recurso. Se você é usuário InterBase versões anteriores ou Firebird, experimente, teste esse recurso baixando uma cópia de avaliação do produto. Lembrando sempre que, se você é desenvolvedor utilize a versão Developer Edition do InterBase SMP 2009, disponível gratuitamente no site da Embarcadero (http://www.embarcadero.com) . Essa versão permite o uso dos completos recursos de Journal. Vale lembrar também que o Sistema Journaling não está disponível nas versões Desktop e To-Go Edition do InterBase, por se tratar de versões voltadas para aplicações locais, sem acesso via rede, que dispensa esse tipo de segurança adicional. No próximo artigo voltaremos para mostrar com detalhes de como tornar seu Sistema Journaling muito mais poderoso com o sistema de Archiving, completando assim sua solução definitiva contra perda de dados. Está imperdível! Até lá. Referência: É fácil perceber o quanto o Sistema de InterBase 2009 Operations Guide – cap. 9. Journaling passa a ser fundamental para nossos ambientes. Se você é desenvolvedor, imagine o nível de segurança que você pode oferecer aos seus clientes, dos pequenos aos granFelipe Santos Felipe Santos é especialista em InterBase. Trabalha com o InterBase desde des. Se você é usuário de 2001. atuando como consultor e instrutor do produto em todo Brasil. Especialista em ambientes críticos. Atua e trabalha com os maiores clientes do InterBase no Brasil. InterBase/Firebird, pense Participante ativo na comunidade, com diversos artigos publicados. Participante do o quanto esse simples grupo de beta testers mundial do produto. Palestrante em eventos como IB Tour, Borcon Conference, CodeRage Latin América, Delphi Developers Day, Linux Day, entre outros. procedimento pode Atualmente trabalhando na área técnica do InterBase na Presence Tecnologia – agente oficial especializado do produto no Brasil. te ajudar a diminuir [email protected] e muito o seu tempo de parada, economi- Sobre o autor Outubro 2009 15 Explorando Records em Delphi 2009 Os programadores Delphi, mesmo aqueles sem muita experiência, já conhecem ou pelo menos ouviram falar de records ou estruturas nessa linguagem. Records são tipos de dados criados pelo programador, conceito similar ao de classes, porém records contém apenas atributos e classes contém além desses os famosos métodos que manipulam os atributos. Essa é a diferença mais superficial, digamos assim, além disso, existe outro fator importante a ser considerado quando trabalhamos com records e classes, que é a forma de alocação em memória pelo compilador de cada um. Existem dois tipos de alocação: Estática e dinâmica. A primeira é a alocação utilizada para variáveis, sejam elas globais ou locais e para os nossos records. No caso de alocação estática o programa utiliza uma área de memória conhecida com pilha, 16 Outubro 2009 onde as variáveis ficam até que a função onde foram declaradas acabe (no caso das variáveis locais) ou até que o programa seja encerrado (no caso de variáveis globais). O conceito de pilha é justamente o que nos vem à mente quando pensamos na palavra: Uma sequência de objetos dispostos um sobre o outro onde o primeiro a entrar é o primeiro a sair. Esse conceito fica ainda mais claro se imaginarmos a seguinte situação em um aplicativo: Temos a variável global nomeuser que é solicitada ao usuário quando da abertura do aplicativo. Temos a função soma que utiliza as variáveis locais num1 e num2 que realizam uma soma comum. É fácil visualizar o seguinte, assim que o aplicativo é iniciado a variável nomeuser é alocada na pilha, depois com a chamada da função as variáveis num1 e num2 são alocadas também. Assim que a função termina, o programa destrói as variáveis num1 e num2 que foram as últimas a entrar e, portanto primeiras a sair. A variável nomeuser que foi alocada primeiro será liberada por último, quando o aplicativo for encerrado. Alocação dinâmica é utilizada por arrays não dimensionados e classes. Nesse tipo de alocação, o programa cria um ponteiro para um espaço de memória contido em uma área chamada “heap”. Quando trabalhamos com alocação dinâmica precisamos criar o espaço e liberá-lo depois de utilizado. Um exemplo é a criação de formulários em Delphi. Veja o código: Try Form1:=tform1. create(self); Form1.showmodal; Finally Freeandnil(form1); End; Nessa sequência, criamos o objeto e consequentemente o espaço em memória para form1 e depois liberamos o espaço e o ponteiro que referenciava esse espaço com freeandnil. Essa técnica é utilizada em Delphi com a grande maioria das classes. Já foi possível perceber que além da diferença inicial, records e classes são alocados de forma diferente. Records de forma estática e classes de forma dinâmica. Por serem alocados de forma estática, records podem ser passados com parâmetros de funções e pode servir de valor de retorno, o que não se faz possível com classes. Além disso, a partir do Delphi 2006, é possível inserir métodos em records, o que pode ser muito útil em alguns casos. Nesse artigo estarei falando um sobre como podemos explorar melhor os records no Delphi 2009. De início criaremos dois records: um para ser passado para a função e outro para permitir que a função retorne os três valores desejados: Type DadosEntrada=Record Nomeusuário:string; Senha:string; End; Type DadosRetorno=Record Usuariologado:string; Codigousuario:integer; Nível:string; End; Records e Funções Em determinadas situações, precisamos passar vários valores para uma função ou fazer com que alguma função retorne um conjunto de valores. Uma alternativa nesses casos é utilizar arrays, porém podemos também fazer uso de records, o que impede que passemos ou retornemos arrays de tamanho incorreto, causando vazamentos de memória. Repare que a declaração de records é igual à de classes apenas com a diferença da palavra record que indica qual tipo de dado estamos criando. Iremos agora à função: Function Validausuario(dad os:DadosEntrada):DadosReto rno var sql:string; sdsvalida:tsqldataset; Begin Try Sql:=’select * from USERS where LOGIN= ’+quotedstr(dados. nomeusuario)+’ and SENHA = ’+quotedstr(dados.senha); Sdsvalida:=tsqldataset. create(nil); With sdsvalida do Begin Sqlconnection:=conexao; Commandtext:=sql; Open; If Criaremos a seguir um pequeno exemplo de função de validação de usuários que receberá alguns dados para realizar a validação e nos devolverá nome, código e nível de usuário. Não entrarei em detalhes com relação ao método de consulta ao banco que utilizarei para fazer a validação já que o foco do artigo são os records. Outubro 2009 17 recordcount>0 then Begin Result.Usuari ologado:=fieldbyname(‘NOME’ ).asstring; Result.Codigo Usuario:=fieldbyname(‘CODIG O’).asstring; result End; End; Finally Sdsvalida.free; End; End; Na função, fazemos um login simples e armazenamos as informações de saída no Record, o que nos possibilita ter dois valores de saída. Nesse caso são dois, mas poderiam ser mais bastando para isso a inserção de mais campos no Record. Records com Métodos A partir do Delphi 2006, Records em linguagem Object Pascal passaram a contar com mais uma funcionalidade passando a aceitar métodos além dos campos normais. Isso sem dúvida é uma grande funcionalidade que pode ser explorada pelos desenvolvedores Delphi. Como se comportam como variáveis estáti- 18 Outubro 2009 Figura 1 cas, ao usarmos records com métodos, podemos usufruir de uma maior agilidade já que o Record é liberado de memória assim que o escopo onde foi declarado termina. Vamos construir um exemplo simples agora para utilizarmos essa funcionalidade dos Records. Iremos declarar um Record que armazenará nome e alguns dados de uma pessoa. Nesse Record teremos o construtor e um método que fará a alteração da senha. RG:string; Cpf:string; Senha:string; Constructor Create(Pnome:string); Procedure alterasenha(Psenha:string); End; Implementação dos métodos: Type Pessoa=Record Nome:string; Constructor Create(Pnome:string); evento on click faça: Begin Nome:=Pnome; End; Procedure alterasenha(senha:string); Begin Senha:=Psenha; End; Iremos agora criar uma pequena aplicação exemplo para ilustrar o uso deste Record que criamos: Abra o Delphi 2009 e inicie uma nova aplicação, monte o formulário de acordo com a figura 1: Veja a Figura 1. No evento onclick do Button faça: rg:=edtrg.Text; cpf:=edtcpf.Text; senha:=edtsenha.Text; end; end; procedure TForm1. Button2Click(Sender: TObject); var novasenha:string; begin novasenha:=InputBox(‘Nova Senha’,’Digite a nova senha’,’’); RefPessoaGlobal. alterasenha(novasenha); ShowMessage(‘A nova senha é: ‘+RefPessoaGlobal. senha); end; Repare que não precisamos nos preocupar em liberar RefPessoa, que é a variável que faz referência a Pessoa porque como se trata de um Record, quando a função que instanciou o Record termina esse último é liberado de memória. Se quiséssemos manter os dados de RefPessoa poderíamos copiá-los para uma variável de escopo global que só seria liberada ao término da aplicação. Vamos demonstrar isso. Declare uma variável global RefPessoaGlobal do tipo Pessoa e no evento onclick que criamos a pouco, insira mais essa linha: Teste o aplicativo e salve as alterações. Conclusão procedure TForm1. Button1Click(Sender: TObject); var RefPessoa:Pessoa; begin RefPessoa.create(edtnome. Text); with RefPessoa do begin RefPessoaGlobal:=RefPessoa; Com isso essa nova variável irá manter os dados que passamos a RefPessoa já que esta última será liberada ao final do evento. Aproveitando essa nova variável, vamos fazer uso do método alterasenha do Record Pessoa. Insira mais um Button no formulário e em seu Através desse artigo, o leitor pode ter contato com novas funcionalidades em um sistema que podem ser proporcionadas pelo uso dos records. Se bem aproveitadas podem ser de grande utilidade para o dia a dia do desenvolvimento. Espero que tenham gostado e até a próxima! Sobre o autor Antonio Spitaleri Neto Consultor Técnico The Club. [email protected] Outubro 2009 19 Herança Visual de formulários em Windows Forms Uma das técnicas que mais chamou atenção desde o inicio da minha vida de programador é a herança visual de formulários. Chama atenção pelo fato de aproveitarmos o que a Programação Orientada a Objetos (POO) tem de melhor, usando herança, abstração, etc. Uma aplicação Windows Forms comercial possui dezenas ou até centenas de formulários, onde muitos possuem as mesmas características para um formulário de cadastro, onde teremos, por exemplo, botões para novo registro, salvar, excluir etc. Podemos então aproveitar essa técnica para criar um formulário base, com todas as funcionalidades comuns dos formulários de cadastros e assim, apenas herdar para os novos. Primeiramente, devemos analisar quais as funcionalidades comuns em um formulário de cadastro que devem ser implementadas no base. 20 Outubro 2009 Criando o formulário base Crie um novo projeto e no formulário dê o nome de “frmBase”. Todas as propriedades dos formulários de cadastro devem ser modificadas no frmBase. Altere as seguintes propriedades: KeyPreview = true, MaximizeBox = false, MinimizeBox = false, StartPosition = CenterScreen. Assim temos uma grande vantagem, pois não precisamos mudar as propriedades em cada formulário. Os formulários herdados do frmBase “recebem” as mesmas propriedades do formulário base. Nota: todas as propriedades do formulário podem ser modificadas nos formulários herdados. Diferentes dos componentes, que para terem suas propriedades modificadas, devem ser configurados para isso. Veremos mais adiante essa configuração. Agora precisamos adicionar os controles de tela que serão comuns em todos os formulários de cadastro. Poderíamos usar botões, mas nesse exemplo, usarei um ToolStrip para adicionar as funcionalidades de cadastro: Novo, Salvar, Excluir etc. No ToolStrip, adicione cinco botões e dê uma imagem a cada um, referente a sua funcionalidade (propriedade Image). Na propriedade DisplayStyle mude para ImageAndText, para mostrar a imagem e o texto. Dê também nomes sugestivos aos botões, como “btnNovo”, “btnSalvar”, etc. Adicione também um StatusStrip no formulário. Veja na Figura 1 como deve ficar o formulário. Figura 1. Formulário base da aplicação Esses são controles comuns de todos os formulários de cadastro, ou seja, os controles para o cadastro de clientes (TextBox, Labels etc), por exemplo, devem ser adicionados no formulário herdado para esse cadastro especifico. Vamos agora criar os métodos comuns que podem ser usados em qualquer formulário herdado. Criando código comum Um exemplo bem simples e para fácil entendimento sobre a vantagem de usar formulários herdados é o código para fechamento do formulário usando a tecla ESC. Sem a herança, o código deveria ser colocado em todos os formulários. Com a herança, o código será adicionado somente no formulário base. Para isso, acesse o evento KeyDown do formulário e adicione o seguinte código que será responsável por fechar o formulário quando o usuário apertar a tecla ESC: if (e.KeyCode == Keys. Escape) this.Close(); Agora, todos os formulários herdados, não precisam codificar o mesmo evento para adicionar essa funcionalidade. Convenhamos, muita produtividade e reaproveitamento de código, mesmo em um exemplo simples. Outro código bastante comum usado em cadastros são os métodos que habilitam/ desabilitam controles de tela e o que “limpa” os controles. O primeiro serve para indicar ao usuário, quando os controles Figura 1. Formulário base da aplicação de tela podem ficar disponíveis para o usuário e o segundo, para limpar, principalmente TextBox, depois de inserção ou atualização dos dados no banco. Adicione o seguinte código, que será o de habilitar/desabilitar controles: private void HabilitaDesabi litaControles(bool Value) { foreach (Control ctl in this.Controls) { if (ctl is ToolStrip) continue; ctl.Enabled = Value; } } O código é bastante simples, onde possui um parâmetro do tipo bool para indicar se deseja habilitar ou desabilitar os controles, usando a propriedade Enabled dos controles de tela (this. Controls), configurados através de um foreach. Fizemos uma verificação do ToolStrip para que os botões não sejam desabilitados, assim, se acharmos um controle do tipo, continuamos o laço, usando o continue. Já podemos usar o método no formulário base, pois após o usuário clicar no Novo, os controles de tela devem ser habilitados. No botão adicione o código, passando como parâmetro true. Com esse código, os botões de cadastros do ToolStrip também estarão desabilitados ou habilitados, então vamos criar um enum para indicarmos quando devemos habilitar ou desabilitar determinado botão, conforme o código a seguir: public enum StatusCadastro { scInsert, scBrowser, scEdit } Crie também uma variável do tipo do enum chamada sStatus. No HabilitaDesabilitaControles vamos modificar o método, adicionando o código da Listagem 1, após o foreach. Listagem 1. Alterando a configuração do botão, de acordo com o status do cadastro //habilitado quando não estivermos inserindo ou editando, somente navegando btnNovo.Enabled = (sStatus == StatusCadastro. scBrowser); //habilitado quando estiver inserindo ou editando Outubro 2009 21 btnSalvar.Enabled = ((sStatus == StatusCadastro.scInsert) || (sStatus == StatusCadastro.scEdit)); //habilitado quando estivermos editando btnExcluir.Enabled = (sStatus == StatusCadastro. scEdit); //habilitado quando não estivermos inserindo ou editando, somente navegando btnLocalizar.Enabled = (sStatus == StatusCadastro. scBrowser); //sempre habilita o sair btnSair.Enabled = true; No código, estamos verificando o valor de sStatus, para habilitar/desabilitar os botões, então para que isso funcione corretamente, devemos atribuir o valor correto para a variável, em determinadas situações. No botão Novo, antes do HabilitaDesabilitaControles, devemos configurar a variável para scInsert. Nos botões Salvar e Excluir, atribuía o valor scBrowser, seguido do HabilitaDesabilitaControles passando false. Já no Localizar, alteramos a variável para scEdit e devemos habilitar os controles de tela. Mais código comum Como indicado anteriormente, outro código interessante a ser usado no formulário base, é para “limpar” controles de tela. Para isso implemente o código da Listagem 2. Listagem 2. Método para limpar controles de tela private void LimpaControles() { foreach (Control ctl in this. Controls) 22 Outubro 2009 { if (ctl is TextBox) (ctl as TextBox).Text = “”; if (ctl is ComboBox) (ctl as ComboBox). SelectedIndex = -1; if (ctl is ListBox) (ctl as ListBox). SelectedIndex = -1; } } O código é bastante semelhante ao anterior, onde fizemos um laço pelos controles de tela. A diferença fica por conta, que verificamos o tipo de controle utilizando a diretiva is para saber o tipo da classe. Sabendo o tipo, usamos o as para fazer um cast e acessar as propriedades da classe. Assim, podemos utilizar, por exemplo, o Text do TextBox, o SelectedIndex do ListBox e ComboBox. Para outras classes, basta verificar seu tipo e fazer o cast na propriedade desejada. Bom, e onde vamos usar esse método? Devemos usar o mesmo quando o usuário quiser adicionar um novo registro, portanto chame o método no botão Novo. Nosso processo será o seguinte para um novo registro: usuário clica em Novo, limpamos e habilitamos os controles de tela, usuário adiciona os dados, salvamos o registro, desabilitamos os controles de tela. Assim, colocamos o usuário em um processo onde os erros, têm menos possibilidade de ocorrer (claro, devemos ter as validações do cadastro em cada formulário herdado). Precisamos também deixar os controles limpos e desabilitados quando o usuário abrir pela primeira vez o formulário. No evento Load do formulário, usaremos o seguinte código: sStatus = StatusCadastro. scBrowser; LimpaControles(); HabilitaDesabilitaControles (false); A idéia das funcionalidades do cadastro é que usaremos um cadastro parametrizado, ou seja, o usuário deve localizar um registro para poder editá-lo, assim quando abrir o formulário, as opções existentes são de inserir um registro (botão Novo) ou localizar para edição. Métodos virtuais Podemos declarar métodos no formulário base, que podem ser subscritos nos formulários herdados. A vantagem é que precisamos apenas subscrever, com o código necessário, não precisando chamar o mesmo em determinadas situações, claro se já o fizermos no formulário base. Para um melhor entendimento dessa parte, imagine o método para salvar os dados. Com o método virtual, declaramos o mesmo no formulário base, e a implementação que será de acordo com o cadastro, deve ser feita no herdado. A chamada do método será feita no formulário base, no botão de salvar os dados. Partindo para a prática, declare os métodos a seguir no formulário base: public virtual bool Salvar() { } public virtual bool Excluir() { } public virtual bool Localizar() { } O método pode ter implementação de código e vamos subscrever o mesmo no formulário herdado (ou classe, caso fosse essa a intenção). Faça a chamada aos métodos nos respectivos botões. Como temos um retorno do método (bool), o código que já implementamos nos botões devem ser executados, somente se o método retornou true. Adicionamos uma mensagem ao bloco de código para que o usuário saiba que o método foi executado com sucesso. Para o botão Salvar teríamos o código da Listagem 3. Listagem 3. Alterando o código dos botões if (Salvar()) { sStatus = StatusCadastro. scBrowser; HabilitaDesabilitaControles (false); MessageBox.Show(“Registro salvo com sucesso!”, “Salvar”, MessageBoxButtons.OK, MessageBoxIcon. Information); } Figura 2. Criando um formulário baseado em outro Podemos ainda implementar um código ou variável que retornasse o motivo do método ter retornado false, por exemplo, não salvando os registros. Lembrando, tudo isso pode e deve ser implementado no base, sem duplicar código em cada formulário. Execute de maneira semelhante, os outros métodos nos seus respectivos botões. Criando o formulário herdado Bom, podemos claro implementar mais código no formulário base, mas isso depende da necessidade do nosso projeto. Para criar um formulário herdado, primeiramente, dê um Build no seu projeto e em seguida, acesse o menu Project>Add Windows Forms. Figura 3. Escolhendo o formulário base Em Add New Item, na seção Windows Forms, escolha a opção Inherited Form, como podemos ver na Figura 2. Figura 2. Criando um formulário baseado em outro Após colocar o nome do formulário e clicar em Add, teremos um novo editor, onde vamos escolher o formulário que será herdado. Veja na Figura 3 a tela onde escolhemos o formulário. Figura 3. Escolhendo o formulário base Outubro 2009 23 Caso o formulário que você deseja herdar, não aparecer, experimente dar um Build na aplicação e tente novamente. Ao clicar em OK, teremos um formulário com os mesmos componentes do formulário base (Figura 4). Figura 4. Formulário herdado Nota: Os componentes adicionados no formulário base, não podem ser excluídos nos formulários herdados, portanto, como frisado anteriormente, é necessário fazer uma análise perfeita dos controles que devem ser mostrados no base. Você pode alterar propriedades como Enabled e Visible para modificar o controle no formulário herdado, mas para isso, deverá alterar a propriedade Modifiers para Public do componente no formulário base. Caso esteja utilizando o Visual C# (versão gratuita do Visual Studio para aplicações Windows Forms), a opção Inherited Form da Figura 2, pode não aparecer. Mas para contornar isso, é muito fácil. Abra o editor de código do formulário que deseja herdar (crio primeiramente) e na declaração do tipo, altere como o código a seguir: Figura 4. Formulário herdado Veja que não precisamos nos preocupar com funcionalidades para fechar formulário, limpar e desabilitar controles de tela, tudo esta codificado no formulário base, trazendo produtividade e fácil manutenção dos cadastros. Declare os métodos da Listagem 4 no formulário de clientes. Listagem 4. Subscrevendo os métodos da classe base (formulário) Antes public partial class Form1 : Form Depois public partial class frmClientes : frmBase Após, salve o formulário, feche o mesmo e dê um build na aplicação. Ao reabrir, o mesmo estará com os componentes do formulário base. Codificando o formulário herdado Após herdar o formulário, o que precisamos fazer? Primeiro, devemos adicionar os controles para mostrar os dados do cadastro que precisamos. Aqui, didaticamente, usaremos um cadastro de clientes. Então, adicione controles de tela, e precisamos codificar os métodos virtuais, criados no formulário base, salvando e excluindo os dados do cliente. 24 Outubro 2009 public override bool Salvar() { return base.Salvar(); } atualizará os dados do cliente, bem como o de excluir os dados, usando os métodos. Para efeitos de didática, imaginamos que temos o código para adicionar os dados, onde apenas vamos retornar true no método. Vamos configurar para que o formulário de clientes seja o formulário inicial da aplicação. Ao abrir o formulário, clicamos em Novo e adicionamos os valores nos TextBox. Após clicamos em Salvar e temos a mensagem de confirmação que só é executada se o método salvou corretamente os dados. Veja que com essa técnica, poupamos trabalho public override bool Excluir() { return base.Excluir(); } public override bool Localizar() { return base.Localizar(); } Veja que temos o retorno do método criado no formulário base, usando base.NomedoMétodo, isso indica que qualquer código que for implementado no método, será executado com essa chamada. O trabalho que teremos no formulário de clientes será escrever o código que adicionará ou para o leitor implementar e mostrar o conhecimento adquirido com o artigo. Conclusão Figura 5. Exemplo do formulário em execução e código, ou seja, temos em apenas um método o código necessário, por exemplo, para excluir um registro, que varia de acordo com o cadastro. Veja na Figura 5 o cadastro de clientes em execução. Veja a Figura 5. Exemplo do formulário em execução Para a funcionalidade de Localizar, também podemos criar um formulário de pesquisa base, e, por exemplo, passar um DataTable ou DataSet para fazer a pesquisa dos dados. Deixo essa idéia leitor, mas sempre tenha em mente que o tempo de análise é muito importante antes de começar a desenvolver seus projetos e no nosso exemplo, um formulário base. Um grande abraço a todos e sucesso em seus projetos! Vimos neste artigo, como criar formulários bases, para serem usados de maneira produtiva em aplicações Windows Forms. As dicas apresentadas neste artigo podem e devem ser melhoradas e adaptadas a necessidade do Confira o video deste artigo no site do The Club na edição online da revista. 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 Outubro 2009 25 Acesso a dados com jQuery Olá, chegamos ao final dos nossos 3 artigos sobre jQuery. Nesse artigo irei tratar sobre como acessar a funções (Handler) no Server site através de funções nativas do jQuery. Levando-se em consideração a prática do primeiro artigo vamos ao que interessa: Primeiro vamos abrir o Visual Studio 2008 e criaremos uma aplicação Web (imagem 01). Depois de criarmos a aplicação WEB adicionamos o jQuery a nossa aplicação e agora estamos prontos para começar a desenvolver nosso exemplo de utilização. Veja a imagem 1. Imagem 1 Depois da aplicação criada, tecle com o botão direito sobre o projeto > depois Add > New Item (Imagem 02), abrirá a tela Add New Item, nessa tela iremos selecionar a Opção Web do lado esquerdo e a Opção Generic Handler (imagem 03) neste ponto nossa aplicação deverá ter os seguintes arquivos (Imagem 04), esse Generic Handler é uma espécie de WebService que fará a “ligação” entre o client-side ( jQuery ) e a camada de negócio ou o banco de dados, através dele que acessaremos as funções C#. Veja a Imagem 2. Veja a imagem 3. 26 Imagem 2 Imagem 3 Outubro 2009 Agora vamos começar a codificar: Primeiro vamos incluir o jQuery na nossa página Default. Aspx através da Listagem 01, nessa listagem referenciamos o jQuery, logo após esse código referenciamos a nosso objeto de pesquisa conforme listagem 02, na listagem 02 temos a lista de categorias que podemos ter em nosso exemplo, que seria semelhante a uma loja virtual, observem que estamos criando links e nesses links apontamos para a própria página através do # e no evento onclick atribuímos a chamada de um método JavaScript que iremos implementar adiante, passamos para esse método o código da categoria que estamos procurando, exemplo a categoria Culinária é o código 1 Informática código 3 e assim sucessivamente. Agora vamos implementar nosso método para acessar o servidor e retornar os produtos da categoria sem a necessidade de fazer um postback de toda a página (listagem 03), observem que na listagem 03 eu criei primeiro um Bloco de text/JavaScript que deve ser criado dentro das tag <head> do nosso HTML, depois eu criei uma variável contendo o nome do nosso handler para facilitar a manutenção futuramente e depois implementei o nosso GetComunicacaoDados(e), onde é o código da categoria que estou pesquisando. Depois crio outra variável nomeada como detailArea contendo o nome do elemento HTML que vai receber os produtos de determinada categoria, depois implementamos o método $.get do jQuery que tem como seus parâmetros default a URL, DATA, CALLBACK onde que URL indica a página que temos nosso Handler o DATA indica o valor que queremos passar em nosso parâmetro, uma espécie de QueryString e o Callback é o que o Client-side vai processar quando o servidor responder a requisição, no campo URL passamos a nossa variável criada anteriormente com o caminho do nosso Handler depois no parâmetro DATA é no formato JSON indiquei um atributo nomeado como codCategoria e passei para ele o código da categoria que tínhamos colocado no onclick lá no link de categorias, e por último o CALLBACK que é o que será processado pelo client-side quando o servidor retornar da requisição, neste caso ele simplesmente vai colocar o retorno para dentro do nosso divProdutos onde estarão os produtos da nossa loja. Para criar o Div de produtos utiliza a listagem 04. Ok agora o nosso Client-Side está pronto, a nossa página Default.aspx está ok. Agora vamos abrir o arquivo Handler1.ashx e implemente no método ProcessRequest a listagem 05, nessa listagem primeiro criamos uma variável inteira que receberá o valor do JSON codCategoria que contém dentro dele a categoria que está sendo pesquisada, depois eu “escrevo” no context (página) o retorno do meu método RetornarProdutos que é um método que pode estar em qualquer NameSpace da minha aplicação, assim conseguimos interagir entre o HTML e o C#, mas retornando ao nosso exemplo o Método RetornarProdutos está dentro da mesma classe do Handler esse método está descrito na Listagem 06, fiz apenas um exemplo simples para retornar valores pois não é de intenção desse artigo entrar em questão o acesso a banco de dados. Agora vamos acessar a nossa aplicação (F5): Quando abrimos a nossa página vai estar no topo as categorias que foram listadas (Elétrodoméstico, Culinária, Etc.) e em baixo um FieldSet com o título Produtos da Categoria, quando clicamos em alguma das categorias o JavaScript do onClick daquele link é executado fazendo uma requisição ao servidor para que o mesmo retorne com os produtos referenciados para determinada categoria. Então é isso, espero que tenham gostado dessa série de artigos sobre jQuery e em breve estarei com novos artigos sobre novas tecnologias que estão sendo lançadas no VS2010 e Silverlight 3. Listagem 01: <asp:ScriptManager id=”sm1” runat=”server”> <Scripts> <asp:ScriptReference Path=”~/Scripts/jquery1.3.2.js” /> </Scripts> </asp:ScriptManager> <li><a href=”#” onclick=”GetCo municacaoDados(1);return false;”>Culinária</a></li> <li><a href=”#” onclick=”GetCo municacaoDados(2);return false;”>Informática</a></li> <li><a href=”#” onclick=”GetCo municacaoDados(3);return false;”>Jardinagem</a></li> </ul> Listagem 03: <script type=”text/ javascript”> // The handler url var handlerUrl = ‘Handler1.ashx’; function GetComunicacaoDados(e) { var detailArea = ‘#divCategorias’; $.get(handlerUrl , { codCategoria: e.toString() } , function(data) { $(“#divprodutos”). html(data); }); } </script> Listagem 02: Listagem 04: <ul id=”categories”> <li><a href=”#” onclick=”GetCo municacaoDados(0);return false;”>Eletrodomésticos</ a></li> <br /> <fieldset> <legend> Produtos da Categoria </ legend> Outubro 2009 27 <div id=”divprodutos”></div> </fieldset> Listagem 05 public void ProcessRequest(HttpContext context) { int codCategoria = Int32. Parse(context.Request. QueryString[“codCategoria”]); context. Response.Write(RetornaProdut os(codCategoria)); } Listagem 06 private string RetornaProdutos(int 28 Outubro 2009 CodCategoria) { // Aqui posso criar um acesso a banco de dados e // filtrar os produtos da cateogira indicada no parâmetro do método StringBuilder sb = new StringBuilder(); sb.Append(“produto 01 da cateogia “ + CodCategoria. ToString() + “<br/>”); sb.Append(“produto 02 da cateogia “ + CodCategoria. ToString() + “<br/>”); sb.Append(“produto 03 da cateogia “ + CodCategoria. ToString() + “<br/>”); return sb.ToString(); } Conclusão: O objetivo dessa seqüência de artigos sobre jQuery era de passar um pouco de conhecimento aos desenvolvedores .net e web uma pequena parte dessa fabulosa Framework que veio para firmar ainda mais a chamada WEB 2.0 com muitas mais interatividade, praticidade e performance. Sobre o autor Djonatas Tenfen Trabalha a quase 7 anos com Delphi, trabalha na empresa Benner Sistemas (www.benner.com.br ) na área de tecnologia desenvolvendo ferramentas em Delphi e como hobby e visão de mercado está migrando seus conhecimentos para a plataforma .NET. Faz parte do grupo .NET Blumenau http://dotnetblumenau.ning.com/ . Possue certificação 70-536 (Microsoft .NET Framework 2.0 Application Development Foundation ) . Twitter: djonatastenfen - blog http://www.djonatastenfen.blogspot.com/ [email protected] Dicas DELPHI Alterar propriedade buttonstyle das toolbars ligadas ao actionmanager em tempo de execução: Mostrar jpg num grid uses jpeg TButtonProperties(ActionMana ger1.ActionBars[0].Items[0]. CommandProperties).ButtonSize := bsLarge; Esse typecast também serve para alterar outros subitens de command properties como buttontype, groupposition e textassociation Criando uma caixa de mensagem personalizada function mensagem(Texto: string; Botao: TMsgDlgButtons = [mbOk]; Rotulo:String = ‘Atenção’; Icon : TMsgDlgType = mtWarning): integer; var wl_mensagem: TForm; begin no evento do onDrawColumnCell do grid. var jpeg:tjpegimage; begin if column.Field is tgraphicfield then begin with dbgrid1.Canvas do begin fillrect(rect); jpeg:=tjpegimage.create; try jpeg.Assign(column.Field); draw(rect.Left,rect.top,jpeg); finally jpeg.Free; end; end; end; end; wl_Mensagem := CreateMessageDialog(Texto, Icon, Botao); (wl_mensagem.FindComponent(‘YES’) as TButton).Caption := ‘&Sim’; (wl_mensagem.FindComponent(‘NO’) as TButton).Caption := ‘&Não’; (wl_mensagem.FindComponent(‘OK’) as TButton).Caption := ‘&Ok’; (wl_mensagem. FindComponent(‘CANCEL’) as TButton).Caption := ‘&Cancela’; wl_mensagem.Color := clBtnFace; wl_mensagem.BorderStyle := bsSingle; wl_mensagem.BorderIcons := []; wl_mensagem.Caption := Rotulo; result := wl_mensagem.ShowModal; end; Wav em sua aplicação Na Unit do form vá até USES e acrescente lá: MMSystem, (coloque a vírgula). Depois vá no evento OnShow do form e coloque o comando: Begin PlaySound(‘C:caminho_da_ musicanome_da_musica.wav’, 1, SND_ ASYNC); End; End. Outubro 2009 29 30 Outubro 2009 Outubro 2009 Outubro 2009