sábado, 28 de abril de 2007

Numéricos inteiros - Delphi

Existem vários tipos diferentes para dados numéricos no Delphi. Os tipos mais comumente usados são o “Integer” e o “Real”, eles podem parecer suficientes e satisfatórios para seu cotidiano até o dia ... que ao candidatar-se para uma vaga ... aquela provinha básica ... surge uma questão do tipo:

a) function ProcessaNumero(value: Byte): integer; overload;
b) function ProcessaNumero(value: ShortInt): integer; overload;
c) function ProcessaNumero(value: word): integer; overload;
d) function ProcessaNumero(value: Cardinal): integer; overload;
e) function ProcessaNumero(value: Double): integer; overload;

o trecho de código abaixo irá executar qual das versões sobrecarregadas da função “ProcessaNumero”?

var
ZnNumber: Integer;
begin
ZnNumber := ProcessaNumero(-1);
end;


Na maioria das linguagens de programação vc vai ter tipos numéricos inteiros de 8 , 16, 32 e 64 bits, no Delphi não é diferente. O chamado grupo dos tipos “Integer types” apresentam algumas características que devem ser observadas. No que tange a sinalização, exceto o de 64 bits, esses grupos se dividem em sinalizados e não sinalizados. Ou seja, tipos numéricos cujo tamanho reservado para ele será de 8 bits, sinalizado (positivo e negativo) e não sinalizado (apenas positivos). O mesmo para os de 16, bem como para os de 32 bits. Esses tipos, que se enquadram dentro desse contexto, sinalizados e não, quem são eles?

8 bits:
Byte – Não sinalizado.
Shortint – Sinalizado.

16 bits:
Word - Não sinalizado.
Smallint – Sinalizado.

32 bits:
Longword - Não sinalizado.
Longint - Sinalizado.


Vc pode me perguntar: E o Interger? E o Cardinal? Não estariam dentro dessa situação?
Antes de responder, gostaria que vc me respondesse: Qual é o tamanho do Integer? 32 bits, seria a sua resposta? Qual o tamanho do Cardinal? 32 bits, seria a sua resposta? Pois bem amigo, o Integer, bem como o Cardinal, variam de tamanho de acordo com a versão do Delphi. Em outras palavras, se Delphi 16 bits, então Integer e Cardinal possuem tamanho de 16 bits, o mesmo vale para o Delphi 32 bits. Isso acontece para compatibilidade com arquitetura do processador e o sistema operacional. Portanto, não me surpreenderia se o Integer, a partir do Delphi 64 bits, que virá em breve, novamente tenha seu tamanho adaptado a nova arquitetura. Portanto, o Integer e o LongInt não são exatamente a mesma coisa. Do mesmo modo o Cardinal e o Longword, são a partir desse ponto de vista, são tipos distintos. Observe que o Integer e Cardinal são usados com freqüência pq eles correspondem a representação numérica nativa da CPU.
No mais, como recurso mnemônico para distinguir quais são os sinalizados e quais não são, observe como referencia a partícula “int” no nome do tipo. Todos os sinalizados possuem “int” no nome. Exemplo:

Sinalizados: Shortint, smallin, longint e Integer.
Não sinalizados: Byte, Word, Longword, Cardinal.

Generic integer types










Type Range Format .NET Type Mapping
Integer -2147483648..2147483647 signed 32-bit System.Int32
Cardinal 0..4294967295 unsigned 32-bit System.UInt32


Fundamental integer types include Shortint, Smallint, Longint, Int64, Byte, Word, and Longword.

Fundamental integer types


















































Type

Range

Format

.NET Type Mapping

Shortint

-128..127

signed 8-bit

System.SByte

Smallint

-32768..32767

signed 16-bit

System.Int16

Longint

-2147483648..2147483647

signed 32-bit

System.Int32

Int64

-2^63..2^63-1

signed 64-bit

System.Int64

Byte

0..255

unsigned 8-bit

System.Byte

Word

0..65535

unsigned 16-bit

System.UInt16

Longword

0..4294967295

unsigned 32-bit

System.UInt32



Em geral operações aritméticas com inteiros retorna um valor do tipo Integer, 32 bits.
Um retorno de 64 bits apenas é possível quando um ou mais operadores sejam Int64
A seguir, um exemplo de código que produzirá um resultado incorreto.


function ResultadoIncorreto(Value: Integer): Int64;
var
Aux: Integer;
Begin
(* Atribuindo o máximo valor de um numérico sinalizado de 32 bits *)
Aux := High(Value);
(* ao somar mais um, estou ultrapassando a faixa de tamanho para esse tipo.
Ou seja, supostamente estou forçando um resultado cujo o tipo seja de
64 bits *)
result := Aux + 1; // contudo, o resultado não é o esperado ... hehhehehhe
end;


procedure TForm1.Button1Click(Sender: TObject);
const
Msg = 'Resultado %d';
begin
self.Caption := Format(Msg, [ResultadoIncorreto(200)]);
end;


A forma correta para se chegar ao resultado esperado seria utilizando um cast para 64 bits:

function ResultadoIncorreto(Value: Integer): Int64;
var
Aux: Integer;
begin
(* Atribuindo o máximo valor de um numérico sinalizado de 32 bits *)
Aux := High(Value);
(* ao somar mais um, estou ultrapassando a faixa de tamanho para esse tipo.
Contudo, para que meu resultado seja capaz de suportar este número preciso
fazer um cast para que resultado da operação seja de 64 bits *)
result := Int64(Aux) + 1; // Agora sim conseguirei o resultado esperado.
end;


Obs:
As funções suportam plenamente como argumentos valores Int64. Todavia, algumas outras funções padrões truncarão argumentos desse tamanho. Veja, High, Low, Succ, Pred, Inc, Dec, IntToStr, e IntToHexRound, Trunc, StrToInt64, e StrToInt64Def são funções que retornam valores Int64.


Outro exemplo:

O que acontece quando vc tenta incrementar o último valor de um tipo inteiro? Ou quando vc tenta decrementar o primeiro? No caso de incrementar o último, vai retornar o primeiro valor. Como vc pode observar no exemplo anterior quando não fizemos o cast para Int64, o retorno foi “-2147483648”. No outro caso, justamente o contrário. Se eu tentar decrementar -2147483648 o retorno será “2147483648” positivo. Isso acontece para todos os “Integer Types”. Teste o trecho de código abaixo:

procedure TForm1.Button1Click(Sender: TObject);
var
I: Shortint;
begin
I := High(Shortint);
I := I + 1;
Self.Caption := Format('%d', [i])
end;

O resultado será – 128. Contudo, se nas opções do projeto (menu Project ► Options), em compiler a checkbox range-checking estiver checada, habilitando a checagem de range, vc vai receber um exceção bacana.







Agora pq isso acontece? Eis o “x” da questão ... vc vai decorar que isso acontece com os Integer Types? Garanto que se vc entender o pq não vai precisar decorar.


Ordinal Types

A Delphi language classifica os tipos de acordo com algumas características que eles apresentam segundo sua estrutura. Existem os Simple Types, um sub-conjunto deles são os Ordinal Types. Dele fazem parte os integer, character, Boolean, enumerated, e subrange types.
Um Ordinal Type, define um conjunto de valores no qual cada valor, exceto o primeiro, possui obrigatoriamente um único predecessor. Bem como, cada valor, exceto o último possui um único sucessor.
É importante conhecer as funções predefinadas que operam sobre os ordinal types.






































Function

Parameter

Return value

Remarks

Ord

ordinal expression

ordinality of expression's value

Does not take Int64 arguments.

Pred

ordinal expression

predecessor of expression's value


Succ

ordinal expression

successor of expression's value


High

ordinal type identifier or variable of ordinal type

highest value in type

Also operates on short-string types and arrays.

Low

ordinal type identifier or variable of ordinal type

lowest value in type

Also operates on short-string types and arrays.


Exemplo:

A) High(Byte) retorna 255, pq o maior valor possível num tipo byte é 255.
B) Succ(5) retorna 6. Pq 5 é um tratado pelo compilador como um tipo Integer, logo, o inteiro sucessor de 5 é o 6.
C) No caso de booleanos é correto a expressão: Succ(False) = True;


As procedures Inc() e Dec(), incrementam e decrementam valores cujo o tipo pertença ao conjunto dos Ordinal Types. Por exemplo

Inc(I);// é equivalente a Succ(i), ou I := I + 1;






Artigo completo (View Full Post)

sexta-feira, 27 de abril de 2007

Delphi - Implementando suporte a eventos na sua classe

Este post mostra em Delphi como implementar suporte a eventos em suas classes.

Este post mostra a mesma funcionalidade em C#. Eu recomendo dar uma olhada para alguns detalhes explicativos.

Primeiro vamos ver nossa classe

unit classesunit;

interface

type
TEventoInverter = procedure(const msg: string) of object;

TFigura = class
private
eixoX: Double;
eixoY: Double;
eixoZ: Double;
FAposInverter: TEventoInverter;
public
constructor Create(a, b, c: Double);
procedure InverterX;
procedure InverterY;
procedure InverterZ;

published
property AposInverter: TEventoInverter read FAposInverter write FAposInverter;
end;


implementation

{ TFigura }

constructor TFigura.Create(a, b, c: Double);
begin
eixoX := a;
eixoY := b;
eixoZ := c;
end;

procedure TFigura.InverterX;
begin
eixoX := -eixoX;
FAposInverter('Invertido eixo X');
end;

procedure TFigura.InverterY;
begin
eixoY := -eixoY;
FAposInverter('Invertido eixo Y');
end;

procedure TFigura.InverterZ;
begin
eixoZ := -eixoZ;
FAposInverter('Invertido eixo Z');
end;

end.


Primeiro vamos analisar a seção type:
TEventoInverter = procedure(const msg: string) of object;


Esta é a forma de declarar um tipo procedure em Delphi.

Este tipo (TEventoInverter) representa uma procedure que aceita um parâmetro string.

FAposInverter: TEventoInverter;
...
property AposInverter: TEventoInverter read FAposInverter write FAposInverter;


Nos métodos InverterX, InverterY e InverterZ nós "disparamos" o evento AposInverter, representado pelo campo privado FAposInverter. Como o evento precisa de um parâmetro string nós passamos a mensagem de acordo com o eixo que é invertido.

Agora vamos trabalhar com essa classe e interceptar este evento:

unit formprincipalunit;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, classesunit;

type
TFormPrincipal = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
Figura: TFigura;
procedure FiguraAposInverter(const msg: string);
{ Private declarations }
public
{ Public declarations }
end;

var
FormPrincipal: TFormPrincipal;

implementation

{$R *.dfm}

procedure TFormPrincipal.Button1Click(Sender: TObject);
begin
Figura.InverterX;
end;

procedure TFormPrincipal.Button2Click(Sender: TObject);
begin
Figura.InverterY;
end;

procedure TFormPrincipal.Button3Click(Sender: TObject);
begin
Figura.InverterZ;
end;

procedure TFormPrincipal.FiguraAposInverter(const msg: string);
begin
ShowMessage(msg);
end;

procedure TFormPrincipal.FormCreate(Sender: TObject);
begin
Figura := TFigura.Create(3, 2, 5);
Figura.AposInverter := FiguraAposInverter;
end;

end.


Este Form possui 3 botões: cada um inverte um eixo.

Agora vamos avaliar as "peças":

private
Figura: TFigura;
procedure FiguraAposInverter(const msg: string);


Temos a variável Figura que vai receber a instância da classe TFigura e o procedimento FiguraAposInverter que vai interceptar o evento:

procedure TFormPrincipal.FormCreate(Sender: TObject);
begin
Figura := TFigura.Create(3, 2, 5);
Figura.AposInverter := FiguraAposInverter;
end;


É no evento OnCreate do From que nós criamos a instância de TFigura para a variável Figura e definimos que o manipulador do evento AposInverter é a nossa procedure FiguraAposInverter.

Bem... ficou um pouco maior do que eu pensei mas tá aí. Grande abraço a todos e até a próxima!

Artigo completo (View Full Post)

C# - Implementando suporte a eventos na sua classe

Olá novamente a todos. Neste post vou escrever sobre uma técnica interessante e com certeza muito útil no desenvolvimento de componentes - Eventos.

Nas linguagens de programação e IDEs modernas nós trabalhamos com eventos o tempo todo - OnClick do botão, OnChange da caixa de texto, etc...

Vamos fazer um pequeno exemplo. Vou criar uma classe chamada Figura, que representa uma figura com os eixos X, Y e Z.

class Figura {

// eixos da figura
private float eixoX = 0;
private float eixoY = 0;
private float eixoZ = 0;

// contrutor da classe
public Figura(float a, float b, float c) {
eixoX = a;
eixoY = b;
eixoZ = c;
}


// métodos que invertem os eixos
public void InverterX() {
eixoX = -eixoX;
AposInverter("X invertido");
}

public void InverterY() {
eixoY = -eixoY;
AposInverter("Y invertido");
}

public void InverterZ() {
eixoZ = -eixoZ;
AposInverter("Z invertido");
}

// definição do evento AposInverter
public delegate void FigureHandler(string msg);
public static FigureHandler AposInverter;
}

Esta classe (Figura) possui 3 campos de dados (eixoX, eixoY e eixoZ), 3 métodos (InverterX, InveterY e InterterZ) e 1 evento (AposInverter)

Vamos detalhar um pouco a declaração do evento AposInverter:

1. definimos o delegate do método a ser executado para o evento.
Delegate, resumidamente, representa uma chamada à execução de um método. Na linguagem C++ há um recurso parecido chamado callback function, que representa um apontamento (ponteiro) para um método. Em PHP também há um recurso semelhante que eu exemplifiquei neste post. Em C# a implementação é mais elegante e protegida também. O delegate é um tipo em C#; nós temos que definir o delegate em tempo de desenvolvimento.

Vamos rever o delegate:


public delegate void FigureHandler(string msg);


Neste caso o delegate FigureHandler é um método sem retorno (void) que aceita um parâmetro (string msg).

Nos métodos InveterX, InveterY e InverterZ temos o disparo do evento AposInveter informando o que foi invertido. Com isso podemos criar na nossa classe principal uma função que é executada quando este evento acontece, podendo assim agir de acordo com a inversão dos eixos.

Vamos ver a implementação na classe principal:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DelegatesApp {
public partial class Form1 : Form {

private Figura figura;

public Form1() {
InitializeComponent();

figura = new Figura(10, 20, 30);
Figura.AposInverter += new Figura.FigureHandler(figura_AposInverter);
}

private static void figura_AposInverter(string msg) {
MessageBox.Show(msg);
}

private void button1_Click(object sender, EventArgs e) {
figura.InverterX();
}

private void button2_Click(object sender, EventArgs e) {
figura.InverterY();
}

private void button3_Click(object sender, EventArgs e) {
figura.InverterZ();
}

}
}

Esta classe representa um Windows Form de uma Windows Application em C#.

Primeiro temos um campo privado dla classe figura do tipo Figura (nossa classe criada).
No construtor da classe Form1 criamos uma instância da classe Figura na variável figura e conectamos o método figura_AposInverter ao evento AposInverter da classe Figura

private static void figura_AposInverter(string msg) {
MessageBox.Show(msg);
}

Aqui criamos o método no modelo do manipulador de evento (delegate) da classe.

Então no evento Click dos botões invertemos cada um dos eixos. O comportamento esperado do programa é chamar um MessageBox.Show() a cada inversão de eixo.

Então é isso. Particularmente eu achei muito simples a forma de implementar algo tão útil no desenvolvimento.

Abraços a todos e, como sempre, fiquem à vontade para comentar e participar.

Artigo completo (View Full Post)

Um pouco sobre a arquitetura dos aplicativos Delphi


The Architecture of Delphi Applications - little about

O Delphi gera um aplicativo (programa) na forma de um objeto. Na pratica são dois objetos: O Apllication, da classe TApllication, e o Screen, da classe TScreen.

TApplication

Nos programas que fazemos em Delphi, muitas vezes o objeto Application é referenciado em código. Suponho que muitos o fazem sem ter a mínima idéia do que se trata. Neste artigo vamos falar um pouco sobre esse objeto, e estudar algumas técnicas de como trabalhar com ele.

O Objeto Application, da classe TApplication, esta definido na Unit “Forms”. Veja na figura abaixo, a declaração de uma seção “var” onde é declarada uma série de objetos, dentre eles o Application, tratados pelos engenheiros do Delphi, na própria VCL, como objetos globais.


Só por curiosidade, eu fiz um busca no BDS, e encontrei 46 units da VCL usam a unit Forms. No D7 são 54. Só pra vc ter um idéia da visibilidade desses objetos dentro da VCL.



Embora seja definido aqui, ele só é criado, instanciado na unit “Controls”, especificamente na procedure “InitControls”. Veja um screenshot da Unit “Controls”.

“Application is a global object of the TApplication class, defined in the Forms unit and created in the Controls unit” (Mastering Delphi 7).




A classe TApplication, também está definida na unit “Forms”. Ela herda de TComponent, algumas de suas propriedades podem ser configuradas no menu Project ►Options (Shift + Ctrl + F11), Application (no D7, D6 ou D5 fica numa aba; No BDS item da treeview).



Em Project Options, Forms (no D7, D6 ou D5 fica numa aba; No BDS item da treeview).vc pode também configurar qual dos Forms em sua aplicação será o principal, bem como quais serão criados, alocados, imediatamente assim que a aplicação for executada, e os que ficarão disponíveis para serem instanciados dinamicamente.




O componente ApplicationEvents (TApplicationEvents, palheta Additional) nos permite manipular os eventos do objeto Application.

Vamos para nosso primeiro exemplo: Manipulando os evento do objeto Application com o “TApplicationEvent”.

1 - Inicie uma nova aplicação.

1.1 Adicione um outro Form, Form2.

Construindo o Form1

Adicione e configure os componentes no Form1 segundo listagem abaixo:

Form1: TForm1
Left = 200
Top = 280
Caption = 'Form1'
ClientHeight = 98
ClientWidth = 434
Position = poDesigned

Panel1: TPanel
Name = PnlForm
Left = 16
Top = 28
Width = 396
Height = 41

ApplicationEvents1: TApplicationEvents





Codificando o evento OnActivate do Application1, “ApplicationEvents1Activate”:

procedure TForm1.ApplicationEvents1Activate(Sender: TObject);
begin
PnlForm1.Caption := 'Application Active';
PnlForm1.Color := clYellow;
Self.Color := clBtnFace;
Beep;
end;



Codificando o evento OnDeactivate do Application1, “ApplicationEvents1Deactivate”:

procedure TForm1.ApplicationEvents1Deactivate(Sender: TObject);
begin
PnlForm1.Caption := 'Application Not Active';
PnlForm1.Color := clWhite;
end;


Codificando o evento OnException do Application1, “ApplicationEvents1Exception”:

procedure TForm1.ApplicationEvents1Exception(Sender: TObject; E: Exception);
var
ARed, AGreen, ABlue: Integer;
begin

ARed := RandomCollor;
AGreen := RandomCollor;
ABlue := RandomCollor;
Self.Caption := E.Message +' - '+ E.ClassName;
Self.Color := RGB(ARed, AGreen, ABlue);
PnlForm1.Caption := E.ClassName + ' - Trate a exceção aqui.';
PnlForm1.Color := clAqua;


end;


Codificando o procedimento RandomCollor, declarado na seção “private” do Form1:

function TForm1.RandomCollor: Integer;
begin
(* retorna aleatoriamente um valor para colorir o form *)
Randomize;
Result := RandomRange(0, 255);
end;


Codificando o evento OnActivate do Form1, “FormActivate”:

procedure TForm1.FormActivate(Sender: TObject);
begin
PnlForm1.Caption := 'Form1 Active';
PnlForm1.Color := clRed;
end;


Codificando o evento OnDeactivate do Form1, “FormDeactivate”:

procedure TForm1.FormDeactivate(Sender: TObject);
begin
PnlForm1.Caption := 'Form1 Not Active';
PnlForm1.Color := clBtnFace;
end;




Construindo o Form2.
Adicione e configure os componentes no Form2 segundo listagem abaixo:
OBS: Set a propriedade “Visible” do Form2 para “true”, isso fará com que os dois forms sejam exibidos quando a aplicação for executada.


Form2: TForm2
Left = 230
Top = 0
Caption = 'Form2'
ClientHeight = 236
ClientWidth = 426
Position = poDesigned
Visible = True

ListBox1: TListBox
Name = LstForm2
Left = 0
Top = 39
Width = 426
Height = 197
Align = alBottom
ItemHeight = 13

Button1: TButton
Name = BtnGeraExcecao
Left = 330
Top = 8
Width = 88
Height = 25
Caption = 'Gerar Exceção’

Edit1: TEdit
Name = EdtExcecao
Left = 4
Top = 12
Width = 317
Height = 21



Vamos criar um propriedade para o From2. Isso servirá para contarmos quantas vezes o Form2 for ativado. Além disso vou declarar tb uma exceção, aqual usaremos mais tarde, Na seção “type”, em “public” digite conforme exemplificado abaixo:

type
EZnException = class (Exception);
TForm2 = class(TForm)
LstForm2: TListBox;
BtnGeraExcecao: TButton;
EdtExcecao: TEdit;
private
{ Private declarations }
public
property ContAtivacao: Integer; // Após digitar, pressione “Ctrl+Shift+C”
end;


Ao pressionar “Ctrl+Shift+C” a IDE irá completar para vc a implementação da propriedade.

Codificando o evento OnActivate do Form2, “FormActivate”:

procedure TForm2.FormActivate(Sender: TObject);
const
TxtAtivacao = 'Form2 ativado pela %d° Vez.';
begin
SetContAtivacao(FContAtivacao + 1);
LstForm2.Items.Add(Format(TxtAtivacao, [FContAtivacao]));
end;


Codificando o evento OnDeactivate do Form2, “FormDeactivate”:

procedure TForm2.FormDeactivate(Sender: TObject);
begin
LstForm2.Items.Add('Form2 Not Active');
end;


Codificando o evento OnClick do BtnGeraExcecao, “BtnGeraExcecaoClick”:
Na seção type definimos anteriormente a exceção EZnException

procedure TForm2.BtnGeraExcecaoClick(Sender: TObject);
begin
if EdtExcecao.Text = '' then
raise Exception.Create('EdtExcecao vazio.')
else
raise EZnException.Create(EdtExcecao.Text);
end;


Segundo exemplo:
2 - Inicie uma nova aplicação.

2.1 - Adicione e configure os componentes no Form1 segundo listagem abaixo:

Form1: TForm1
Caption = 'Exemplo 2'
ClientHeight = 293
ClientWidth = 426

Label1: TLabel
Left = 120
Top = 12
Caption = 'Digite o Nome do Form'

RadioGroup1: TRadioGroup
Name = RdgOwner
Left = 8
Top = 12
Width = 101
Height = 73
Caption = 'Definir Owner'
Items.Strings = (
'Application'
'Self'
'Nil')

Edit1: TEdit
Name = EdtNameFrom
Left = 120
Top = 31
Text = ''

CheckBox1: TCheckBox
Name = ChkShow
Left = 120
Top = 68
Caption = 'Exibir'

Button1: TButton
Name = BtnCriar
Left = 344
Top = 29
Caption = 'Criar Form'

ListBox1: TListBox
Name = LstObjetos
Left = 0
Top = 95
Width = 426
Height = 179
Align = alBottom

StatusBar1: TStatusBar
Adicione dois Panels

Button1: TButton
Left = 343
Top = 64
Width = 75
Height = 25
Caption = 'Listar Forms'

ApplicationEvents1: TApplicationEvents



Codificando o evento OnActivate do Application1, “ApplicationEvents1Activate”:

procedure TForm1.ApplicationEvents1Activate(Sender: TObject);
begin
(* Ao ativar a aplicação, seta um valor para a propriedade name do objeto Application. Isso terá apenas valor didático para o nosso exemplo *)
Application.Name := 'ZnApplication';
end;


Codificando o evento OnClick do BtnCriar, “BtnCriarClick”:

procedure TForm1.BtnCriarClick(Sender: TObject);
var
SomeForm: TForm;
Begin
(* Cria dinâmicamente um form, de acordo com a escolha do usuário, num Owner específico.*)
case RdgOwner.ItemIndex of
0: Application.CreateForm(TForm, SomeForm);
1: SomeForm := TForm.Create(Self);
2: SomeForm := TForm.Create(Nil);
end;
// Atribuindo valores que permitirão identificarmos o form
SomeForm.Name := EdtNameFrom.Text;
SomeForm.Caption := EdtNameFrom.Text;
SomeForm.Visible := ChkShow.Checked;

//Atribuindo o procedimento FormActivate
SomeForm.OnActivate := FormActivate;

EdtNameFrom.SetFocus;
end;


Codificando o evento OnClick do Button1, “Button1Click”:

procedure TForm1.Button1Click(Sender: TObject);
const
FormNameAndOwner = 'ClassName: %s - Name: %s - Owner: %s';
var
I: Integer;
MyOwner: TComponent;
begin
(*Monta uma lista de todos os componentes associados ao objeto Application*)
for I := 0 to Application.ComponentCount - 1 do
begin
if Application.Components[i] is TComponent then
begin
MyOwner := TComponent(Application.Components[i]).Owner;
LstObjetos.Items.Add(Format(FormNameAndOwner,
[TComponent(Application.Components[i]).ClassName,
TComponent(Application.Components[i]).Name,
MyOwner.Name]));
if Application.Components[i] is TForm then
if NOT(Application.Components[i] = Application.MainForm) then
TForm(Application.Components[i]).Visible := ChkShow.Checked;
end;
end;

end;


Montando a listagem dos componentes associados ao Application estou tentando iluminar um pouco a arquitetura de um aplicativo Delphi. Os forms que forem criados na segunda opção do radiogroup (cria o form no “Self”), ou seja, o Owner dele é o form1, portanto, ele é “neto” do Application.
Outro ponto importante é que os forms criados com Owner nulo, terceira opção do radiogroup, implica que esses forms não serão desalocados quando o objeto Application for destruído. Eles ficarão perdidos, como lixo na memória. Outro ponto curioso que vc pode testar é que este é o único caso que é permitido criar várias instâncias cujos nomes sejam iguais. Depois, é possível que vc recebe alguns acessos violados por isso ... hehehhe ... Em suma, esses dois aspectos têm um impacto muito negativo.


Codificando o evento OnActivate do Form1, “FormActivate”:

procedure TForm1.FormActivate(Sender: TObject);
var
I: Integer;
Begin
(* Exibe na statusbar o nome do form ativo *)
for I := 0 to Application.MainForm.ComponentCount - 1 do
if Application.MainForm.Components[i] is TStatusBar then
TStatusBar(Application.MainForm.Components[i]).Panels[0].Text :=
'Form ativo: ' + TForm(Sender).Name;

end;

Quando vc executar o aplicativo, criar os forms, clickar sobre cada um deles e observar a statusbar ... clickar sobre o Button1 “Listar Forms”, que na verdade esta listando componentes associados ao Application, vc vai notar a presença de um “THintWindow” ... tchan, tchan, tchan ... Quem é ele?
THintWindow – Classe da janela que exibe o hint, dos controles.

“the small pop-up window that appears over a control at runtime when the control has its ShowHint property set to true”.

A classe TApplication tem um atributo privado que é um objeto, FHintWindow cujo o tipo é THintWindow. Quando é atribuído valor para a sua propriedade “ShowHint” (property ShowHint: Boolean read FShowHint write SetShowHint), no método privado SetShowHint, é instanciado, apontando para o outro objeto global “HintWindowClass”, o qual também é instanciado nesse momento. Veja o trecho de código que retirei da VCL:

procedure TApplication.SetShowHint(Value: Boolean);
begin
if FShowHint <> Value then
begin
FShowHint := Value;
if FShowHint then
begin
FHintWindow := HintWindowClass.Create(Self);
FHintWindow.Color := FHintColor;
end
else
begin
FHintWindow.Free;
FHintWindow := nil;
end;
end;
end;

Veja também a imagem com a declaração dos objetos globais na unit Forms. No screenshot abaixo, debuggando comprovo que acabei de afirmar no parágrafo acima.



Ok, por causa dessa associação com objeto HintWindowClass achei que seria interessante dar uma incrementada no exemplo que implementamos anteriormente. Os componentes no form1 são os mesmos apenas acrescentei duas funcionalidades novas para explorar o “HintWindowClass”. Além disso acho que dessa forma podemos jogar mais luz sobre como a VCL arquiteta o aplicativo Delphi. As funcionalidades são: A primeira visa, através do Application recuperar os hists exibidos de cada componentes visual presente no form. A outra, é a conseqüência dessa, para exibir os hints, preciso de componentes nos forms que são criados dinamicamente. Mas, se estou criando esses forms dessa forma, como vou adicionar componentes nele? Ou, quais componentes usarei para isso?
Simples, vamos criá-los dinamicamente, e ateatóriamente ... hehehheh ... vai ser divertido.
Na seção “private”, declararemos as seguintes rotinas:

  • procedure BuildSetHint;
  • procedure CreateComponent(AOwner: TComponent; AParent: TWinControl);
  • function ZnCreateControlByName(AClassName: string; AOwner: TComponent): TControl;
  • function BuidComponentName(const AClassName: string; const NumId: Integer): string;
veja como fica:
Antes preciso definir uma constante, um array de classes ....

const
ZnObjectLst: array[0..9] of TPersistentClass = (TEdit, TBitbtn,
TMonthCalendar, TSpinEdit, TSpinButton, TMemo, TShape,
TLabel, TComboBox, TListBox);


private
procedure BuildSetHint;
procedure CreateComponent(AOwner: TComponent;
AParent: TWinControl);
function ZnCreateControlByName(AClassName: string;
AOwner: TComponent): TControl;
function BuidComponentName(const AClassName: string;
const NumId: Integer): string;
public
{ Public declarations }
end;

O bom e velho “Ctrl+Shit+C” para partimos para implementação do corpo de cada uma das rotinas.


procedure TForm1.BuildSetHint;
const
ATxtHint = '%s teste hint Zn - Class: %s.';
var
I: Integer;
J: Integer;
MyControl: TControl;
begin
(* Monta e atribui um hint a cada componente vinculado, em algun nível (neste caso, somente dois níveis, mas poderíamos fezer essa busca recursivamente ...), ao objeto Application. *)
for I := 0 to (Application.ComponentCount - 1) do
begin
if Application.Components[i] is TForm then
for J := 0 to TForm(Application.Components[i]).ComponentCount - 1 do
if TForm(Application.Components[i]).Components[J] is TControl then
begin
MyControl :=
TControl(TForm(Application.Components[i]).Components[J]);
MyControl.Hint := Format(ATxtHint, [MyControl.Name,
MyControl.ClassName]);
MyControl.ShowHint := True;
end; // fim if then ...
end; // fim laço I
end;


Para criar os componentes dinamicamente:

function TForm1.ZnCreateControlByName(AClassName: string;
AOwner: TComponent): TControl;
var
Cls: TControlClass;
begin
(* Aqui a chapa esquenta um pouco ...
Essa função retorna uma classe dinamicamente. Para fazer isso,
vc tem que registra, também em tempo de execução, a classe.
Mais tarde explicaremos o pq disso ....*)

Result := nil;
RegisterClasses(ZnObjectLst);
Cls := TControlClass(GetClass(AClassName));
if Cls = nil then exit;
Result := Cls.Create(AOwner);


end;

Essa é molezinha,
peace of cake ...

function TForm1.BuidComponentName(const AClassName: string;
const NumId: Integer): string;
begin
Result := 'Zn' + Copy(AClassName, 2,
Length(AClassName) - 2) + IntToStr(NumId);
end;




procedure TForm1.CreateComponent(AOwner: TComponent; AParent: TWinControl);
var
OrdElement: Integer;
ZnControl: TControl;
begin
(* Determina, aleatóriamente que componente será criado,
seta noem, posição para o componente novo *)
Randomize;
OrdElement := RandomRange(0,9);
ZnControl :=
ZnCreateControlByName(ZnObjectLst[OrdElement].ClassName, AOwner);
OrdElement := 1;
with ZnControl.Create(AOwner) do
begin
ZnControl.Parent := AParent;
while AOwner.FindComponent(BuidComponentName(
ZnControl.ClassName, OrdElement)) <> nil do
Inc(OrdElement,1);

Name := BuidComponentName(ZnControl.ClassName, OrdElement);
Randomize;
Top := RandomRange(0, (AParent.Height - ZnControl.Height) - 12);
Randomize;
Left := RandomRange(0, (AParent.Width - ZnControl.Width) - 12);
end;
end;


Agora usaremos o evento OnShowHint do Applicatio, através do ApplicationEvents1, para capturarmos centralizadamente todos os hins exibidos na aplicação. Evento OnShowHint do ApplicationEvent1, “ApplicationEvents1ShowHint”:

procedure TForm1.ApplicationEvents1ShowHint(var HintStr: string;
var CanShow: Boolean; var HintInfo: THintInfo);
begin
StatusBar1.Panels[1].Text := Application.Hint;
end;

Pronto, acho que por aqui já da pra se ter uma noção da arquitetura ...
Estou exausto, não agüento mais falar sobre isso, o assunto é muito extenso. Estou louco de cansaço. Depois falaremos sobre o objeto Screen. Gostaria muito de montar um diagrama exemplificando a reação entre esses objetos e suas interfaces ... fica mais tarde ... quem sabe. Por garantia, segue o código da unit completo .... By


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Spin, ExtCtrls, AppEvnts, Buttons;

const
ZnObjectLst: array[0..9] of TPersistentClass = (TEdit, TBitbtn,
TMonthCalendar, TSpinEdit, TSpinButton, TMemo, TShape,
TLabel, TComboBox, TListBox);


type
TForm1 = class(TForm)
RdgOwner: TRadioGroup;
EdtNameFrom: TEdit;
Label1: TLabel;
ChkShow: TCheckBox;
BtnCriar: TButton;
LstObjetos: TListBox;
StatusBar1: TStatusBar;
Button1: TButton;
ApplicationEvents1: TApplicationEvents;
procedure ApplicationEvents1ShowHint(var HintStr: string;
var CanShow: Boolean; var HintInfo: THintInfo);
procedure ApplicationEvents1Activate(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure BtnCriarClick(Sender: TObject);
private
procedure BuildSetHint;
procedure CreateComponent(AOwner: TComponent;
AParent: TWinControl);
function ZnCreateControlByName(AClassName: string;
AOwner: TComponent): TControl;
function BuidComponentName(const AClassName: string;
const NumId: Integer): string;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses Math, Strutils;

{$R *.dfm}

procedure TForm1.ApplicationEvents1Activate(Sender: TObject);
begin
Application.Name := 'ZnApplication';
BuildSetHint;
end;

procedure TForm1.ApplicationEvents1ShowHint(var HintStr: string;
var CanShow: Boolean; var HintInfo: THintInfo);
begin
StatusBar1.Panels[1].Text := Application.Hint;
end;

procedure TForm1.BtnCriarClick(Sender: TObject);
const
TextCaptionFrm = '%s - Owner: %s';
var
SomeForm: TForm;
AownerName: string;
begin
(*Cria dinamicamente um Form e, aleatóriamente, dois componentes
associados a ele. *)
case RdgOwner.ItemIndex of
0: Application.CreateForm(TForm, SomeForm);
1: SomeForm := TForm.Create(Self);
2: SomeForm := TForm.Create(Nil);
end;

SomeForm.Name := EdtNameFrom.Text;
if not Assigned(SomeForm.Owner) then
AownerName := 'Null'
else
AownerName := SomeForm.Owner.Name;
SomeForm.Caption :=
Format(TextCaptionFrm, [EdtNameFrom.Text, AownerName]);
SomeForm.Visible := ChkShow.Checked;
SomeForm.OnActivate := FormActivate;
CreateComponent(SomeForm, SomeForm);
CreateComponent(SomeForm, SomeForm);
EdtNameFrom.SetFocus;
BuildSetHint;
end;

function TForm1.BuidComponentName(const AClassName: string;
const NumId: Integer): string;
begin
Result := 'Zn' + Copy(AClassName, 2,
Length(AClassName) - 2) + IntToStr(NumId);
end;

procedure TForm1.BuildSetHint;
const
ATxtHint = '%s teste hint Zn - Class: %s.';
var
I: Integer;
J: Integer;
MyControl: TControl;
begin
(* Monta e atribui um hint a cada componente vinculado, em algun nível,
ao objeto Application. *)
for I := 0 to (Application.ComponentCount - 1) do
begin
if Application.Components[i] is TForm then
for J := 0 to TForm(Application.Components[i]).ComponentCount - 1 do
if TForm(Application.Components[i]).Components[J] is TControl then
begin
MyControl :=
TControl(TForm(Application.Components[i]).Components[J]);
MyControl.Hint := Format(ATxtHint, [MyControl.Name,
MyControl.ClassName]);
MyControl.ShowHint := True;
end; // fim if then ...
end; // fim laço I
end;

procedure TForm1.Button1Click(Sender: TObject);
const
FormNameAndOwner = 'ClassName: %s - Name: %s - Owner: %s';
var
I: Integer;
MyOwner: TComponent;
begin
for I := 0 to Application.ComponentCount - 1 do
begin
if Application.Components[i] is TComponent then
begin
MyOwner := TComponent(Application.Components[i]).Owner;
LstObjetos.Items.Add(Format(FormNameAndOwner,
[TComponent(Application.Components[i]).ClassName,
TComponent(Application.Components[i]).Name,
MyOwner.Name]));
if Application.Components[i] is TForm then
if Application.Components[i] <> Application.MainForm then
TForm(Application.Components[i]).Visible := ChkShow.Checked;
end;
end;

end;

procedure TForm1.CreateComponent(AOwner: TComponent; AParent: TWinControl);
var
OrdElement: Integer;
ZnControl: TControl;
begin
(* Determina, aleatóriamente que componente será criado,
seta noem, posição para o componente novo *)
Randomize;
OrdElement := RandomRange(0,9);
ZnControl :=
ZnCreateControlByName(ZnObjectLst[OrdElement].ClassName, AOwner);
OrdElement := 1;
with ZnControl.Create(AOwner) do
begin
ZnControl.Parent := AParent;
while AOwner.FindComponent(BuidComponentName(
ZnControl.ClassName, OrdElement)) <> nil do
Inc(OrdElement,1);

Name := BuidComponentName(ZnControl.ClassName, OrdElement);
Randomize;
Top := RandomRange(0, (AParent.Height - ZnControl.Height) - 12);
Randomize;
Left := RandomRange(0, (AParent.Width - ZnControl.Width) - 12);
end;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
I: Integer;
begin
(* Atribui a statusbar do form1 o nome do Form ativo.
Usamos ess implementação pq aqui, agora, em tempo de desenvovimento,
é impossível saber em que Form estará sendo processado ess código. *)

for I := 0 to Application.MainForm.ComponentCount - 1 do
if Application.MainForm.Components[i] is TStatusBar then
TStatusBar(Application.MainForm.Components[i]).Panels[0].Text :=
'Form ativo: ' + TForm(Sender).Name;

end;

function TForm1.ZnCreateControlByName(AClassName: string;
AOwner: TComponent): TControl;
var
Cls: TControlClass;
begin
(* Aqui a chapa esquenta um pouco ...
Essa função retorna uma classe dinamicamente. Para fazer isso,
vc tem que registra, também em tempo de execução, a classe.
Mais tarde explicaremos o pq disso ....*)
Result := nil;
RegisterClasses(ZnObjectLst);
Cls := TControlClass(GetClass(AClassName));
if Cls = nil then exit;
Result := Cls.Create(AOwner);


end;

end.


Artigo completo (View Full Post)

quarta-feira, 25 de abril de 2007

Nova funcionalidade: Busca do google no blog

Olá a todos.

A Equipe do Estação ZN orgulhosamente apresenta...

Olhe a barra lateral à esquerda ------->

Logo no início tem um campo de busca do Google.

A busca que você fizer vai aparecer logo abaixo, na mesma barra. E busca dentro do blog, a web, vídeos (traz do YT também!) e locais (link para o Google Maps).

Nesse site você encontra soluções de API do Google para várias tarefas no seu site. Sempre que eu encontrar alguma legal eu implemento aqui no Estação ZN.

Se alguém conhecer alguma legal e quiser indicar, fique à vontade!

Grande abraço a todos a até mais!

Artigo completo (View Full Post)

terça-feira, 24 de abril de 2007

Mal começou a Web 2.0 e já vem a Web 3.0!

Olá a todos.

Neste post trago uma novidade talvez não tão nova assim.

Sabe a Web? Isso mesmo, sites, etc, etc... Veio então a Web 2.0: O usuário gerando o conteúdo e consumindo o conteúdo gerado por usuários. Aeeewww!!!!

Tudo indo bem, tudo legal... E mal se começa a falar sobre Web 2.0 e já vem: Web 3.0.


Qual é a parada dessa vez? Segudo este artigo é Web semântica.

Não vou aqui repetir o artigo com outras palavras; portanto leia o artigo do site xD.

Resumindo o ponto central da Web 3.0 é interação entre os dados que circulam na Web e, através deste cruzamento de informações, ter a capacidade de responder as pergundas do usuário e responder ao usuário o que ele quer saber.

Conhecem o Google News? Pois é. Este site é um dos maiores sites de notícias da internet e advinha só: a atualização é feita automaticamente, se intervenção humana na parada! Se duvida vai lá dar uma olhada hehe.

Pelo artigo (e eu com certeza concordo) o e-commerce vai ganhar e muito com essa nova fase da Web:


Imagine um produto sendo descrito por áudio, vídeo e ainda ser classificado pela inteligência artificial por preço, loja, tendências de mercado, entre muitos outros atributos?



Vale a pena conferir este artigo e se preparar para a nova "versão" da Web. Abraços.

Artigo completo (View Full Post)

segunda-feira, 23 de abril de 2007

Aplicações Robustas IV: Propagando de Exceções


Esse artigo é continuação do post “Aplicações Robustas III

Dando continuidade a seqüência de artigos sobre tratamento de exceções.....

Neste artigo vamos tratar de como acontece a propagação de exceções. Já falamos anteriormente sem detalhamos muito, vamos agora testar alguns exemplos e observar mais de perto como isso acontece. Consequentimente estaremos apurando a tecnica de tratamento de exceções.

Propagando exceções:

Suponha que uma exceção vemha ocorrer dentro de uma função “A”, essa função foi chamada pela procedure “B”, que por sua vez foi executada no evento OnClick de um botão. De cara vc pode perceber o problema envolvido se vc tiver um tratamento já na função “A”. Como propar essa exceção? Algumas vezes, acredite isso contece, vc terá que propagar essa exceção até a achamda mais externa. Vamos para a prática ...

Construindo um exemplo:

Inicie uma nova aplicação no Delphi:

No D7 - Menu, File ►New ► Application.
No BDS - Menu, File ►New ► VCL Forms Application – Delphi for Win 32 (“Alt, n, r”).

Adicione uma nova Unit: Nela vamos copdificar uma função.

No D7
- Menu, File ►New ► Unit.
No BDS - Menu, File ►New ► Unit – Delphi for Win 32 (“Alt, n, t”).

Eu vou salvar a Unit com o nome de “ZnFunctionPropagExcept”, o nome da unit do Form1 vou salvar como “ZnPropagExceptForm.pas”. Veja o Código da Unit:

unit ZnFunctionPropagExcept;

interface
uses DbClient, Db, Classes, SysUtils, Dialogs;

type
ZnException = class(Exception);

function SetDados(Lista: TStringList;
Cds: TClientDataSet; AField: TField): String;

procedure ForcaExcecao(const Value: string);

implementation

procedure ForcaExcecao(const Value: string);
begin
(* força uma exceção. Basta que um dos elementos da lista seja a palavra
exceção. *)
if AnsiUpperCase(Value) = AnsiUpperCase('exceção') then
raise ZnException.Create('ZnException: Buuuuuuuhhhhhhhh!!!!!!');
end;

function SetDados(Lista: TStringList;
Cds: TClientDataSet; AField: TField): String;
var
I: Integer;
begin
try
if Lista.Count <= 0 then
raise
Exception.Create('Nenhum elemento na lista.');
for I := 0 to Lista.Count - 1 do
begin
ForcaExcecao(Lista[i]);
if not Cds.Locate(AField.FieldName, Lista[i], []) then
begin
Cds.Append;
AField.AsString := Lista[i];
Cds.Post;
end;
end;
except
(*essa exceção não vou propagar*)
on E: ZnException do
MessageDlg(E.Message, mtInformation, [mbOK], 0);
(*Propagando a exceçaõ genérica*)
on E: Exception do raise;
end;
Result := Cds.XMLData;// retorna os dados no formato Xml numa string.
end;
end.



Na seção “uses” declaramos as seguintes bibliotecas: DbClient (para usarmos o TClientDataSet), Db (para usarmos o TField), Classes (para usarmos TStringList), SysUtils (onde está definida Exception), Dialogs (para usarmos a função “MessageDlg”).
Na seção “type” declaramos uma exceção “ZnException” aqual usaremos no nosso tratamento. Os dois modulos cdificados nesta Unit são: A função “SetDados” que recupera os dados de uma lista e atribui a um dataset, retorna um string que usaremos como log do que foi adicionado no dataset. O procedimento “ForcaExcecao”, como o próprio nome já indica força um exceção, algo que é impressindível para o nosso exemplo. Após codificar esta Unit, passe para a implentação do form1 na unit “ZnPropagExceptForm”.


No Form1 adicione os seguintes componentes: Um Edit (TEdit), um Button (TButton), um Label (Tlabe) e um ListBox (TListBox), da palheta Standard; Dois DbGrids (TDbGrid), da palheta Data Control; Dois DataSource(TDataSource), Dois ClientDataSet(TClientDataSet) Data access. Configure as propriedades dos componentes segundo listado abaixo:


Form1: TZnPropagExceptFrm
Name = ZnPropagExceptFrm
ClientHeight = 395
ClientWidth = 578
Caption = 'Propagando Exceções'

Label1: TLabel
Left = 16
Top = 8
Width = 31
Height = 13
Caption = 'Digite o Dado'

ListBox1: TListBox
Name = LstDados
Left = 16
Top = 56
Width = 121
Height = 329

Button1 : TButton
Name = BtnAddElemento
Left = 143
Top = 27
Width = 90
Height = 25
Caption = 'Add Elemento'

Edit1: TEdit
Name = EdtDados
Left = 16
Top = 29
Width = 121
Height = 21

ClientDataSet1: TClientDataSet
Name = cdsLog

ClientDataSet2: TClientDataSet
Name = cdsDados

DataSource1: TDataSource
Name = dsDados
DataSet = cdsDados

DataSource: TDataSource
Name = dsLog
DataSet = cdsLog

DbGrid1: TDBGrid
Name = GrdLog
Left = 248
Top = 216
Width = 322
Height = 169
DataSource = dsLog

DbGrid2: TDBGrid
Name = GrdDados
Left = 247
Top = 29
Width = 322
Height = 181
DataSource = dsDados

Button2: TButton
Name = BtnTransDados
Left = 143
Top = 70
Width = 90
Height = 25



A seção type da unit1 deverá estar assim

type
TZnPropagExceptFrm = class(TForm)
LstDados: TListBox;
BtnAddElemento: TButton;
EdtDados: TEdit;
Label1: TLabel;
dsDados: TDataSource;
cdsLog: TClientDataSet;
GrdLog: TDBGrid;
GrdDados: TDBGrid;
cdsDados: TClientDataSet;
dsLog: TDataSource;
BtnTransDados: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;


Campos persistentes:

Vamos adicionar um campo persistente em cada um dos ClientDataSets. O Nome do Campo vai ser “ZnTexto”, o tipo string, e tamanho 200 (quantidade de caracteres). Click com o botão direito do mouse sobre o ClientDataSet, escolha aopção, “Fields Editor”.




Agora botão direito no Field Editor, escolha a opção, “New Field”.



Cofigure conforme a figura abaixo e click “Ok”:



Pronto vc acabou de criar um campo persistente num dataset. Repita a mesma operação para o outro ClientDataSet.



Codificando o evento OnClick do botão “BtnAddElemento”, “BtnAddElementoClick”:


procedure TZnPropagExceptFrm.BtnAddElementoClick(Sender: TObject);
begin
(* Atribuido a lista o valor digitado no Edtdados *)
LstDados.Items.Add(EdtDados.Text);
EdtDados.Clear; // Limpa o Edit
EdtDados.SetFocus;// seta o foco para ele
end;

É chegado o momento de usarmos a Unit contendo os módulos que programamos onde está o tratamento de exceção. Faça use unit, ou seja no Menu, File ►Use Unit (Alt, f, u), ou Alt + F11.



Codificando o evento OnClick do botão “BtnTransDado”, “BtnTransDadosClick”:

procedure TZnPropagExceptFrm.BtnTransDadosClick(Sender: TObject);
var
Lst: TStringList;
begin
Lst := TStringList.Create;
(*Bloco protegido*)
try
try
Lst.Assign(LstDados.Items);
cdsLog.XMLData := SetDados(Lst, cdsDados, cdsDadosZnTexto);
except
(*Capturando a exceção propagada pela rotina "SetDados"*)
on Zn: Exception do
begin
Self.Caption := Zn.Message + '/'+ Zn.ClassName;
MessageBox(0, 'Exceção propagada capturada!!!!',
'Estação Zn', MB_ICONASTERISK or MB_OK);
end;
end;
finally
Lst.Free;
end;

end;


Vamos executar nosso programa para testar. Eu garanto que vai ocorrer uma exceção, não programe uma linha de código além do que foi proposto até aqui. Antes de qualquer coisa, vamos preparar a IDE para não interromper a execução do programa caso aja uma exceção.
No D7:
No menu Tools ► Debuggrer Options ► Aba “Language Exceptions”, desmarque a check “Stop on Delphi Exception” (Dica do Felipe)
No BDS:
No menu Tools ►Options ...Selecione, na treeview a esquerda, Borland Debugger, sub item, language Exceptions, e desmarque a check “Notify on Language Exceptions”. Veja na figura abaixo:



Execute, pressionado F9, adiciono alguns elementos clickando em “add Elemento” e click em “Transfere Dados”. Se vc esta sincronisado comigo vai ocorrer, propositalmente, o seguinte: Uma mensagem será exibida “Exceção propagada capturada!!!!”, e no caption do form deve estar a mensagem da exceção e o tipo da exceção, que será “EDatabaseError”.



Eu deixei o ClientDataSet fechado de propósito para forçarmos uma exceção diferente da “ZnException”, definida na Unit “ZnFunctionPropagExcept”. Pois de acordo com o que codificamos ela será a única que não será propagada. Portanto, nesse primeiro teste experientamos a propagaçao da exceção. Como o DataSet estava fechado, ao tentar executar o metodo “Locate”, foi levantada uma execeção “EDatabaseError”. A prova de que ela foi propagada foi a execução dos comandos da setrutura “on ..do” no except do evento OnClick botão “BtnTransDado”: A atribuição ao caption do Form, e a execução da função “MessageBox”.
Prosseguindo com o nosso exemplo, vamos acrescentar um botão que irá abrir os ClientDataSets. Ficando ao critério do usuário forçar a exceção “EDatabaseError” ou não. Configure o novo botão da seguinte forma: Name = btnOpenCds; Caption = “Ativar Cds”; Top = 108; left =143; Width = 90.



procedure TZnPropagExceptFrm.BtnOpenCdsClick(Sender: TObject);
begin
(*O Método "CreateDataSet nos permite trabalhar com o ClienteDataSet
desconectado do banco de dados. "*)
cdsLog.CreateDataSet;
cdsDados.CreateDataSet;
end;

Vamos executar nosso segundo teste. Pressione F9, ative os datasets clickando no novo botão. Adicione alguns elementos. Lembre-se, se vc não adicionar a palavra “Exceção”, nenhuma exceção será levantada. Portanto, para testar o caso da exceção que não é propagada, adicione a palavra “exceçao”. E Clicke sobre o botão “Transfere Dados”.



Note na figura acima que o processo transferiu alguns elementos, mas quando encontrou a palvra “Exceçao”, foi interrompido e levantou a exceção. Contudo, essa exceção, não foi capturada pelo tratamento do botão “Transfere Dados”.
Verifique o codigo da função “SetDados”, veja que o que diferenciou um caso do outro foi a utilização da paralra reservada “raise” na estutura “on ..do” (que verificava se a exceção era do tipo “ZnException”). Quando vc trata uma exceção, a estrutura “try ... except” provê para vc um manipulador de exceções. Esse manipulador gerencia também o “tempo de existência” dessa exceção. Antes do final da estrutura a exceção “morre”, é desalocada. Assim sendo, se vc deseja que ela seja atribuida, passada, ao próximo manipulador, vc tem que usar a palavra reservadda “raise”.
Note Ainda que, assim que vc clickar no ok da mensagem da exceção, o processo continuará a execução após a estrutura “try ... except” da função “SetDados” e efetuará o log dos elementos transferidos para o “cdsDados”.



Artigo completo (View Full Post)

domingo, 22 de abril de 2007

Não tem cara de tiozão… Nissan Sentra ... Música

Momento lazer

A tv esta ligada no outro quarto ... a música do comercial do Nissan ...
Me a marro nela ... Pra distrair um pouco a mente resolvi documentar ...Hehhehehe, tem o solo e tudo.
A música “Será que é para mim?” da banda The Uncles é a trilha sonora do comercial do novo Nissan Sentra

Pra desempoerar o Sibelius: “Ctrl+K” para adicionar Cifra. O cursor vai ficar azul, click sobre o compasso para digitar a cifra desejada, exemplo: “Am7”.

Com o ‘space’ da espaço para adicionar um novo acorde, o ideal é que seja no mesmo compasso.



Não existe idade pra cair na tentação
Tanto que um belo dia algo chamou minha atenção
Um carro prateado, descolado, todo bonitão
Mas será que é pra mim algo tão moderno assim?
Não tem cara de tiozão, de tiozão ...

Não tem cara de tiozão
Mas acelerou meu coração
É tão maravilhoso que despertou minha paixão
Despertou minha paixão
Acelerou meu coração
É tão maravilhoso que despertou minha paixão

É diferente de tudo que eu já vi
Não é de tiozão, mas eu não to nem ai ...
Meu desejo é muito forte
Quero acelerar até o fim
Algo tão moderno assim pode ser pra mim

Não tem cara de tiozão
Mas acelerou meu coração
É tão maravilhoso que despertou minha paixão
Despertou minha paixão
Acelerou meu coração
É tão maravilhoso que despertou minha paixão

      Solo 
O carro que eu vi é impossível descrever
Parecia uma miragem, só de lembrar quero guiar
Quero poder voltar a ter um carrão e viver
Aquela emoção de acelerar o coração

Não tem cara de tiozão
Mas acelerou meu coração
É tão maravilhoso que despertou minha paixão
Despertou minha paixão
Acelerou meu coração
É tão maravilhoso que despertou minha paixão


O solo é uma oitava a cima. Não escrevi os bands ... escrevi uma idéia bem próximo do que o cara tocou.
Outra possibilidade tb, pressione “Ctrl+K”, o cursor vai ficar azul, click sobre o compasso com o botão direito, em seguida click com o botão esquerdo. Aparecerá um menu popup com várias opções de cifras.


Artigo completo (View Full Post)

Aplicações Robustas III: Criação de Exceções

Esse artigo é continuação do post “Robustez no Delphi

Dando continuidade a seqüência de artigos sobre tratamento de exceções.....

Com base em tudo o que foi falado nos artigos anteriores sobre exceções vamos tratar agora sobre como e por que criar suas próprias exceções. Entenda que não me refiro a levantar um exceção, quando uso o termo “criar” estou falando de definir uma classe nova, derivada diretamente de Exception, ou de um de seus herdeiros.

Criando suas próprias exceções:

Imagine uma situação em que a lógica do negócio envolvida no seu programa, defina certas regras que não podem ser quebradas. Você não vai encontrar nas bibliotecas do Delphi nenhuma exceção para o caso de um cliente ser cadastrado com valor para data de nascimento maior que a data corrente, por exemplo. Portanto, quando você precisa garantir que as regras pertinentes ao negócio sejam suportadas pelo seu sistema em forma de exceções é preciso para isso definir classes, tipos, de exceções específicas para isso.

Construindo um exemplo.


Para exemplificar, vamos propor um problema para em seguida implementarmos um pequeno programa, em cima desse problema, de maneira que possamos modelar as regrar contidas no domínio do problema em questão em forma de exceções.

O problema:
O programa que vamos contruir simula um pequeno aspecto da relação de uma farmácia e seus clientes. Vejamos, para compra de qualquer remédio controlado é exigido a apresentação, por parte do cliente, de uma receita válida para o remédio específico desejado. Em nosso exemplo vamos trabalhar com os seguintes atributos da receita: Data de validade e substância (remédio). Criaremos, portanto, duas classes de exceções: EDataInvalida (caso a receita esteja vencida), ESubstanciaErrada. Vamos arbitrariamente definir que a validade de uma receita seja de 4 dias. Já temos algumas regras, agora já podemos construir nosso pequeno programa.

Inicie uma nova aplicação no Delphi
: Menu, File ►New ► Application.

No Form1 adicione os seguintes componentes: Duas ComboBox (TComboBox), um Button (TButton), três Labels (TLabel) e um ListBox (TListBox), da palheta Standard; Um DateTimePicker1 (TDateTimePicker), da palheta Win32. Configure as propriedades dos componentes segundo listado abaixo:


Form1:
Width = 470
Height = 494
Caption = 'Criando Exceções'

Label1:
Left = 12
Top = 12
Caption = 'Receita'

Label2:
Left = 300
Top = 12
Caption = 'Remédio'

Label3:
Left = 12
Top = 58
Caption = 'Data da Receita'

ComboBox1:
Name = CmbReceita
Left = 12
Top = 29
Width = 280

ComboBox2:
Name = CmbRemedio
Left = 300
Top = 29
Width = 150

Button1: TButton
Left = 373
Top = 64
Width = 75
Height = 25
Caption = 'Efetuar venda'

ListBox1:
Name = LstVendas
Left = 12
Top = 104
Width = 445
Height = 353

DateTimePicker1:
Name = DtReceita
Left = 12
Top = 75
Width = 82


A seção type da unit1 deverá estar assim

type
TForm1 = class(TForm)
CmbReceita: TComboBox;
CmbRemedio: TComboBox;
Button1: TButton;
ListBox1: TListBox;
DtReceita: TDateTimePicker;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;


Logo abaixo da palavra reservada “type” vamos codificar a definição das nossas exceções. Faça conforme exemplo abaixo:



type
EDataInvalida = class(Exception);//Exceção para data inválida
ESubstanciaErrada = class(Exception); // Exceção de substância errada

TForm1 = class(TForm)
CmbReceita: TComboBox;
CmbRemedio: TComboBox;
Button1: TButton;
LstVendas: TListBox;
DtReceita: TDateTimePicker;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
private
procedure LoadReceitas;
procedure LoadRemedios;
procedure CriticarVenda;
{ após digitar as procedures a cima pressione: “Ctrl+Shift+C”}
public
{ Public declarations }
end;


Codificaremos primeiro o preenchimento da combo com os remédios.

procedure TForm1.LoadRemedios;
begin
(* Montando a combo dos remédios *)
with CmbRemedio do
begin
Items.Add('Frontal');
Items.Add('Tefxel');
Items.Add('Lorax');
Items.Add('Haldol');
Items.Add('Urbanil');
Items.Add('Tagretol');
end;
end;


Em seguida o preenchimento da combo das receitas. Para facilitarmos nosso exemplo vamos ordenar as receitas conforme a ordem dos remédios.


procedure TForm1.LoadReceitas;
const
DescReceita = 'Receita para o remédio: %s';
var
i: Integer;
begin
for i := 0 to CmbRemedio.Items.Count - 1) do
CmbReceita.Items.Add(Format(DescReceita, [CmbRemedio.Items[i]]))

end;


Agora, a rotina que gera as exeções, “CriticarVenda”.

procedure TForm1.CriticarVenda;
begin
(* Caso nehuma opção seja selecionada levantaremos uma exceção genérica*)
if (CmbReceita.ItemIndex < 0) and (CmbRemedio.ItemIndex < 0) then
raise Exception.Create('Selecione a receita, em seguida o remédio.');

(* Caso uma opção não seja selecionada levantaremos a exceção que criamos*)
if (CmbReceita.ItemIndex <= 0) xor (CmbRemedio.ItemIndex <= 0) then
raise ESubstanciaErrada.Create('A receita não corresponde ao remédio.');

(* Caso as opção selecionada sejam diferntes *)
if not (CmbReceita.ItemIndex = CmbRemedio.ItemIndex ) then
raise ESubstanciaErrada.Create('A receita não corresponde ao remédio.');

(* definimos, hipoteticamente, prazo de validade de 6 dias para a receita. *)
if DtReceita.Date < (Date - 6) then
raise EDataInvalida.Create('Prazo de validade da receita expirou.');
end;

No evento OnCreate do From1 faremos a chamada aos procedimentos que preencherão as Combos.

procedure TForm1.FormCreate(Sender: TObject);
begin
LoadRemedios;
LoadReceitas;
DtReceita.Date := Date - 10;
end;

Para finalizar, o evento OnClick do Button1, onde construiremos o tratamento das exceções.

procedure TForm1.Button1Click(Sender: TObject);
const
MsgSucesso = 'Venda efetuada com sucesso: %s';
begin
try
CriticarVenda;
LstVendas.Items.Add(Format(MsgSucesso, [CmbRemedio.Text]));
except
on E: EDataInvalida do
LstVendas.Items.Add(E.Message);
on E: ESubstanciaErrada do
begin
LstVendas.Items.Add(E.Message);
CmbRemedio.SetFocus;
if CmbReceita.ItemIndex < 0 then
begin
MessageBox(0, 'Selecione a receita.',
'Criando Exceções - Estação ZN', MB_ICONERROR or MB_OK);
CmbReceita.SetFocus;
end;
end;

on E: Exception do
begin
MessageBox(0, PChar(E.Message),
'Criando Exceções - Estação ZN', MB_ICONERROR or MB_OK);
CmbReceita.SetFocus;
end;
end;
end;

Veja as imagens abaixo:






Artigo completo (View Full Post)

 
BlogBlogs.Com.Br