segunda-feira, 12 de agosto de 2013

ClientDataSet - Clicando no Título das Colunas do DBGrid para Ordenar


Veja nesta dica como é simples realizar a tarefa de ordenação dos dados de um DBGrid que está ligado a um ClientDataSet, criando e selecionando índices em memória.
O código abaixo mostra como realizar a ordenação clicando no titulo da coluna desejada. Clicando uma uma vez será utilizada a ordem crescente e na segunda, a ordem decrescente.

Primeiramente, no evento onCreate do Form, criaremos um índice Crescente e um Decrescente para cada campo do DBGrid, com o seguinte código:

procedure TForm1.FormCreate(Sender: TObject);
var
  i : integer;
begin
  for i := 0 to clientdataset1.FieldCount - 1 do
  begin
    CLIENTDATASET1.IndexDefs.Add('a' + CLIENTDATASET1.Fields[i].FieldName, 
      CLIENTDATASET1.Fields[i].FieldName, []);
    CLIENTDATASET1.IndexDefs.Add('d' + CLIENTDATASET1.Fields[i].FieldName,
      CLIENTDATASET1.Fields[i].FieldName, [ixDescending]);
  end;
end;
Em seguida, programaremos o evento onTitleClick do DBGrid:

procedure TForm1.DBGrid1TitleClick(Column: TColumn);
begin
  if 'a' + Column.FieldName = CLIENTDATASET1.IndexName then
    CLIENTDATASET1.IndexName := 'd' + column.FieldName
  else
    CLIENTDATASET1.IndexName := 'a' + column.FieldName;
end;
Pronto! Seu DBGrid em ordem Crescente e Decrescente de qualquer coluna, com apenas um click!

segunda-feira, 22 de julho de 2013

Enviando e-mails com Delphi 5

Este resumo não está disponível. Clique aqui para ver a postagem.

Repositório do Delphi


O repositório do Delphi é um local para armazenamento de objetos, como formulários e projetos, que facilita o compartilhamento desses objetos por vários projetos. Quando você clica em File|New... para criar um novo objeto, você pode escolher um dos itens do repositório.

Acrescentando um formulário ao repositório 

     Abra um projeto DPR, e dentro dele o formulário "Form" (Form1.pas). Esse formulário está sendo compartilhado dentro do mesmo projeto, mas se você quiser reutilizá-lo em outros projetos, pode acrescentá-lo ao repositório. 
     Para isso, clique com o botão direito e em Add to Repository. Você deve informar o título do item, uma descrição e qual a página onde ele será inserido (se você digitar um nome de página que não existe, uma nova será criada), além do seu nome para indicar qual o autor desse item. Opcionalmente você pode escolher um ícone que será usado para representar o item. Para o exemplo digite o seguinte: 

   Title: Formulário para uma tabela
   Description: Formulário de banco de dados para uma tabela
   Page: Forms (default)
   Author: seu nome 

     O botão "Browse" permite você procurar um ícone , para representar sua classe de formulário, caso não informe ele irá mostrar o seguinte ícone . Clique Ok. O novo item será adicionado ao repositório. Agora crie um novo projeto com File|New Application e veremos como o item pode ser reutilizado. 

     Formas de criar novos objetos 
     Se você clicar em File|New... e na página "Forms", você verá que o novo item ("Formulário para uma tabela") está dentro do repositório. Agora ele está disponível em qualquer projeto, permitindo criar novos formulários a partir dele. 

     Existem três opções para criar novos objetos a partir do repositório: copiar [copy] o item, herdar [inherit] do item (criar uma nova classe derivada) ou usar [use] o item diretamente. 

Se você marcar a opção "Copy" e clicar Ok, o Delphi cria uma cópia separada do objeto original que está no repositório. Qualquer alteração no original posteriormente não vai afetar essa cópia e qualquer alteração na cópia é independente do que está no repositório. Note que a unidade do formulário não foi salva e está em memória como "Unit2". Isso permite você salvar com um nome qualquer. 

     A opção "Inherit" (herdar) faz diferente: faz uma referência ao original (ou seja, acrescenta o formulário original dentro do projeto) e cria uma nova classe derivada da original, TForm1. O novo formulário será chamado de 'FormBase1', com a classe 'TFormBase1'. A unidade do formulário não foi salva ainda. Nesse caso, qualquer alteração no original, que está no repositório, será herdada pela classe derivada. 

     A opção "Use" não copia o objeto original, mas compartilha com o projeto atual. Nesse caso, alterações feitas no item dentro do projeto afetam o item no repositório e vice-versa. Se vários projetos usarem o mesmo item, todos eles compartilham o mesmo item. 

     Adicionando um projeto ao repositório 
     Para reutilizar um projeto inteiro, você pode acrescentá-lo ao repositório com o menu Project|Add to Repository... e informar a descrição do item como antes. 

     Gerenciando o repositório 
     Para gerenciar as páginas do repositório e os itens de cada página, você pode clicar em   Tools|Repository. Na caixa de diálogo você pode criar, renomear ou excluir uma página do repositório (você só pode excluir se ela estiver vazia). É possível também mudar a ordem das páginas. 

     Você pode mover os itens entre páginas arrastando o item da lista da esquerda e soltando sobre a página desejada. Também é possível renomear ou alterar características de um item ou excluir o item. 

     Compartilhando o repositório numa equipe 
     Quando uma equipe de desenvolvedores trabalha em conjunto, é importante que eles possam compartilhar o repositório, de forma que novos itens adicionados a ele estejam disponíveis para toda a equipe. 

     Para compartilhar o repositório, você deve usar um diretório na rede que seja acessível a todos os desenvolvedores. Por exemplo, se G: é uma letra de drive que aponta para a rede, você pode usar G:\REPOS. Você deve também copiar os arquivos do repositório do Delphi para esse diretório. 

     Os arquivos do repositório do Delphi são armazenados num subdiretório OBJREPOS, abaixo do diretório onde o Delphi foi instalado (geralmente C:\Arquivos de Programas\Borland\Delphi6). Além desses arquivos, o Delphi usa um arquivo de texto chamado DELPHI32.DRO, localizado no subdiretório BIN do Delphi. 

     Para compartilhar o repositório na rede, faça o seguinte: 
     • Crie um diretório compartilhado (e.g. G:\REPOS). Verifique que todos os desenvolvedores têm acesso a ele e usam a mesma letra de drive; 
     • Copie o repositório de um computador para o diretório compartilhado (G:\REPOS), ou seja, todos os arquivos do subdiretório OBJREPOS do Delphi, mais DELPHI32.DRO, do subdiretório BIN; 
     • Em cada um dos computadores, no Delphi, clique em Tools|Environment Options. Na página Preferences Em "Shared Repository", digite o caminho do diretório compartilhado. 

Agora lembre-se que qualquer alteração ou acréscimo feito por um desenvolvedor afeta todos os outros. É importante também notar que quando você quiser compartilhar um objeto, ele deve ser salvo num diretório compartilhado também, acessível a todos (por exemplo, G:\BIBLIOTECAS). 

Autor    : Celso Rodrigues

segunda-feira, 17 de junho de 2013

Mainmenu e popupmenu personalizados utilizando canvas (muito simples)


Olá Pessoal!!!

Bom estou aqui com mais uma dica que interessa a muitas pessoas! Personalizar 
uma aplicação sem utilizar componentes externos.
Estaremos Pesonalizando os Menus de Nossa aplicação.
  Mas antes quero esclarecer algumas coisas para quem esta procurando por isso:
  -A dica é para personalizar os Menus da aplicação, tanto MainMenu quanto popupMenu.
  -Para quem utiliza Delphi 7 (Exemplo) pode utilizar os Componentes ActionMainMenuBar e 
  ActionManager para fazer alguns MainMenus bacanas(em breve mostrarei passo-a-passo como 
  utilizar estes componentes que são muito legais de trabalhar).
  -Neste exemplo estarei trabalhando com Canvas!
  
 Então... Mãos à Obra!!!!

   Neste exemplo que faremos utilizarei o Componentes MainMenu mas o que vai ser aplicado nele
 pode ser sem problemas aplicado para um popupmenu.

 Vamos deixar os nossos menus com uma aparência bem legal!.

 Eventos Utilizados no componente: onDrawItem, onMeasureItem.

 1º- Crie uma nova aplicação.
 2º- Coloqueno formulario o componente MainMenu (Standard Palette).
 3º- Ative a propriedade OwnerDraw do componente MainMenu.
 3º- Abra o "Editor de Menus" dando 2 clique no componente e crie 1 menu.
        (insira Caption, Name...)

 4º- Acesse o Evento onDrawItem de deste item do Menu e vamos implementar o seguinte código:
}
procedure TForm1.Arquivo1DrawItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
begin
  //Pinta a opção
  
  if Selected then  //verifica se o item está selecionado
    BEGIN
      ACanvas.Brush.Color :=  $00EFD3C6;        //cor de fundo do item (Lembrando que podemos printar um Imagem).
          //   Canv.Brush.Color :=  clAppWorkSpace;
      ACanvas.Rectangle(Arect);                         //Desenharemos uma retangulo em todo o Item selecionado.
      ACanvas.pen.Color:=$00C66931;             //seta a cor da borda do retangulo.
      InflateRect (ARect, -1,-1);
    end
    else
    ACanvas.Brush.Color:=clMenu;                        //se não estiver selecionado o item coloca a cor normal do item.
        //-->  dietrich 01/02/2007
        
        //coloca o texto
    ACanvas.pen.Color:=clBlack;  //cor
    ACanvas.TextRect(ARect,ARect.Left+5, ARect.Top+1,'Sair');//local onde sera escrito o texto ARect

end;

quinta-feira, 18 de abril de 2013

[Básico] - Tabela temporária com ClientDataSet – Conceito


Uma das vantagens de um fórum de programação é observar as dúvidas mais frequentes dos usuários e tentar ajudá-los de uma forma mais prática. Dessa vez, notei que muitos desenvolvedores têm dificuldades em compreender, criar e manipular tabelas temporárias no Delphi utilizando ClientDataSet. Além de ser um recurso muito útil, trabalhar com tabelas temporárias não exige conhecimentos avançados de programação.
Preparado pra mais um pequeno tutorial sobre desenvolvimento?

Tabela temporária? O que é isso?
Também conhecida como “tabela virtual”, uma tabela temporária é capaz de armazenar registros em memória sem a necessidade de estar conectada a um banco dados, diferentes das tabelas físicas, que armazenam os dados em disco. Os registros da tabela temporária são apagados automaticamente quando a aplicação é encerrada ou quando se utiliza um comando próprio pra isso, no qual veremos no próximo artigo. Um grande recurso da tabela temporária é permitir manipular estes dados em memória, como inclusões, alterações, exclusões e filtros, tal como podemos fazer com tabelas físicas no banco de dados.

Mas qual a vantagem de utilizar uma tabela temporária no meu projeto?
Bom, depende do tipo de projeto que você está desenvolvendo. Tabelas temporárias, em linhas gerais, são úteis para trabalhar com dados locais na aplicação de forma dinâmica. Por exemplo, em um ambiente master/detail, uma tabela temporária pode armazenar os registros filhos antes mesmo do registro mestre ser gravado no banco de dados.

Ainda não entendi. Poderia dar um exemplo?
Claro que sim.
Imagine que estamos desenvolvendo um sistema para controle de vendas.
No nosso banco de dados, teremos as seguintes tabelas relacionadas à venda:

-- Tabela VENDAS
COD_VENDA
DATA
COD_CLIENTE
TOTAL

-- Tabela ITENS
COD_VENDA (chave estrangeira referenciando a tabela VENDAS)
COD_PRODUTO
QTDE
VALOR
TOTAL

Agora, suponha que iremos gravar uma nova venda no sistema com os seguintes dados:

COD_VENDA = 1
DATA = 15/02/2013
COD_CLIENTE = 20
TOTAL = ?

Ué, cadê o total?
Você deve concordar que, para informar o valor total, é necessário adicionar os itens, ou seja, precisamos somá-los para calcular o valor total da venda, não é? Por outro lado, para adicionar os itens precisamos do código da venda, já que as duas tabelas são relacionadas por uma chave estrangeira:

(ITENS.COD_VENDA = VENDAS.COD_VENDA).

Agora raciocine comigo: se tentarmos adicionar um item sem o código da venda ou com o código de uma venda que (ainda) não existe, receberemos o erro de “violação de chave estrangeira”, visto que, na verdade, tentaríamos adicionar um registro filho sem a existência do registro pai.
Pois bem, e para obtermos o código da venda, precisamos primeiro gravá-la no banco de dados, certo? Mas espere aí… conforme vimos acima, não temos o valor total pra gravar a venda!
E então entramos em um impasse:
- Não podemos gravar primeiro a venda, pois não temos os itens
- Não podemos gravar primeiro os itens, pois não temos a venda

E agora? Já sei! Vamos gravar a venda sem o valor total!
Muitos desenvolvedores utilizam essa “técnica”, se é que podemos chamá-la assim. Aplicando à nossa realidade, gravamos a venda sem o valor total, apenas para obter o código da venda. Dessa forma, podemos gravar os itens da venda normalmente, e após gravá-los, calculamos o valor total e alteramos o registro da venda, atualizando o total. Em um contexto procedimental, ficaria dessa forma:
Gravar a venda sem o valor total
Obter o código da venda gravada
Gravar os itens utilizando o código da venda obtido
Somar os itens para calcular o total
Editar a venda e atualizar o valor total
Observe que para cada venda será necessário fazer duas operações na tabela de vendas: uma de inserção e outra para alteração. Eu, particularmente, não sou de acordo com esse procedimento.
Em um exemplo, imagine que gravamos a venda, mas ao começar a adicionar os itens, a energia é cortada ou o usuário decide fechar a tela por algum motivo. Obviamente o registro ficará incompleto, já que teremos uma venda sem itens gravada no banco de dados. Tecnicamente, será um registro mestre sem registros filhos.
No caso da energia acabar, o problema pode ainda se agravar um pouco mais. Ao reiniciar o sistema, o usuário irá gerar uma nova venda, visto que, aos olhos do usuário, a outra venda não foi gravada. No fim da história haverá 2 vendas idênticas no sistema: uma sem itens (perdida) e outra válida.

Mas neste caso é só excluir essa venda incompleta, não é?
Sim, pode ser. Mas para isso, você terá que implementar um controle eficiente no seu sistema para identificar esses registros problemáticos. E também, lembre-se que cada vez que isso ocorrer, o código da venda será perdido (“pulado”) no banco de dados.
Aí prepare-se para receber algumas ligações do usuário perguntando:
“Por quê o sistema pulou o número da venda?”
“A venda nº X sumiu do sistema…”
“Tem uma venda aqui sem itens!”

Certo, e qual a sugestão para resolver isso?
Opa, chegou aonde eu queria!
Amigos, todos os problemas citados acima podem ser resolvidos utilizando tabelas temporárias! Durante a inclusão da venda, ao invés de gravarmos os itens no banco de dados, iremos gravá-los em uma tabela temporária. Se a energia acabar, nada será afetado, já que nenhuma alteração foi feita no banco de dados.
Este será o nosso procedimento:
Inserir os itens na tabela temporária;
Ao gravar a venda, os itens da tabela temporária serão “copiados” para a tabela física, tudo em um mesmo método.
Desta maneira, iremos reduzir bastante a possibilidade de uma venda ficar incompleta, visto que a gravação da venda e os itens da venda acontecerão praticamente ao mesmo tempo. E o melhor: durante a inclusão da venda não faremos nenhuma operação no banco de dados, somente na gravação.


Firemonkey: substituição da tecla TAB por tecla ENTER


Antes de tudo, gostaria de deixar claro que esta é uma dica básica; porém alguns encontram dificuldades nesse assunto, então estou aqui com o intuito de esclarecimento. Ultimamente tenho visto algumas dicas na internet de substituição da tecla tab pela tecla enter, porém muitas dicas não funcionam perfeitamente, pulando muitas vezes dos “tedit” para o próximo componente, mas ainda há a possibilidade de o caso ser um botão e ter um comando no evento “onclick”, o comando no “onclick” não ser executado. Pois bem, tenho uma dica aqui que vai funcionar perfeitamente, em ambos aspectos: tanto no salto nos controls, como sem perder os eventos onclick dos botões.

No Evento OnKeyDown do Form digite os comandos abaixo:

begin
if Key = vkReturn then
begin
Key := vkTab;
KeyDown(Key, KeyChar, Shift);
end;
end;

Dica básica mas muito útil, espero que tenham gostado!




quarta-feira, 2 de janeiro de 2013

Trabalhando com POO na prática


Depois de abrirmos o tema para que todos pudessem opinar, iremos começar a criar nossa série de artigos, segue abaixo a descrição de nosso sistema.

Peço a todos que pretendem colaborar de alguma forma que enviem um e-mail para mim para que possamos facilitar o contato e agilizarmos o processo de publicação dos artigos. Segue meu e-mail, rboaro@gmail.com.
 
Fica difícil mensurarmos quantos artigos iremos desenvolver sobre o tema, pois desenvolver uma aplicação por menor que seja requer bastante tempo ,muitas coisas precisam ser definidas. Sendo assim iniciamos descrevendo as funcionalidades básicas do sistema, por favor comentem (por e-mail) e sugiram alterações se acharem necessário.
 
Objetivo do Sistema: Processar vendas de um determinado estabelecimento, quando falo processar vendas, estou me referindo a controlar o estoque dos produtos, e ter as funcionalidades básicas de controle, como por exemplo, gerar títulos a receber , efetuar lançamentos no caixa, quando a venda for a vista etc.
 
Cadastros que serão criados:
> Cidades
> Clientes
> Produtos
> Pedidos
> Titulos a Receber
Seria interessante criarmos tambem outros cadastros, como por exemplo cadastro de Titulos a Pagar, mas nesse caso precisaríamos criar um cadastro de lançamento de notas fiscais de entrada o que pode ser feito baseado nos exemplos que iremos desenvolver.
 
Banco de dados utilizado: depois de muitas opiniões e como é de costume divergentes, pois cada um de nós simpatiza com um determinado banco de dados, como costumo dizer, "o melhor banco de dados é aquele que dominamos e sabemos utilizar o máximo das suas funcionalidades". Dessa forma pensei em criarmos classes de acesso ao Firebird e ao Sql Server Express (free).
 
Driver de acesso ao banco: esse não abro mão de usar dbExpress que é o que há de melhor nas ultimas versões do Delphi.
Versão do Delphi utilizada: Delphi XE2, mas me coloco a disposição para auxiliá-los a adaptar o fonte para outras versões.
 
Frequência dos artigos publicados: no mínimo um por semana, se possível mais, mas como todos temos nossos compromissos a cumprir, deixamos aqui acordado que não podemos passar uma semana sem publicar nada.
 
Acredito que os principais itens foram citados. Sendo assim mãos a obra.

Opção online para mpressão e Re-Impressão de DANFE

 
Alguma vez você já precisou imprimir ou re-imprimir um Documento Auxiliar de Nota Fiscal Eletrônica - DANFE?!

É claro que você pode entrar no seu sistema ERP e solicitar uma re-impressão... mas e se você não estiver na sua empresa?! e se o DANFE foi emitido por um fornecedor?!

Dois projetos interessantes e gratuitos estão disponíveis na internet para que possamos imprimir e re-imprimir DANFEs a partir do arquivo XML da NFe ou da chave de acesso da nota, são eles:

www.webdanfe.com.br/danfe/index.html
www.imprimirdanfe.com.br

Tipos Genéricos no Delphi


Hoje irei falar um pouco sobre tipos genéricos no delphi.
Um tipo genérico no Delphi pode ser definido por qualquer tipo padrão (string, integer, boolean) ou um tipo criado especificamente para sua aplicação.
Como isto é feito???

Defino uma classe Tvalor onde T é o tipo que a classe irá implementar.
Exemplo de Classe genérica:
TValor = class
FValor: T;
end;

Exemplo de utilização da classe:
Procedure teste();
Var
oTexto: Tvalor;
begin
oTexto := TValor.Create;
try
oTexto.Valor := ‘isto é um teste’;
finally
oTexto.Destroy;
oTexto := Nil;
end;
end;

Vamos pensar agora que nem sempre iremos ler o valor diretamente, como exemplo uma lista de objetos (não iremos implementar aqui, mas apenas como ajuda para interpretação OK?), como saberemos o tipo a ser tratado?

Simples, vamos mudar a implementação da classe!
Exemplo:

unit Model.ValorUnit;

interface

uses
System.TypInfo;

type
TValor = class
private
FValor: T;
FTipo: TTypeKind;
function GetValor: T;
procedure SetValor(const Value: T);
function GetTipo: TTypeKind;
public
procedure AfterConstruction; override;
property Valor: T read GetValor write SetValor;
property Tipo: TTypeKind read GetTipo;
end;

implementation

{ TValor }

procedure TValor.AfterConstruction;
var
Info: PTypeInfo;
begin
Info := System.TypeInfo(T);
try
if Info <> nil then
FTipo := Info^.Kind;
finally
Info := nil;
end;
end;

function TValor.GetTipo: TTypeKind;
begin
inherited;
result := FTipo;
end;

function TValor.GetValor: T;
begin
result := FValor;
end;

procedure TValor.SetValor(const Value: T);
begin
FValor := Value;
end;

end.

Nesta segunda implementação (ou alteração) a classe Tvalor agora possui dois campos, Fvalor e Ftipo.
Fvalor irá receber o valor própriamente dito, seja ele string integer ou qualquer outro, enquanto que Ftipo ira receber o tipo de dados que esta sendo utilizado dentro da classe e por consequencia no campo Fvalor.
Para definir o campo Ftipo, utilizei o procedimento AfterConstruction, este procedimento herdado da classe Tobject é chamado após o último construtor da classe ser executado. Segue abaixo o texto do Help do Delphi para melhor entendimento:
“Responds after the last constructor has executed.
AfterConstruction is called automatically after the object's last constructor has executed. Do not call it explicitly in your applications.
The AfterConstruction method implemented in TObject does nothing. Override this method when creating a class that performs an action after the object is created. For example, TCustomForm overrides AfterConstruction to generate an OnCreate event.”.

Neste procedimento, a nossa classe com o auxílio da System.TypInfo busca o tipo repassado ao criar um objeto do tipo Tvalor retornando o TtypeKind.
Tipos de TtypeKind:
TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString, tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray, tkUString, tkClassRef, tkPointer, tkProcedure).

Porque não usar a RTTI do Delphi?
No caso de Classes Genéricas, a RTTI não consegue achar a classe com o tipo especificado através da RTTI, não podendo retornar a classe e não retornando a classe, não se pode definir o tipo de dado da propriedade.