Esse artigo faz parte da revista Clube Delphi edição 4. Clique aqui para ler todos os artigos desta edição



Atenção: por essa edição ser muito antiga não há arquivo PDF para download. Os artigos dessa edição estão disponíveis somente através do formato HTML. 

 

Dica

Os objetos TtreeView e TTreeNode

Uma das formas de exibição de dados utilizadas amplamente hoje em dia é a estrutura denominada Árvore, popularizada entre nós pelo Windows Explorer. Para a criação de uma árvore em aplicação que utiliza a estrutura de árvore, o Delphi disponibiliza a classe TTreeView.

Esta classe é de fácil utilização. Para carregar dados em um TreeView a partir de um banco de dados e recuperá-los, utilizamos os métodos AddObject e AddChildObject. Para isto é necessário utilizar um parâmetro do tipo untyped pointer que irá preencher a propriedade Data do objeto TTreeNode.

Irei mostrar neste artigo como construir uma aplicação que mostra os clientes e seus respectivos pedidos na arvore e ao clicar nas ramificações do pedido, um grid mostra os seus respectivos itens. No Delphi, crie uma nova aplicação e no form coloque um objeto TtreeView que se encontra na paleta Win32 e um objeto DBgrid que se encontra na paleta DataControls. O Banco de dados que iremos acessar terá três tabelas:Clientes, Pedidos e Itens e Produtos. Coloque quatro componentes Ttable que se encontram na paleta BDE e quatro objetos dataSource que se encontra na paleta DataAccess. Ligue os quatro objetos table com as tabelas e cada datasource. Veja o formulário em tempo de projeto na figura 1.

Figura 1: o exemplo em execução

Para criar a árvore, criaremos uma procedure que preencherá o nível zero da mesma. Não tente criar a árvore e todos os seus níveis de uma só vez, pois este processo se tornará demorado demais. O ideal é que os demais níveis sejam criados somente quando usuário clicar em um dos nós da árvore. Na seção Private do Form declare a seguinte procedure:

procedure Tform1.

PreencheArvore (Tabela:

TTable);

var

Pont: Tpont;

begin

while not Tabela.Eof do

begin

New (Pont):

Pont^ : = Tabela.

Fieldbyname (‘Codigo’)

.asInteger;

TV.Items.AddObject (nil,

Tabela.Fieldbyname

(‘nome’).asString, pont);

Tabela.Next;

end;

end;

Neste momento seu programa deverá realizar uma consulta ao banco de dados para facilitar quais registros ficarão neste ramo de sua árvore. Em nosso exemplos, duas tabelas criarão a árvore: CLIENTE e PEDIDOS. E duas outras serão utlizadas para o peencimento do Grid: PRODUTO e ITENS.

Repare que o relacionamento entre as tabelas foi implementado através das propriedades MasterSource e MasterField, sendo utilizada também a propriedade Filter para selecionar os pedidos de um determinado cliente.

O campo descrição pode ser atribuído para a propriedade tect do objeto TtreeNode, e a chave primária deste registro deve ficar guardada em uma variável dinâmica acessada através do ponteiro encontrado na propriedade Data do objeto.

O motivo de criarmos uma variável do tipo Pointer é muito simples: A necessidade de armazenar chaves primárias compostas.

Em nosso exemplo as tabelas utilizadas no TTreeView possuem uma chave primária simples, e por isso criaremos um ponteiro para números inteiros. Caso suas tabelas possuam chaves primárias compostas e com estruturas diferentes, você deverá criar um tipo record para cada chave e um ponteiro para cada tipo record.

A manipulação da propriedade Data é a mesma utilizada para qualquer ponteiro: você deve primeiramente criar uma variável dinâmica com o método New, preenchê-la, e atribuir o endereço desta variável à propriedade Data.

Figura2. o exemplo em templo de projeto

 

New (Pont):

Pont^ : = Tabela.

Fieldbyname (‘Codigo’)

.asInteger;

TV.Items.AddObject (nil,

Tabela.Fieldbyname

(‘nome’).asString,pont);

Para recuperar os dados apontados pela propriedade Data do TtreeNode você deve utilizar um recurso de programação conhecido como Type cast, ou seja você informa o tipo com o qual você quer ler aquele objeto. Porém, você deve observar que este recurso só será bem sucedido quando utilizado com tipos compatíveis. A sintaxe de um Typecast é a seguinte: Tipo (Objeto ou variável).

Crie sua precedure para leitura dos dados armazenados na propriedade data, siga o exemplo aqui exibido e apresente em sua aplicação uma interface com a qual o usuário já está muito familiarizado. Segue abaixo o código completo da aplicação:

unit UTree;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, Buttons, StdCtrls, DB, DBTables, Grids, DBGrids, ComCtrls, ExtCtrls;

type

TForm1 = class(TForm)

TV: TTreeView;

DBGrid1: TDBGrid;

TBClientes: TTable;

TBPedidos: TTable;

TBIten: TTable;

TBProduto: TTable;

DSClientes: TDataSource;

DSPedidos: TDataSource;

DSProduto: TDataSource;

DSIten: TDataSource;

Database1: TDatabase;

TBClientesCODIGO: TIntegerField;

TBClientesNOME: TStringField;

TBClientesENDERECO: TStringField;

TBPedidosCODIGO: TIntegerField;

TBPedidosDATA: TDateTimeField;

TBPedidosCODCLIENTE: TIntegerField;

procedure TVClick(Sender: TObject);

procedure DSClientesDataChange(Sender: TObject; Field: TField);

procedure FormShow(Sender: TObject);

procedure DSPedidosDataChange(Sender: TObject; Field: TField);

procedure FormClose(Sender: TObject; var Action: TCloseAction);

private

{ Private declarations }

public

procedure preenchearvore(Tabela:Ttable);

Procedure preenchegalho(Tabela:TTable);

{ Public declarations }

end;

TPont=^Integer;

var

Form1: TForm1;

implementation

{$R *.dfm}

Procedure Tform1.preenchearvore(Tabela:TTable);

var

Pont: TPont;

begin

while not Tabela.Eof do

begin

New (Pont);

Pont^ := Tabela.Fieldbyname ('Codigo').asInteger;

TV.Items.AddObject (nil,Tabela.Fieldbyname('nome').asString, pont);

Tabela.Next;

end;

end;

Procedure Tform1.preenchegalho(Tabela:TTable);

var

Pont:TPont;

begin

If Tv.Selected.haschildren then

exit;

while not tabela.eof do

begin

New(pont);

Pont^:=Tabela.Fieldbyname ('codigo').asInteger;

TV.Items.AddChildObject (TV.Selected,'Pedido: '+Tabela.Fieldbyname('codigo').asString+

' - '+Tabela.Fieldbyname ('data').asString,pont);

Tabela.Next;

end;

end; 


procedure TForm1.TVClick(Sender: TObject);

begin

If TV.Selected.Level < 1 then

If TBClientes.FindKey([Tpont(TV.Selected.Data)^]) then

preenchegalho(TBPedidos)

else

else

TBPedidos.FindKey([Tpont(TV.Selected.Data)^]);

end;

procedure TForm1.DSClientesDataChange(Sender: TObject; Field: TField);

begin

TBPedidos.Filter:='codcliente = '+ tbclientes.fieldbyname('codigo').AsString;

end;

procedure TForm1.FormShow(Sender: TObject);

begin

Tbclientes.open;

TBPedidos.Open;

TBIten.open;

TBProduto.open;

preenchearvore(TBClientes);

end;

procedure TForm1.DSPedidosDataChange(Sender: TObject; Field: TField);

begin

tbiten.Filter:='codpedido = '+ tbpedidos.Fieldbyname('codigo').asstring;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

Tbclientes.close;

TBPedidos.close;

TBIten.close;

TBProduto.close;

end;

end.