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.

quarta-feira, 26 de dezembro de 2012

Desenhar Um Ícone (bitmap) Em Células do Dbgrid

A dica abaixo serve para desenhar um ícone(bitmap) em cada célula de um dbgrid de acordo com o valor de um determinado campo da tabela... Ex: temos uma tabela "sexo" com o campo "sexo" que guarda os valores "M" para masculino e "F" para feminino. Então podemos fazer o dbgrid mostrar uma ícone(bitmap) de um homem ou um de uma mulher ao invés dos valores "M" e "F"...

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
  DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
  Icon: TBitmap;
begin
  Icon := TBitmap.Create;
  if (Column.FieldName = 'SHARES') then
  begin
    with DBGrid1.Canvas do
    begin
      Brush.Color := clWhite;
      FillRect(Rect);
      if (Table1.FieldByName('SHARES').Value > 4500) then
        ImageList1.GetBitmap(1, Icon)
      else
        ImageList1.GetBitmap(0, Icon);
      Draw(round((Rect.Left + Rect.Right - Icon.Width) / 2), Rect.Top, Icon);
    end;
  end;
end;

Curso de Delphi: 7.Consultas SQL