segunda-feira, 10 de dezembro de 2007

Recuperando informações sobre a versão e o Build do Executável

A função VerQueryValue retorna as informações correntes no “version-information” resorce. No Delphi, no menu “Project/Options” (Shif + Ctrl + F11), você define várias configurações sobre o seu projeto. As definições sobre a versão do seu programa é definida na aba “Version Info”, basta você marcar a opção para incluir informação sobre a versão no projeto (Includ version infomation in projetct).





Essa função deve trabalhar em conjunto com a GetFileInformationInfo, pois esta última é quem recupera propriamente o valor desejado do “version-information”.
Todas as duas funções são chamadas a API do sistema operacional Windows. No Delphi 7 elas estão definidas na unit “Windows”.

Analisado a Função:

function VerQueryValue; external version name ‘VerQueryValueA’;


function VerQueryValue(

const LPVOID pBlock, // Endereço do buffer para o “version resource”.
LPTSTR lpSubBlock, // Endereço para o valor a ser recuperado
LPVOID *lplpBuffer, // Ponteiro para o endereço do buffer onde esta armazenado as informações.
PUINT puLen // Endereço do versio-value length buffer
);

Parameters

pBlock

Points to the buffer containing the version-information resource
returned by GetFileVersionInfo.

lpSubBlock

Points to a zero-terminated string specifying which version-information value to
retrieve. The string consists of names separated by backslashes (\) and can have
one of the following forms:

Form Description
\ Specifies the root block. The function retrieves a pointer to the
VS_FIXEDFILEINFO structure for the version-information resource.
\VarFileInfo\Translation Specifies the translation table in the
variable information structure. The function retrieves a pointer to an
array of language and character-set identifiers. An application uses
these identifiers to create the name of a language-specific structure in
the version-information resource.
\StringFileInfo\lang-charset\string-name
Specifies a value in a language-specific structure.
The lang-charset name is a concatenation of a language and character-set
identifier pair found in the translation table for the resource.
The lang-charset name must be specified as a hexadecimal string.
The string-name name is one of the predefined strings described in the
following Remarks section.


lplpBuffer

Points to a buffer that receives a pointer to the version-information value.

puLen

Points to a buffer that receives the length, in characters, of the
version-information value. (retirado de Win32 Programming Techniques)

Existem em vários exemplos sobre o uso dessa função na web, basta “googar” buscando bor “VerQueryValue” e retornará uma série de links. Vou colocar um exemplo simples aqui somente a título de documentação:

Adicione num Form dois componentes “TLabel” (Label1 e Label2)e um TListBox (ListBox1). No Evento "OnCreate" do From1 digite como exemplificado abaixo:


procedure TForm1.FormCreate(Sender: TObject);
const
AExa = '041604E4';
AFileInfo = '\StringFileInfo\';
var
ApplicationExeName: String;
ReservedSpace, VLength: Cardinal;
MyBuff: PChar;
VersionBuild, CommentBuild: String;
begin
ApplicationExeName := Application.ExeName;
ListBox1.Items.Add(ApplicationExeName);

ReservedSpace := GetFileVersionInfoSize(PChar(ApplicationExeName),
ReservedSpace);

ListBox1.Items.Add(Format(ApplicationExeName + '- Tamanho Alocado = %d',
[ReservedSpace]));

if (ReservedSpace > 0) then
begin
MyBuff := AllocMem(ReservedSpace);
GetFileVersionInfo(PChar(ApplicationExeName),0, ReservedSpace, MyBuff);
VerQueryValue(MyBuff, PChar(AFileInfo + AExa+ '\FileVersion'),
Pointer(VersionBuild), VLength);
Label1.Caption := VersionBuild;

VerQueryValue(MyBuff, PChar(AFileInfo + AExa+ '\Comments'),
Pointer(CommentBuild), VLength);
Label2.Caption := CommentBuild;

end;
end;


Antes de executar o programa no menu “Project” ▶ Options” (Alt, P, O), na aba “Version Info”.no Grid de valores chaves, na linha Comments adicione um comentário. Conforme exemplificado abaixo.



Execute o programa e teste ...



Artigo completo (View Full Post)

sábado, 8 de dezembro de 2007

The Police no Maracanã

A ficha ainda não caiu ....

Já estou aquecendo os motores, vamos botar pra jogo pra ver o que rola.

Segue, um idéia de como o bagulho funfa


Message in a Bottle





De quebra ainda vai ter Paralamas!!!




Artigo completo (View Full Post)

Construção de Componentes V – Parte B

Gerenciador Dinâmico de Layout para DBGrids

Daremos continuidade ao artigo sobre a construção de um componente que permite ao usuário configurar várias propriedades de um DBGrid em tempo de execução. Anteriormente, desenvolvemos de um pequeno exemplo da utilização do commponetne, pudemos experimentar a facilidade de usabilidade, tanto do ponto de vista do usuário, quanto do desenvolvedor. Também comentamos as vantagens obtidas decorrente da abordagem de componentização em projetos de software.

Neste artigo, trataremos da funcionalidade de retornar a visibilidade as colunas que o usuário desejou torna-las invisíveis. Recapitulando:

Fluxo alternativo B

1 – Usuário, com o botão direto do mouse, sobre o DBGrid, seleciona
Ver Colunas Escondidas”.

2 - Gerenciador de Layout de Grid exibe interface listando apenas as colunas
do DGGrid, as quais foram, pelo usuário, definidas como invisíveis
previamente. A partir das configurações originais, definidas em tempo de
projeto pelo programador, o Gerenciador de Layout de Grid deverá identificar
quais foram originalmente definidas como invisíveis para nunca permitir que
estas sejam listadas nesta interface.

3 – Usuário seleciona dentre as colunas listadas a que deseja tornar visível.
Confirmando em seguida a operação.

4 - Gerenciador de Layout de Grid grava as alterações e re-carrega o grid
com o que foi modificado.


Form SetupVisibilityColumnFrm:

Adicione ao pakage mais um TForm e manipule as propriedades do mesmo segundo listagem abaixo:

Name = SetupVisibilityColumnFrm( :TSetupVisibilityColumnFrm)
Left = 526
Top = 266
Caption = 'Visibilidade das Colunas'
ClientHeight = 560
ClientWidth = 495

Salve a unit como “SetupVisibilityColumnForm”.

Adicione ao form “SetupVisibilityColumnFrm” os componentes listados abaixo, obedecendo os valores respectivamente defindos na listagem:





1 - Name = PnlBtn (:TPanel)
Align = alBottom
BevelOuter = bvNone

Dentro do PnlBtn

1.1 Name = BtnSave: TBitBtn
Left = 12
Top = 12
Width = 75
Height = 25
TabOrder = 0
Kind = bkOK

1.2 Name = btnCancel( :TBitBtn)
Left = 392
Top = 12
Width = 75
Height = 25
TabOrder = 1
Kind = bkCancel

2 – Name = AdsVisibilityColumn ( :TADODataSet)
CursorType = ctStatic
LockType = ltBatchOptimistic

Adicione os TFields abaixo ao AdsVisibilityColumn

2.1 Name = AdsVisibilityColumnTitleColumn (:TStringField)
DisplayLabel = 'Título da Coluna'
FieldName = 'TitleColumn'
Size = 80

2.2 NAme = AdsVisibilityColumnVisibleColumn (:TBooleanField)
DisplayLabel = 'Visível'
FieldName = 'VisibleColumn'

2.3 Name = AdsVisibilityColumnFieldName (:TStringField)
FieldName = 'FieldName'
Size = 90

3 – Name = AdsProFileClone (:TADODataSet)
LockType = ltBatchOptimistic

4 – Name = dsDados (:TDataSource)
DataSet = AdsVisibilityColumn



5 - Name = GrdConfigColumn (:TDBGrid)
Align = alClient
DataSource = dsDados
Uma importate configuração a ser feita para nosso exemplo funcionar, é setar a
propriedade dgEditing para “false” nas opções do GrdConfigColumn.
Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs,
dgConfirmDelete, dgCancelOnExit]
ReadOnly = True
TabOrder = 1

Adicione duas colunas ao GrdConfigColumn:

5. 1 Coluna [0]
FieldName = 'TitleColumn'
Width = 300
Visible = True

5. 2 Coluna [1]
FieldName = 'VisibleColumn'
Width = 52
Visible = True


Observe que um dos fields do dataset associado ao Grid é bolleano, portanto vamos manipular o valor deles através de um check box. Um check box por linha do Grid. A princípio vamos criar estes checkboxes no muque, via código. Eu não gosto disso, detesto código extra, mas com o intuito de tornar nosso conteúdo mais acessível vou fazer assim por enquanto.

Antes de prosseguir apague a instância da classe do form, SetupVisibilityColumnFrm, criada automaticamante pelo Delphi. Ela deverá estar bem abaixo da primeira palavra reservada “var” na unit do form que criamos. Apague, tanto a palavra reservada “var”, quanto a variável “SetupVisibilityColumnFrm”.

Codificando os eventos do “GrdConfigColumn”

1) OnCellClick = GrdConfigColumnCellClick

procedure TSetupVisibilityColumnFrm.GrdConfigColumnCellClick(
Column: TColumn);
begin
if GrdConfigColumn.SelectedField.DataType = ftBoolean then
SaveBoolean;
end;

Não compile ainda, falta codificarmos o “SaveBoolean”:
1.1 Método privado SaveBoolean:

procedure TSetupVisibilityColumnFrm.SaveBoolean;
begin
with GrdConfigColumn do
begin
SelectedField.DataSet.Edit;
SelectedField.AsBoolean := not SelectedField.AsBoolean;
SelectedField.Dataset.Post;
end;
end;



Neste ponto a sua unit “.pas” deve estar semelhante ao trecho abaixo:

unit SetupVisibilityColumnForm;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, ADODB, StdCtrls, Buttons, ExtCtrls, Grids, DBGrids;

TSetupVisibilityColumnFrm = class(TForm)
GrdConfigColumn: TDBGrid;
PnlBtn: TPanel;
BtnSave: TBitBtn;
btnCancel: TBitBtn;
AdsProFileClone: TADODataSet;
dsDados: TDataSource;
AdsVisibilityColumn: TADODataSet;
AdsVisibilityColumnTitleColumn: TStringField;
AdsVisibilityColumnVisibleColumn: TBooleanField;
AdsVisibilityColumnFieldName: TStringField;
procedure GrdConfigColumnCellClick(Column: TColumn);
private
procedure SaveBoolean;
public
{ Public declarations }
end;

(*apagamos a declaração da instância bem aqui ....*)

implementation

{$R *.dfm}

procedure TSetupVisibilityColumnFrm.GrdConfigColumnCellClick(Column: TColumn);
begin
if GrdConfigColumn.SelectedField.DataType = ftBoolean then
SaveBoolean;
end;

procedure TSetupVisibilityColumnFrm.SaveBoolean;
begin
with GrdConfigColumn do
begin
SelectedField.DataSet.Edit;
SelectedField.AsBoolean := not SelectedField.AsBoolean;
SelectedField.Dataset.Post;
end;
end;

end.



2) Evento OnDrawColumnCell do Grid (GrdConfigColumn): GrdConfigColumnDrawColumnCell


procedure TSetupVisibilityColumnFrm.GrdConfigColumnDrawColumnCell(
Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
Const
CtrlState : array[Boolean] of Integer = (DFCS_BUTTONCHECK,
DFCS_BUTTONCHECK or DFCS_CHECKED);
var
CheckBoxRectangle : TRect;
begin
if Column.Field.DataType = ftBoolean then
begin
GrdConfigColumn.Canvas.FillRect(Rect);
CheckBoxRectangle.Left := Rect.Left + 2;
CheckBoxRectangle.Right := Rect.Right - 2;
CheckBoxRectangle.Top := Rect.Top + 2;
CheckBoxRectangle.Bottom := Rect.Bottom - 2;
DrawFrameControl(GrdConfigColumn.Canvas.Handle, CheckBoxRectangle,
DFC_BUTTON, CtrlState[Column.Field.AsBoolean]);
end;

end;


3) Criando Propriedades no From

Na seção “public” contruiremos o meio de comunicação deste módulo com a classe TMangerGridZn. Primeiro as propriedades, digite conforme o trecho abaixo:


public
property FieldEnabledVisibilityName: String
read FFieldEnabledVisibilityName write FFieldEnabledVisibilityName;
property TitleColumnFieldName: String
read FTitleColumnFieldName write FTitleColumnFieldName;
property VisibleFieldName: String
read FVisibleFieldName write FVisibleFieldName;
property FieldNameFieldName: String
read FFieldNameFieldName write FFieldNameFieldName;
property FieldNameGridName: String
read FFieldNameGridName write FFieldNameGridName;
end;


Use o class complete (Ctrl + shift + c) para autocampletar e criar os campos privados referentes a cada propriedade na classe TSetupVisibilityColumnFrm.

4) Recuperando as colunas do DBGrid associado a classe TManagerGridZn que previamente foram definadas como invisíveis pelo usuário. Estas colunas deverão ser listadas na inbterface para que o usuário possa selecionar qual delas desejará tornar visível.

Método privado “LoadAds”:
Este método Carrega o dataset que permitirá ao usuário visualizar as colunas que ele definiu como invisíveis. Podendo então, torná-las visíveis.

function TSetupVisibilityColumnFrm.LoadAds(AdsDadosOriginais,
AdsProfileGrid: TADODataSet): Boolean;
var
AuxValues: String;
AuxFieldsLocate: String;
begin
(* Retorna verdadeiro caso existam colunas a serem listadas.
AdsDadosOriginais:
armazena as cofigurações definidas em design time
pelo desenvolvedor no DBGrid associado a TManagerGridZn
AdsProfileGrid:
armazena as cofigurações definidas em run time
pelo usuário no DBGrid associado a TManagerGridZn*)

(* Este método Carrega o dataset que permitirá ao usuário visualizar as
colunas que ele definiu como invisíveis. Podendo então, torná-las visíveis. *)

Result := AdsVisibilityColumn.Active;
if not Result then Exit;

(* Limpando possível lixo*)
if not AdsVisibilityColumn.IsEmpty then
begin
AdsVisibilityColumn.First;
while not AdsVisibilityColumn.Eof do
AdsVisibilityColumn.Delete;
end;
AdsDadosOriginais.First;
while not (AdsDadosOriginais.Eof) do
begin

(* O campo FFieldEnabledVisibilityName corresponde ao informação original
do grid, se o desenvolvedor permitiu visibilidade ao usuário, ou não. *)
if AdsDadosOriginais.FieldByName(FFieldEnabledVisibilityName).AsBoolean then
begin
(* recuperando valores para o locate *)
AuxValues := AdsDadosOriginais.FieldByName(FFieldNameFieldName).AsString;

AuxFieldsLocate := FFieldNameFieldName;

if AdsProfileGrid.Locate(AuxFieldsLocate, AuxValues, []) then
begin
if not AdsProfileGrid.FieldByName(FVisibleFieldName).AsBoolean then
begin
AdsVisibilityColumn.Append;
AdsVisibilityColumnTitleColumn.AsString :=
AdsProfileGrid.FieldByName(FTitleColumnFieldName).AsString;
AdsVisibilityColumnVisibleColumn.AsBoolean :=
AdsProfileGrid.FieldByName(FVisibleFieldName).AsBoolean;
AdsVisibilityColumnFieldName.AsString :=
AdsProfileGrid.FieldByName(FFieldNameFieldName).AsString;
AdsVisibilityColumn.Post;
end;
end;
end;

AdsDadosOriginais.Next;
end;

Result := not AdsVisibilityColumn.IsEmpty;
end;



5 – Retornando as alterações efetuadas pelo usuário a partir do que foi listado na iterface:
Este método já é o output deste módulo, após implementa-lo (segundo trecho abaixo) cuidaremos do principal método de comunicação do nosso form.


procedure TSetupVisibilityColumnFrm.SetUpdates(AdsProfileGrid: TADODataSet);
begin
try
AdsVisibilityColumn.DisableControls;
AdsVisibilityColumn.First;
while not AdsVisibilityColumn.Eof do
begin
AdsProfileGrid.Filter := FFieldNameFieldName + ' = ' +
QuotedStr(AdsVisibilityColumnFieldName.AsString);
AdsProfileGrid.Filtered := True;
AdsProfileGrid.First;
while not AdsProfileGrid.Eof do
begin
AdsProfileGrid.Edit;
AdsProfileGrid.FieldByName(FVisibleFieldName).AsBoolean :=
AdsVisibilityColumnVisibleColumn.AsBoolean;
AdsProfileGrid.Post;
AdsProfileGrid.Next;
end;
AdsVisibilityColumn.Next;
end;
finally
AdsProfileGrid.Filter := '';
AdsProfileGrid.Filtered := False;
AdsVisibilityColumn.EnableControls;
end;
end;



6 – I/O do módulo TSetupVisibilityColumnFrm:
O método público “Execute” é o meio pelo qual implementaremos a lógica de entrada e saída do módulo que acabamos de construir.

function TSetupVisibilityColumnFrm.Execute(AdsSetupOriginal,
AdsProfileGrid: TADODataSet): Boolean;
begin
if FFieldEnabledVisibilityName = '' then
raise Exception.Create('O Campo FieldEnabledVisibilityName não foi atribuido.');
(* Connfigurando exibição do Form *)
Self.BorderStyle := bsDialog;
Self.Position := poScreenCenter;
Self.FormStyle := fsNormal;

(* Carrega o Dataset com as colunas que estão invisíveis *)
Result := LoadAds(AdsSetupOriginal, AdsProfileGrid);
(* Caso nenhum regitro de coluna invisível tenha sido carregado
cancela a execução do módulo, garantido que a propriedade "ModalResult"
do form seja igual a mrCancel *)
if not Result then
btnCancel.Click;
(* Copia os dados do Dataset que contem as configurações do profile*)
AdsProFileClone.Clone(AdsProfileGrid);
Result := (Self.ShowModal = mrOk);
if Result then
SetUpdates(AdsProfileGrid);

end;


7 – BtnOk OnClikc:
O BtnOk esta configurado como ModalResult = mrOk desta forma podemos recuperar ao término da execução do método ShowModal, se o usuário confirmou ou não a operação realizada.


procedure TSetupVisibilityColumnFrm.BtnSaveClick(Sender: TObject);
begin
if dsDados.DataSet.State in [dsEdit, dsInsert] then
dsDados.DataSet.Post;
end;


8 - BtnCancel OnClikc:
Da mesma forma, posso testar a se o usuário cancelou o processo porque o BtnCancel tem a propriedade ModalResult = mrCancel.


procedure TSetupVisibilityColumnFrm.btnCancelClick(Sender: TObject);
begin
if dsDados.DataSet.State in [dsEdit, dsInsert] then
dsDados.DataSet.Cancel;
end;


9 - Ainda falta ativar o dataset que armazenará os dados sobre as colunas que serão listadas na interface (SetupVisibilityColumnFrm). Evento OnCreate do form:

procedure TSetupVisibilityColumnFrm.FormCreate(Sender: TObject);
begin
AdsVisibilityColumn.CreateDataSet;
end;


Neste ponto finalizamos a implementação da funcionalidade pretendida neste artigo. Resta agora retornarmos a classe TManagerGridZn para codificarmos a chamada a este módulo. Por garantia segue o código completo das units contruidas até agora.

10 – Fontes Completos

10.1 SetupVisibilityColumnForm.pas


unit SetupVisibilityColumnForm;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, ADODB, StdCtrls, Buttons, ExtCtrls, Grids, DBGrids;

type
TSetupVisibilityColumnFrm = class(TForm)
GrdConfigColumn: TDBGrid;
PnlBtn: TPanel;
BtnSave: TBitBtn;
btnCancel: TBitBtn;
AdsProFileClone: TADODataSet;
dsDados: TDataSource;
AdsVisibilityColumn: TADODataSet;
AdsVisibilityColumnTitleColumn: TStringField;
AdsVisibilityColumnVisibleColumn: TBooleanField;
AdsVisibilityColumnFieldName: TStringField;
procedure FormCreate(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure BtnSaveClick(Sender: TObject);
procedure GrdConfigColumnDrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
procedure GrdConfigColumnCellClick(Column: TColumn);
private
FFieldNameGridName: String;
FFieldEnabledVisibilityName: String;
FTitleColumnFieldName: String;
FFieldNameFieldName: String;
FVisibleFieldName: String;
procedure SaveBoolean;
function LoadAds(AdsDadosOriginais, AdsProfileGrid: TADODataSet): Boolean;
procedure SetUpdates(AdsProfileGrid: TADODataSet);
public
function Execute(AdsSetupOriginal, AdsProfileGrid: TADODataSet): Boolean;

property FieldEnabledVisibilityName: String
read FFieldEnabledVisibilityName write FFieldEnabledVisibilityName;
property TitleColumnFieldName: String
read FTitleColumnFieldName write FTitleColumnFieldName;
property VisibleFieldName: String
read FVisibleFieldName write FVisibleFieldName;
property FieldNameFieldName: String
read FFieldNameFieldName write FFieldNameFieldName;
property FieldNameGridName: String
read FFieldNameGridName write FFieldNameGridName;
end;


implementation

{$R *.dfm}

procedure TSetupVisibilityColumnFrm.btnCancelClick(Sender: TObject);
begin
if dsDados.DataSet.State in [dsEdit, dsInsert] then
dsDados.DataSet.Cancel;
end;

procedure TSetupVisibilityColumnFrm.BtnSaveClick(Sender: TObject);
begin
if dsDados.DataSet.State in [dsEdit, dsInsert] then
dsDados.DataSet.Post;
end;

function TSetupVisibilityColumnFrm.Execute(AdsSetupOriginal,
AdsProfileGrid: TADODataSet): Boolean;
begin
if FFieldEnabledVisibilityName = '' then
raise Exception.Create('O Campo FieldEnabledVisibilityName não foi atribuido.');
(* Connfigurando exibição do Form *)
Self.BorderStyle := bsDialog;
Self.Position := poScreenCenter;
Self.FormStyle := fsNormal;

(* Carrega o Dataset com as colunas que estão invisíveis *)
Result := LoadAds(AdsSetupOriginal, AdsProfileGrid);
(* Caso nenhum regitro de coluna invisível tenha sido carregado
cancela a execução do módulo, garantido que a propriedade "ModalResult"
do form seja igual a mrCancel *)
if not Result then
btnCancel.Click;
(* Copia os dados do Dataset que contem as configurações do profile*)
AdsProFileClone.Clone(AdsProfileGrid);
Result := (Self.ShowModal = mrOk);
if Result then
SetUpdates(AdsProfileGrid);

end;

procedure TSetupVisibilityColumnFrm.FormCreate(Sender: TObject);
begin
AdsVisibilityColumn.CreateDataSet;
end;

procedure TSetupVisibilityColumnFrm.GrdConfigColumnCellClick(Column: TColumn);
begin
if GrdConfigColumn.SelectedField.DataType = ftBoolean then
SaveBoolean;
end;

procedure TSetupVisibilityColumnFrm.GrdConfigColumnDrawColumnCell(
Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
Const
CtrlState : array[Boolean] of Integer = (DFCS_BUTTONCHECK,
DFCS_BUTTONCHECK or DFCS_CHECKED);
var
CheckBoxRectangle : TRect;
begin
if Column.Field.DataType = ftBoolean then
begin
GrdConfigColumn.Canvas.FillRect(Rect);
CheckBoxRectangle.Left := Rect.Left + 2;
CheckBoxRectangle.Right := Rect.Right - 2;
CheckBoxRectangle.Top := Rect.Top + 2;
CheckBoxRectangle.Bottom := Rect.Bottom - 2;
DrawFrameControl(GrdConfigColumn.Canvas.Handle, CheckBoxRectangle,
DFC_BUTTON, CtrlState[Column.Field.AsBoolean]);
end;

end;

function TSetupVisibilityColumnFrm.LoadAds(AdsDadosOriginais,
AdsProfileGrid: TADODataSet): Boolean;
var
AuxValues: String;
AuxFieldsLocate: String;
begin
(* Retorna verdadeiro caso existam colunas a serem listadas.
AdsDadosOriginais:
armazena as cofigurações definidas em design time
pelo desenvolvedor no DBGrid associado a TManagerGridZn
AdsProfileGrid:
armazena as cofigurações definidas em run time
pelo usuário no DBGrid associado a TManagerGridZn*)

(* Este método Carrega o dataset que permitirá ao usuário visualizar as
colunas que ele definiu como invisíveis. Podendo então, torná-las visíveis. *)

Result := AdsVisibilityColumn.Active;
if not Result then Exit;

(* Limpando possível lixo*)
if not AdsVisibilityColumn.IsEmpty then
begin
AdsVisibilityColumn.First;
while not AdsVisibilityColumn.Eof do
AdsVisibilityColumn.Delete;
end;
AdsDadosOriginais.First;
while not (AdsDadosOriginais.Eof) do
begin

(* O campo FFieldEnabledVisibilityName corresponde ao informação original
do grid, se o desenvolvedor permitiu visibilidade ao usuário, ou não. *)
if AdsDadosOriginais.FieldByName(FFieldEnabledVisibilityName).AsBoolean then
begin
(* recuperando valores para o locate *)
AuxValues := AdsDadosOriginais.FieldByName(FFieldNameFieldName).AsString;

AuxFieldsLocate := FFieldNameFieldName;

if AdsProfileGrid.Locate(AuxFieldsLocate, AuxValues, []) then
begin
if not AdsProfileGrid.FieldByName(FVisibleFieldName).AsBoolean then
begin
AdsVisibilityColumn.Append;
AdsVisibilityColumnTitleColumn.AsString :=
AdsProfileGrid.FieldByName(FTitleColumnFieldName).AsString;
AdsVisibilityColumnVisibleColumn.AsBoolean :=
AdsProfileGrid.FieldByName(FVisibleFieldName).AsBoolean;
AdsVisibilityColumnFieldName.AsString :=
AdsProfileGrid.FieldByName(FFieldNameFieldName).AsString;
AdsVisibilityColumn.Post;
end;
end;
end;

AdsDadosOriginais.Next;
end;

Result := not AdsVisibilityColumn.IsEmpty;
end;

procedure TSetupVisibilityColumnFrm.SaveBoolean;
begin
with GrdConfigColumn do
begin
SelectedField.DataSet.Edit;
SelectedField.AsBoolean := not SelectedField.AsBoolean;
SelectedField.Dataset.Post;
end;
end;

procedure TSetupVisibilityColumnFrm.SetUpdates(AdsProfileGrid: TADODataSet);
begin
try
AdsVisibilityColumn.DisableControls;
AdsVisibilityColumn.First;
while not AdsVisibilityColumn.Eof do
begin
AdsProfileGrid.Filter :=
FFieldNameFieldName + ' = ' + QuotedStr(AdsVisibilityColumnFieldName.AsString);
AdsProfileGrid.Filtered := True;
AdsProfileGrid.First;
while not AdsProfileGrid.Eof do
begin
AdsProfileGrid.Edit;
AdsProfileGrid.FieldByName(FVisibleFieldName).AsBoolean :=
AdsVisibilityColumnVisibleColumn.AsBoolean;
AdsProfileGrid.Post;
AdsProfileGrid.Next;
end;
AdsVisibilityColumn.Next;
end;
finally
AdsProfileGrid.Filter := '';
AdsProfileGrid.Filtered := False;
AdsVisibilityColumn.EnableControls;
end;
end;

end.


10.2SetupVisibilityColumnForm.dfm


object SetupVisibilityColumnFrm: TSetupVisibilityColumnFrm
Left = 526
Top = 266
Caption = 'Visibilidade das Colunas'
ClientHeight = 560
ClientWidth = 495
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object GrdConfigColumn: TDBGrid
Left = 0
Top = 0
Width = 495
Height = 509
Align = alClient
DataSource = dsDados
Options = [dgTitles, dgIndicator, dgColumnResize,
dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit]
ReadOnly = True
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
OnCellClick = GrdConfigColumnCellClick
OnDrawColumnCell = GrdConfigColumnDrawColumnCell
Columns = < expanded =" False" fieldname =" 'TitleColumn'"
charset =" DEFAULT_CHARSET" color =" clWindowText" height =" -11"
name =" 'MS" style =" [fsBold]" width =" 300" visible =" True"
expanded =" False" fieldname =" 'VisibleColumn'" alignment =" taCenter"
charset =" DEFAULT_CHARSET" color =" clWindowText" height =" -11"
name =" 'MS" style =" [fsBold]" width =" 52" visible =" True">
end
object PnlBtn: TPanel
Left = 0
Top = 509
Width = 495
Height = 51
Align = alBottom
BevelOuter = bvNone
TabOrder = 1
ExplicitTop = 482
ExplicitWidth = 487
object BtnSave: TBitBtn
Left = 12
Top = 12
Width = 75
Height = 25
TabOrder = 0
OnClick = BtnSaveClick
Kind = bkOK
end
object btnCancel: TBitBtn
Left = 392
Top = 12
Width = 75
Height = 25
TabOrder = 1
OnClick = btnCancelClick
Kind = bkCancel
end
end
object AdsProFileClone: TADODataSet
LockType = ltBatchOptimistic
Parameters = <>
Left = 96
Top = 204
end
object dsDados: TDataSource
DataSet = AdsVisibilityColumn
Left = 132
Top = 68
end
object AdsVisibilityColumn: TADODataSet
CursorType = ctStatic
LockType = ltBatchOptimistic
FieldDefs = <>
Parameters = <>
StoreDefs = True
Left = 212
Top = 68
object AdsVisibilityColumnTitleColumn: TStringField
DisplayLabel = 'Título da Coluna'
FieldName = 'TitleColumn'
Size = 80
end
object AdsVisibilityColumnVisibleColumn: TBooleanField
DisplayLabel = 'Visí'vel'
FieldName = 'VisibleColumn'
end
object AdsVisibilityColumnFieldName: TStringField
FieldName = 'FieldName'
Size = 90
end
end
end


11 - Na classe TManagerGridZn codificaremos a chamada ao form “TSetupVisibilityColumnFrm”. Antes, declare na seção “uses” da “implementatio” a unit do módulo que criamos acima.




procedure TManagerGridZn.ConfigurarGridViewColumnLayoutClick(Sender: TObject);
var
FrmVisibilityConfig: TSetupVisibilityColumnFrm;
AuxPath: String;
AdsOrigConfigMemmento: TADODataSet;
begin
try
AdsOrigConfigMemmento :=
DmMangerGridZn.GetDefaultConfiguration(FGrid.Name, FGrid.Owner.Name);
FrmVisibilityConfig := TSetupVisibilityColumnFrm.Create(Self);
FrmVisibilityConfig.FieldEnabledVisibilityName :=
DmMangerGridZn.AdsOriginalConfigUpdateVisibility.FieldName;
FrmVisibilityConfig.TitleColumnFieldName :=
DmMangerGridZn.AdsOriginalConfigColumnTitleCaption.FieldName;
FrmVisibilityConfig.VisibleFieldName :=
DmMangerGridZn.AdsOriginalConfigColumnVisible.FieldName;
FrmVisibilityConfig.FieldNameFieldName :=
DmMangerGridZn.AdsOriginalConfigColumnFieldName.FieldName;
FrmVisibilityConfig.FieldNameGridName :=
DmMangerGridZn.AdsOriginalConfigGridName.FieldName;

if FrmVisibilityConfig.Execute(AdsOrigConfigMemmento,
DmMangerGridZn.AdsDados) then
begin
AuxPath := BuildFilePathComplete;
DmMangerGridZn.SaveToFile(AuxPath);
end;
finally
FrmVisibilityConfig.Free;
AdsOrigConfigMemmento.Close;
FreeAndNil(AdsOrigConfigMemmento);
LoadFileProfile;
end;

end;

O próximo passo é retornar a método que monta o menu popup para acrescentar a chamada ao método “ConfigurarLayoutGridClick” ao item de menu específico.


procedure TManagerGridZn.BuildMenuPopup;
var
i: Integer;
MyMenuitem: TMenuItem;
begin
(* Verifco se o componente encontra-se em run time*)
if not (csDesigning in Self.ComponentState) then
begin
if Assigned(FPopupMenu) then
begin
for i := 0 to High(PopupMenuItemName) do
begin
if (FPopupMenu.Items.Count > 0) and (i = 0) then
CreatePopupMenuItem(PopupMenuItemName[i], '-');

if (i = 1) then
CreatePopupMenuItem(PopupMenuItemName[i], 'Configurar Layuot do &Grid');

if (i = 2) then
CreatePopupMenuItem(PopupMenuItemName[i], '&Ver Colunas Invisíveis');

if (i = 3) then
CreatePopupMenuItem(PopupMenuItemName[i],
'&Restaurar Configuração Orignal');
end;

MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[1]));
if assigned(MyMenuitem) then
MyMenuitem.OnClick := ConfigurarLayoutGridClick;
(* A linha comentada com "//" será implementada mais tarde *)
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[2]));
if assigned(MyMenuitem) then
MyMenuitem.OnClick := ConfigurarGridViewColumnLayoutClick;

end;
end;

end;


Por hora chega!!!! Recompile a package e execute o programa de teste que fizemos no artigo anterior. Teste o componente e experimente a nova funcionalidade. O Embreve teminaremos, espero, a funcionalidade para restaurar as configurações default.

Artigo completo (View Full Post)

terça-feira, 20 de novembro de 2007

Over Kill (Men at Work)


Momento anos 80 …
Bem, o tom da gravação original foi em Mi maior. A versão violão e voz do Colin Hay foi um tom abixo (Ré maior), também houve uma mudança sensível na harmonia nas partes referentes aos solos.

Over Kill (Men at Work)



Introdução: || C#m7 | C#m7 | Bsus4 | Bsus4 ||

E B/D#
I can’t get sleep
D A/C#
I think about the implications
E B/D#
Of diving in to deep
D A/C#
And possibly the complications

E B/D#
Especially at night
D A/C#
I worry over situations
E B/D#
I know will be alright
D A/C#
Perhaps it’s just my imagination

C#m7 Bsus4 B7
Day after day reappears
C#m7 Bsus4 B7
Night after night my heartbeats
G#sus4 G#7
Shows the fear
A7M G#sus4 G#7 A7M
Ghosts appear and fade away


E B/D#
Alone between the sheets
D A/C#
Only brings exasperation
E B/D#
It’s time to walk the streets
D A/C#
Smell the desperation

E B/D#
At least there’s pretty lights
D A/C#
And though there’s little variation
E B/D#
It nullifies the night
D A/C#
From over kill

C#m7 Bsus4 B7
Day after day reappears
C#m7 Bsus4 B7
Night after night my heartbeats
G#sus4 G#7
Shows the fear
A7M G#sus4 G#7 A7M
Ghosts appear and fade away

Solos

I can’t get sleep
I think about the implications
Of diving in to deep
And possibly the complications

Especially at night
I worry over situations
I know will be alright
It’s just over kill

C#m7 Bsus4 B7
Day after day reappears
C#m7 Bsus4 B7
Night after night my heartbeats
G#sus4 G#7
Shows the fear
A7M G#sus4 G#7 A7M
Ghosts appear and fade away….

A7M G#sus4 G#7 A7M
Ghosts appear and fade away





Artigo completo (View Full Post)

Construção de Componentes V – Parte A

No artigo anterior começamos a construir o componente Gerenciador de Layout de Grid, TManagerGridZn. Vimos como fazer uma package no Delphi 2006, associar outras units a ela e etc. Como fazer isso no Delphi 7 também já foi exemplificado nos primeiros artigos sobre o assunto. Para programarmos um pequeno módulo a fim de testarmos o novo componente precisamos instalar a package, para fazer isso basta clickar com o botão direto sobre ela, no Project Manager e selecionar a opção “Install”.



Projeto teste Gerenciador de Layout de Grid (TesteGerencLayoutGrid.bdsproj): Inicie uma nova aplicação no Delphi, no From1 adicione os seguintes componentes:
1 – TClientDataSet
Name = CdsDados

1.1 Adicione dois TFields ao CdsDados;

TIntegerField
Name = CdsDadosID_Dado
Field Kind = fkData

TStringField
Name = CdsDadosDescDado
Field Kind = fkData


TStringField
Name = CdsDadosCoisas
Field Kind = fkData



2 – TdataSource (Data Access)
Name = DsDados
DataSet = CdsDados

3 - TDBGrid (Data Controls):
Name = DDGrid1
Align = alBottom
DataSource = DsDados

3.1 – Adicione duas colunas ao Grid, correspondentes aos três
TFields do CdsDados

4 -TManagerGridZn (Estação ZN)
Grid = DBGrid1

5 - TBitBtn (Addititional)
Name = BtnActivar
Caption = 'Ativar'

cerifique-se de que o ManagerGridZn (TManagerGridZn), esteja associado ao DBGrid1. O DBGrid tem que estar associado ao DsDados(TDataSource), que deve estar associado ao CdsDados(TClientDataSet).

No evento OnClick do BtnAtivar codifique:


procedure TForm1.BtnActivarClick(Sender: TObject);
var
i: Integer;
begin
(* Ativa o Dataset*)
if not CdsDados.Active then
CdsDados.CreateDataSet;
//Preenche o DataSet
for i := 0 to 30 do
begin
CdsDados.InsertRecord([i, Format('Dado %d', [i]), 'Bla!!!!']);
end;
end;


Pronto!! That’s it!!!
Chega de código .... tá bom??? Também acho ... hehehe

Note que para trabalhar com a classe que criamos nào foi necessário NENHUMA linha código.

Execute o programa e teste conforme os screenshots abaixo:

Primeiro carregue os dados clickando no botão “Ativar”. Com o botão direito do mouse, chame o menu popup que o componente TManagerGridZn criou e associou ao grid. Selecione a 1° opção.



Altere as propriedades da coluna sobre a qual vc clickou.



Ok, fácil? Agora você pode intuitivamente manter um layout personalizado para qualquer grid, em qualquer um dos seus projetos.





Podemos concluir, a partir deste exemplo, que um programa bem modularizado, onde o grau de acoplamento é reduzido ao máximo, propicia o reuso de maneira eficiente. Também, que é possível a construção de componentes de fácil consumo (mínima curva de aprendizado por parte do desenvolvedor que vai usar o componente em seus programas).
Outro ponto importante a destacar é que com essa abordagem podemos desenvolver novas funcionalidades para sistemas que foram construídos por outros (sistemas antigos, legados), que precisam sofrer manutenção, demandando para isso um grau mínimo de intervenção no código dos desenvolvedores originais. Sobretudo porque, a maioria deles são extremamente sensíveis a qualquer intervenção, mínima que seja, em seus fontes.

A seguir, finalizaremos a implementação do componente “TManagerGridZn”, pois ainda faltam ser codificadas as funcionalidades de recuperar as colunas que o usuário definir como invisíveis, “Ver Colunas Escondidas” (Invisíveis), e o restore default values “Restaurar configuração original”.

Código completo da unit1.pas



unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, DB, DBClient, Grids, DBGrids, ManagerGridZn;

type
TForm1 = class(TForm)
DBGrid1: TDBGrid;
CdsDados: TClientDataSet;
dsDados: TDataSource;
CdsDadosID_Dado: TIntegerField;
CdsDadosDescDado: TStringField;
BtnActivar: TBitBtn;
ManagerGridZn1: TManagerGridZn;
CdsDadosCoisas: TStringField;
procedure BtnActivarClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.BtnActivarClick(Sender: TObject);
var
i: Integer;
begin
if not CdsDados.Active then
CdsDados.CreateDataSet;

for i := 0 to 30 do
begin
CdsDados.InsertRecord([i, Format('Dado %d', [i]), 'Bla']);
end;

end;

end.



unit1.dfm


object Form1: TForm1
Left = 223
Top = 168
Caption = 'Form1'
ClientHeight = 375
ClientWidth = 531
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object DBGrid1: TDBGrid
Left = 0
Top = 80
Width = 531
Height = 295
Align = alBottom
DataSource = dsDados
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
Columns = < expanded =" False" fieldname =" 'ID_Dado'" visible =" True" expanded =" False" fieldname =" 'DescDado'" visible =" True" expanded =" False" fieldname =" 'Coisas'" visible =" True">
end
object BtnActivar: TBitBtn
Left = 408
Top = 8
Width = 75
Height = 25
Caption = 'Ativar'
TabOrder = 1
OnClick = BtnActivarClick
end
object CdsDados: TClientDataSet
Aggregates = <>
Params = <>
Left = 316
Top = 40
object CdsDadosID_Dado: TIntegerField
FieldName = 'ID_Dado'
end
object CdsDadosDescDado: TStringField
FieldName = 'DescDado'
Size = 30
end
object CdsDadosCoisas: TStringField
FieldName = 'Coisas'
end
end
object dsDados: TDataSource
DataSet = CdsDados
Left = 388http://www.blogger.com/img/blank.gif
Top = 44
end
object ManagerGridZn1: TManagerGridZn
Grid = DBGrid1
Left = 80
Top = 12
end
end



Continuação da série Construção de Componentes

Artigo completo (View Full Post)

domingo, 18 de novembro de 2007

Construção de Componentes V


Ok, estou exausto. Faz tempo que não consigo postar nada aqui no Estação Zn. Estou terminando um artigo sobre Grafos, mas ele esta sendo bem mais trabalhoso que imaginei. Em breve, espero, ele estará pronto e será devidamente documenado aqui.
Por hora, daremos continuidade a seqüência sobre construção de componentes. Pretendo, desta vez, mudar a abordagem que adotei nos artigos anteriores. Não vou ficar repetindo explicações que já foram faladas previamente em artigos anteriores.

Gerenciador Dinâmico de Layout para DBGrids
Neste artigo irei construir um componente possuidor da capacidade de permitir ao usuário configurar várias propriedades de um DBGrid. Provendo uma funcionalidade de personalização da interface de um cadastro, por exemplo. Aumentando com isso a usabilidade de um software. Meu principal objetivo é que o componente possa oferecer a ação, de personalização do grid, intuitiva para o usuário e simultaneamente também, facilidade do programador usar-lo em seus programas.

Características do Componente:
A idéia é que ele seja o mais coeso o possível, ele vai receber um DBGrid qualquer por associação (faremos isso através de propriedade), a partir daí ele fará todo o trabalho. Veja a descrição das ações:


A) Visão do componente:

1 – DBGrid é associado ao Gerenciador de Layout de Grid.

2 - Gerenciador de Layout de Grid verifica se DBGrid possui
um menu Popup associado.
2.1 Gerenciador de Layout de Grid cria um menu Popup
com as seguintes opções: “Alterar o Layout do Grid” ,
Ver Colunas Escondidas” (Invisíveis) e “Restaurar Default”.
Isso, se e somente se DBGrid não possuir, nenhum Popup menu
associado.

2.2 Gerenciador de Layout de Grid cria apenas os itens com as
seguintes opções: “Alterar o Layout do Grid”,
Ver Colunas Escondidas” (Invisíveis) e “Restaurar Default”.
Isso, se e somente se DBGrid possuir, nenhum Popup menu associado.

3 - Gerenciador de Layout de Grid armazena todas as características
originais do DBGrid.

4 - Gerenciador de Layout de Grid cria diretório o qual será usado para
armazenas os dados referentes as alterações efetuadas pelo usuário no
DBGrid.

5 - Gerenciador de Layout de Grid lê, de uma arquivo XML presistido as
configurações do perfil do usuário, a última configuração feita do
DBGrid e plica ao mesmo.

As alterações permitidas serão: Largura da coluna, Título da Coluna, Ordem e
Visibilidade.



B) Visão do Usuário 1

1 – Usuário, com o botão direto do mouse, sobre a coluna que deseja alterar
o layout, seleciona “Alterar o Layout do Grid”.

2 - Gerenciador de Layout de Grid, exibe interface permitindo que usuário altere
o valor das propriedades: Largura da coluna, Título da Coluna, Ordem e
Visibilidade.

3 – Usuário, altera os valores das propriedades e confirma a operação.

4 - Gerenciador de Layout de Grid, grava as alterações e re-carrega o grid
com o que foi modificado.


C) Visão do Usuário 2

1 – Usuário, com o botão direto do mouse, sobre o DBGrid, seleciona
“Ver Colunas Escondidas”.

2 - Gerenciador de Layout de Grid, exibe interface listando apenas as colunas
do DGGrid, as quais foram, pelo usuário, definidas como invisíveis
previamente. A partir das configurações originais, definidas em tempo
de projeto pelo programador, o Gerenciador de Layout de Grid deverá
identificar quais foram originalmente definidas como invisíveis para
nunca permitir que estas sejam listadas nesta interface.

3 – Usuário, seleciona dentre as colunas listadas a que deseja tornar visível.
Confirmando em seguida a operação.

4 - Gerenciador de Layout de Grid, grava as alterações e
re-carrega o grid com o que foi modificado.


Mãos a Obra:

Aqui começa a parte de codificação do componente “Gerenciador de Layout de Grid”.
Antes, não custa nada detalhar um pouco como fazer isso numa versão mais recente do Delphi. Segue, passo a passo, como iniciar a criação de um componente no Delphi 2006.

Passo 1) Inicie no Delphi, conforme fizemos em exemplo anteriores um novo componente:

1.1 Desphi 2006: No menu “Components” ► “New VCL Component”
1.1.1 No diálogo seguinte escolha: “VCL for Delphi win 32”

1.2 Delphi 7: No menu “Components” ► “New Component”

Passo 2) Selecione TComponent para ancestral.
Passo 3) Defina a classe conforme a imagem abaixo.


Class Name = TManagerGridZn
Pallet Page = Estação Zn
Unit Name = ManagerGridZn.pas


Passo 4) Salve a unit ManagerGridZn.

Passo 5) Criando uma package para nosso novo component:
Se vc está trabalhando com Delphi 7, veja nos artigos anteriores. No Delphi 2006, selecione no Menu “File” ► “Package – Delphi for Win32”.



Agora vamos re-nomear a package. Com o botão direito do mouse sobe a package, no Project Manager, selecione “Rename”. Altere o nome da package, em seguida salve.



Passo 6) Adicione na pasta Contains a unit que criamos, onde definimos a classe, ManagerGridZn.pas. Para isso, basta clikar com o botão direito do mouse sobre a pasta e selecionar a opção “Add...”. Esta, pode ser acessada no “Project Manager”.

Passo 7) Codificando ...
 7.1 Definido Constantes: 


const
StoredFileDir = 'USERPROFILE';
AFolder = 'ZnGridProfile';
NameFileGrdMng = 'GridProfile%s.xml';
PopupMenuItemName: array [0..3] of String = ('ConfigurarGridSep',
'ConfigurarGridLayout', 'ConfigurarGridViewColumn', 'RestoreGridDefaultValues');





StoredFileDir: Constante cujo o valor defini com a variável de
ambiente do Windows que aponta para o diretório que
armazena o perfil do usuário logado. Neste diretório
pretendo persistir os dados com as configurações que
o componente fizer.

AFolder: Essa constante define o nome do diretório onde esses dados ficarão
presistidos.

NameFileGridMng: Define o nome do arquivo contendo os dados (Vou usar XML
para isso).

PopupMenuItemName: Define os nomes dos itens de menu, para o Popmenu que
associado ao DBGrid será o meio pelo qual o usuário
poderá executar as funcionalidades do componente
“TManagerGridZn”.

Propriedade Grid:
Vou criar a propriedade a qual será usada para associar ao componente “TManagerGridZn” o DbGrid que manipulará.

Antes, na seção “uses” da “interface” declare a unit “DBGrids”.

uses
SysUtils, Classes, DBGrids;


Em seguida na seção “published” da classe digite a propriedade.

type
TManagerGridZn = class(TComponent)
private
FGrid: TDBGrid;
procedure SetGrid(const Value: TDBGrid);
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
property Grid: TDBGrid read FGrid write SetGrid;
end;


Codificando o corpo do procedimento “SetGrid”:

procedure TManagerGridZn.SetGrid(const Value: TDBGrid);
begin
if FGrid <> Value then
begin
FGrid := Value;

if FGrid <> nil then
FGrid.FreeNotification(Self);
end;
end;


Passo 8) O Popup Menu:
Agora vamos cuidar para darmos ao componente a inteligência que o torne capaz de criar o menu popup conforme definimos anteriormente.
Após declarar a biblioteca “Menus” na seção “uses” da “interface” declare um capo na seção “private” conforme exemplificado abaixo:


type
(*estrutura de enumerado que serve para indicar a classe se a associação ao
Popup Menu foi: Externa (O Grid já possuia um menu associado),
Criado internamente (O Grid não possuia um menu associado),
Inexistente (Não há associação ainda) *)
TPopupExistent = (peExternalAssociation, peInternalCreated, peInexistent);

(* Casse que define o componente “Gerenciador de Layout de Grid”*)
TManagerGridZn = class(TComponent)
private
FGrid: TDBGrid;// Ponto de associação
FPopupMenu: TPopupMenu; // Ponto para associação da associação (DBGrid)
FIsPopupExistent: TPopupExistent;
procedure SetGrid(const Value: TDBGrid);
procedure SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean = False);
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
(* Interface para associação com um DBGrid *)
property Grid: TDBGrid read FGrid write SetGrid;
end;



8.1 Codificando o método privado para atribuição do Popup Menu:
procedure SetPopupMenu”.


procedure TManagerGridZn.SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean);
begin
if (FPopupMenu = Value) and (Value <> nil) then Exit;

(* "nAllowCreate" define se o popup menu será sempre criado.
Considerando o caso de a mesma instância da classe manipular
vários DBGrids que compartilham o mesmo popup menu. *)
if not Assigned(Value) or (nAllowCreate) then
begin
FPopupMenu := TPopupMenu.Create(Self);
FGrid.PopupMenu := FPopupMenu;
(* Avisa a classe que o popup menu foi criado *)
FIsPopupExistent := peInternalCreated;
end
else
begin
FPopupMenu := Value;
(* Avisa a classe que o popup menu já existia,
portanto possui itens. Ele foi associado com esses itens *)
FIsPopupExistent := peExternalAssociation;
end;

(* notifica a classe que o popup menu foi desassociado *)
if FPopupMenu <> nil then
FPopupMenu.FreeNotification(Self);
end;



8.2 Na seção protected os métodos que cuidam da criação e destruição dos
itens de menu dinamicamente:


protected
procedure BuildMenuPopup; virtual;
procedure CreatePopupMenuItem(const NameItem, CaptionItem: String); virtual;
procedure DestroyMenuItem; virtual;


8.3 Codificando o método protegido “procedure BuildMenuPopup”:

procedure TManagerGridZn.BuildMenuPopup;
var
i: Integer;
MyMenuitem: TMenuItem;
begin
(* Verifco se o componente encontra-se em run time*)
if not (csDesigning in Self.ComponentState) then
begin
if Assigned(FPopupMenu) then
begin
for i := 0 to High(PopupMenuItemName) do
begin
if (FPopupMenu.Items.Count > 0) and (i = 0) then
CreatePopupMenuItem(PopupMenuItemName[i], '-');

if (i = 1) then
CreatePopupMenuItem(PopupMenuItemName[i], 'Configurar Layuot do &Grid');

if (i = 2) then
CreatePopupMenuItem(PopupMenuItemName[i], '&Ver Colunas Invisíveis');

if (i = 3) then
CreatePopupMenuItem(PopupMenuItemName[i],
'&Restaurar Configuração Orignal');
end;
( * As linhas comentadas com "//" serão implementadas mais tarde *)
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[1]));
if assigned(MyMenuitem) then
//MyMenuitem.OnClick := ConfigurarGrid1Click;

MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[2]));
if assigned(MyMenuitem) then
//MyMenuitem.OnClick := ConfigurarGridViewColumnLayout1Click;

end;
end;
end;



8.4 Codificando o método protegido “procedure CreatePopupMenuItem”:

procedure TManagerGridZn.CreatePopupMenuItem(const NameItem,
CaptionItem: String);
var
MyMenuitem: TMenuItem;
begin
MyMenuitem := TMenuItem(Self.FindComponent(NameItem));
if not assigned(MyMenuitem) then
begin
MyMenuitem := TMenuItem.Create(Self);
with MyMenuitem do
begin
Name := NameItem;
Caption := CaptionItem;
end;
FPopupMenu.Items.Add(MyMenuitem);
end;
end;


Codificando o método protegido “procedure DestroyMenuItem”:

procedure TManagerGridZn.DestroyMenuItem;
var
MyMenuitem: TMenuItem;
i: Integer;
begin
if Assigned(FPopupMenu) then
begin
for i := 0 to High(PopupMenuItemName) do
begin
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[i]));
if assigned(MyMenuitem) then
begin
MyMenuitem.Free;
end;
end;
end;
end;


O Componente deverá montar os itens no menu popup exatamente no momento em que o DBGrid for associado ao “Gerenciador de Layout de Grid”. Portanto, no método “SetGrid” que faz interface para a associação do DBGrid, cuidaremos de chamar o método que monta o menu popup. Vamos acrescentar ao método “SetGrid” duas linhas para chamarmos respectivamente “SetPopupMenu” e “BuildMenuPopup”.


procedure TManagerGridZn.SetGrid(const Value: TDBGrid);
begin
if FGrid <> Value then
begin
FGrid := Value;

if FGrid <> nil then
begin
FGrid.FreeNotification(Self);
(* Cuida da associação, menu popup, associada ao DBGrid *)
SetPopupMenu(FGrid.PopupMenu);
(* Monta os itens de menu, os quais nos referimos
na especificação do componente *)
BuildMenuPopup;
end;
end;
end;


Passo 9) Persistindo e Recuperando as Configurações do Layout do DBGrid:

Agora vamos construir as funcionalidades para persistir as alterações efetuadas no Grid pelo usuário, de maneira que elas sejam percebida por ele como uma configuração personalizada. Para isso retornaremos a seção “protected” e acrescentaremos a ela a declaração dos métodos envolvidos nesta funcionalidade.


protected
(* Métodos que trabalham na presistência, e recuperação das alterações
efetuadas sobre as propriedades do DBGrid. *)
function BuildFilePathComplete: String;
procedure CreateFileProfileSTO; virtual;
procedure CreateFileProfile; virtual;
procedure LoadFileProfile; virtual;
function GetDirSt: String;
(* Métodos que trabalham na montagem do popup menu já implementados
anteriormente *)
procedure BuildMenuPopup; virtual;
procedure CreatePopupMenuItem(const NameItem, CaptionItem: String); virtual;
procedure DestroyMenuItem; virtual;
public


9.1 Codificando o método protegido “GetDirSt”:


function TManagerGridZn.GetDirSt: String;
var
AuxPath: String;
begin
(* Busca o path da variável de ambiente "%USERPROFILE%" *)
AuxPath := GetEnvironmentVariable(PChar(StoredFileDir));

(* Caso não encontre, busca o path da variável de ambiente "%TEMP%" *)
if (AuxPath = '') then
AuxPath := GetEnvironmentVariable(PChar('TEMP'));

if (AuxPath = '') then
raise Exception.Create(
'Não é possível encontrar diretório para salvar configurações.');
Result := AuxPath;

end;


Se estivéssemos programando em Delphi 5 deveríamos alocar memória dinamicamente. O código deveria ficar assim:

var
AuxPath: String;
AuxBuffer: array[0..4095] of Char;
begin
SetString(AuxPath, AuxBuffer,
GetEnvironmentVariable(PChar(StoredFileDir), AuxBuffer, SizeOf(AuxBuffer)));

if (AuxPath = '') then
begin
SetString(AuxPath, AuxBuffer,
GetEnvironmentVariable(PChar('TEMP'), AuxBuffer, SizeOf(AuxBuffer)));
end;

if (AuxPath = '') then
raise Exception.Create('Não é possível encontrar diretório para ' +
'salvar configurações.');
Result := AuxPath;


O Delphi 7 ainda suporta essa implementação, mas no 2006 isso não é mais possível.

9.2 Codificando o método protegido “BuildFilePathComplete”:

function TManagerGridZn.BuildFilePathComplete: String;
var
AuxPath: String;
begin
(* Busca o diretório das configrações pessoais *)
AuxPath := GetDirSt;
AuxPath := IncludeTrailingBackslash(AuxPath);
(*cancatena com o nome do arquivo *)
AuxPath := AuxPath + IncludeTrailingBackslash(AFolder);

if not DirectoryExists(AuxPath) then
CreateDir(AuxPath);
(* Acrescenta ao nome do arquivo a identificação do grid *)
AuxPath := AuxPath + Format(NameFileGrdMng, [FGrid.Name + FGrid.Owner.Name]);
Result := AuxPath;
(* inicializando a informação para a classe que o popup menu ainda não existe *)
FIsPopupExistent := peInexistent;
end;


9.3 Codificando o método protegido “CreateFileProfile”:

procedure TManagerGridZn.CreateFileProfile;
var
APath: String;
begin
APath := BuildFilePathComplete;
if not FileExists(APath) then
CreateFileProfileSTO;
end;

9.4 Repositório para os objetos que persistirão as propriedades,
tanto alteradas, quanto originais do DBGrid.

Vou optar por trabalhar com um datamodule, porque assim eu tenho a facilidade de em tempo de design trabalhar com os datasets de forma visual.
No Delphi 2006, para adicionar um datamodule acesse menu “New” ► “Other ...”



Vou alterar a propriedade “Name” dele para “DmMangerGridZn”, em seguida apagarei a variável que o Delphi cria na seção “interface” com o valor da propriedade name. O principal objetivo que quero obter com isso é poder isolar uma instância que criarei desta classe. Para salvar a unit usarei o nome de “MangerGridZnData.pas”

O próximo passo é adicionar dois ADODatasets no DmMangerGridZn, criar todos os TFields que preciso. Vamos trabalhar com os dois ADODatasets desconectados, de forma semelhante a um TClientDataSet (Conforme postado pelo Felipe em artigo anterior).



AdsDados:
Este vai armazenar as propriedades do DBGrid que forem manipuladas pelo usuário.
No Object Inspector (F11) altere as propriedades listadas abaixo:

Name = 'AdsDados';
LockType = ltBatchOptimistic;

No fields editor, adicione os seguintes Fields:

1) Armazena o índice da coluna do grid (posição da coluna): TIntegerField
Name = 'AdsDadosColumnsIndex';
DisplayLabel = 'Ordem da Coluna';
FieldName = 'ColumnsIndex';

2) Armazena o Título da Coluna: TStringField
Name = 'AdsDadosColumnTitleCaption';
FieldName = 'ColumnTitleCaption';
Size = 100;

3) Armazena o “FieldName” da Coluna: TStringField
Name := 'AdsDadosColumnFieldName';
DisplayLabel = 'Título da Coluna';
FieldName = 'ColumnFieldName';
Size = 50;

4) Armazena a visibilidade da Coluna: TBooleanField
Name = 'AdsDadosColumnVisible';
FieldName = 'ColumnVisible';

5) Armazena se Coluna é somente leitura: TBooleanField
Name = 'AdsDadosColumnReadOnly';
DisplayLabel = 'Visível';
FieldName = 'ColumnReadOnly';

6) Armazena a largura da Coluna: TIntegerField
Name = 'AdsDadosColumnWidth';
DisplayLabel = 'Largura';
FieldName = 'ColumnWidth';


AdsOriginalConfig:
Este vai armazenar as propriedades, ORIGINALMENTE, definidas pelo programador no DBGrid. No Object Inspector (F11) altere as propriedades listadas abaixo:


Name = 'AdsOriginalConfig';
LockType = ltBatchOptimistic;



1) Armazena o índice da coluna do grid (posição da coluna): TIntegerField
Name = 'AdsOriginalConfigColumnsIndex';
DisplayLabel = 'Ordem da Coluna';
FieldName = 'ColumnsIndex';

2) Armazena o Título da Coluna: TStringField
Name = 'AdsOriginalConfigColumnTitleCaption';
FieldName = 'ColumnTitleCaption';
Size = 100;

3) Armazena o “FieldName” da Coluna: TStringField
Name = 'AdsOriginalConfigColumnFieldName';
DisplayLabel = 'Título da Coluna';
FieldName = 'ColumnFieldName';
Size = 50;

4) Armazena a visibilidade da Coluna: TBooleanField
Name = 'AdsOriginalConfigColumnVisible';
FieldName = 'ColumnVisible';

5) Armazena se Coluna é somente leitura: TBooleanField
Name = 'AdsOriginalConfigColumnReadOnly';
DisplayLabel = 'Visível';
FieldName = 'ColumnReadOnly';

6) Armazena a largura da Coluna: TIntegerField
Name = 'AdsOriginalConfigColumnWidth';
DisplayLabel = 'Largura';
FieldName = 'ColumnWidth';

7) Armazena se a Coluna foi originalmente definida
como visível: TIntegerField
Name = 'AdsOriginalConfigUpdateVisibility';
FieldName = 'UpdateVisibility';

8) Armazena o nome do Grid concatenado com o nome do
Owner do grid: TIntegerField
Name = 'AdsOriginalConfigGridName';
FieldName = 'GridName';
Size = 100;


OBS: Para criar um TField click com o botão direito do mouse sobre o dataset
selecione “New Feild” e digite as propriedades de acordo com as listadas acima,
cada qual respectivamente no ADODataset respectivo.




9.2.1 Codificando os métodos do datamodule “DmMangerGridZn”:

A idéia aqui é demonstrar como podemos modularizar o programa de forma a focalizar a
responsabilidade devida a cada classe. Ou seja, neste caso, a classe responsável pela
persistência fica sendo o datamodule, visto que cada dataset é um “sub-objeto” dele.
Portanto, digite na seção “public” os métodos conforme o exemplo abaixo:


unit MangerGridZnData;

interface

uses
SysUtils, Classes, DB, ADODB;

type
TDmMangerGridZn = class(TDataModule)
AdsDados: TADODataSet;
AdsDadosColumnsIndex: TIntegerField;
AdsDadosColumnTitleCaption: TStringField;
AdsDadosColumnFieldName: TStringField;
AdsDadosColumnVisible: TBooleanField;
AdsDadosColumnReadOnly: TBooleanField;
AdsDadosColumnEnabled: TBooleanField;
AdsDadosColumnWidth: TIntegerField;
AdsOriginalConfig: TADODataSet;
AdsOriginalConfigColumnsIndex: TIntegerField;
AdsOriginalConfigColumnTitleCaption: TStringField;
AdsOriginalConfigColumnFieldName: TStringField;
AdsOriginalConfigColumnVisible: TBooleanField;
AdsOriginalConfigColumnReadOnly: TBooleanField;
AdsOriginalConfigColumnEnabled: TBooleanField;
AdsOriginalConfigColumnWidth: TIntegerField;
AdsOriginalConfigUpdateVisibility: TBooleanField;
AdsOriginalConfigGridName: TStringField;
private
procedure ClonaDataset(QryReferencia, QryClone:TADODataSet;
const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
public
procedure SaveToFile(const APath: String);
procedure LoadFromFile(const APath: String);
function GetDefaultConfiguration(const GridName,
GridOwnerName: String): TADODataSet;
procedure ApplyFilterConfigGrid(const GridName, GridOwnerName: String);
procedure CalcelFilterConfigGrid;
end;


Segue a implementação dos métodos públicos do TDmMangerGridZn que cuidam da
persistência. Antes de codificar declare a biblioteca “Variantes”, e uma constante
na seção “implementation” conforme trecho abaixo:


implementation

{$R *.dfm}

const SaveFormat = pfXML;



procedure TDmMangerGridZn.SaveToFile(const APath: String);
begin
(*SaveFormat é uma constante que define a esteção do arquivo
gerado pelo AdsDados, no caso ".xml"*)
if AdsDados.Active then
AdsDados.SaveToFile(APath, SaveFormat);
end;



procedure TDmMangerGridZn.LoadFromFile(const APath: String);
begin
if AdsDados.Active then
AdsDados.LoadFromFile(APath);

end;


No evento OnCreate do Datamodule vou ativar os ADODatasets.

procedure TDmMangerGridZn.DataModuleCreate(Sender: TObject);
begin
AdsDados.CreateDataSet;
AdsOrignalConfig.CreateDataSet;
end;


Preciso implementar no componente a capacidade dele guardar as característica
originais do DBGrid que se encontra associado a ele. Além disso, preciso considerar
a possibilidade da instância desse objeto manipular mais de um grid em tempo de
execução (ou seja, dinamicamente). Por isso preciso de um campo que identifique
univocamente as propriedades alteradas de um determinado DBGrid, este campo é o
e para recuperar as informações exclusivas de um grid, vou trabalhar filtranto o
dataset que armazena as configurações originais por grid associado a instância.


procedure TDmMangerGridZn.ApplyFilterConfigGrid(const GridName,
GridOwnerName: String);
begin
AdsOriginalConfig.Filter := AdsOriginalConfigGridName.FieldName +
' = ' + QuotedStr(GridName + GridOwnerName);
AdsOriginalConfig.Filtered := True;
end;

Para que o componente possa eficientemente trabalhar manipulando mais de um DBGrid, na mesma instância, tenho que garantir que, no caso de um DBGrid “A”, que anteriormente tenha sido manipulado pelo MangerGridZn, e por isso suas características default estão persistidas nele, possam ser recuperadas caso ele seja associado novamente. Com esse objetivo vou filtrar e desfiltrar o AdsOrignalConfig. Por tanto, já codifiquei o filtro, agora preciso desfazê-lo.

procedure TDmMangerGridZn.CalcelFilterConfigGrid;
begin
AdsOriginalConfig.Filter := '';
AdsOriginalConfig.Filtered := False;
end;


O Método privado “ClonaDataset” tem o objetivo semelhante ao “Clone” de
“TCustomADODataSet” só que neste caso não quero criar nenhum ponteiramento entre
os datasetes envolvidos na clonagem. Em virtude disso implementei um método
especifico.


procedure TDmMangerGridZn.ClonaDataset(QryReferencia, QryClone: TADODataSet;
const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
var
i: Integer;
ClonaKind, ClonaVisible: Boolean;
AuxField: TField;
AbookMark: String;
begin
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
ClonaVisible := not nAllowCloneVisible;
if not ClonaVisible then
ClonaVisible := (QryReferencia.Fields[i].Visible);

ClonaKind := nAllowCloneAllKind;
if not ClonaKind then
ClonaKind := (QryReferencia.Fields[i].FieldKind = fkData);

if ClonaKind and ClonaVisible then
begin
QryClone.FieldDefs.Add(QryReferencia.Fields[i].FieldName,
QryReferencia.Fields[i].DataType,
QryReferencia.Fields[i].Size);
end;
end;

QryClone.CreateDataSet;
try
QryReferencia.DisableControls;
AbookMark := QryReferencia.Bookmark;
QryReferencia.First;
while not QryReferencia.Eof do
begin
QryClone.Append;
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
AuxField := QryClone.FindField(QryReferencia.Fields[i].FieldName);
if assigned(AuxField) then
begin
AuxField.Value := QryReferencia.Fields[i].Value;
AuxField.DisplayLabel := QryReferencia.Fields[i].DisplayLabel;
end;
end;
QryClone.Post;
QryReferencia.Next;
end;
finally
QryReferencia.Bookmark := AbookMark;
QryReferencia.EnableControls;
end;
end;


function TDmMangerGridZn.GetDefaultConfiguration(const GridName,
GridOwnerName: String): TADODataSet;
begin
(* retorna os dados resultantes do filtro aplicado pelo o nome do grid.
Dessa forma o componente pode dinamicamente trabalhar com vários Dbgrids,
alernadamente, na mesma instância sem preder as config originais da cada
um deles. GM 07/11/2007*)
try
ApplyFilterConfigGrid(GridName, GridOwnerName);
Result := TADODataSet.Create(Self);
ClonaDataset(AdsOriginalConfig, Result, False, False);
finally
CalcelFilterConfigGrid;
end;
end;


9.5 Associando a Datamodule criado ao componente TManagerGridZn.

Na seção “uses” da “interface” da unit “ManagerGridZn” declare a unit
“MangerGridZnData”. Esse tipo de associação é chamada de associação por atributo.



interface

uses
SysUtils, Classes, DBGrids, Menus, MangerGridZnData;


Na classe TManagerGridZn declare, na seção “private”, o campo que é uma instância do datamodule que acabamos de criar.


TManagerGridZn = class(TComponent)
private
FGrid: TDBGrid;// Ponto de associação
FPopupMenu: TPopupMenu; // Ponto para associação da associação (DBGrid)
FIsPopupExistent: TPopupExistent;
(* Associação do objeto datamodule que criamos fazer a persistência
inerente a classe TManagerGridZn .*)
DmMangerGridZn: TDmMangerGridZn;


9.6 Codificando o método protegido “CreateFileProfileSTO”:


procedure TManagerGridZn.CreateFileProfileSTO;
var
AuxPath: String;
begin
AuxPath := BuildFilePathComplete;
DmMangerGridZn.SaveToFile(AuxPath);
end;


O método “StoOriginalDisignConfigutarion’ armazena as configurações default do DBGrid. Ou seja, os valores definidos para as propriedades do DBGrid definidos pelo programador, em tempo de disign. Posteriormente codificaremos a chada a ele no método "SetGrid".


procedure TManagerGridZn.StoOriginalDisignConfigutarion;
var
i: Integer;
AuxValues: Variant;
AuxFieldsLocate: String;
begin
(* Filtra pelo Nome do Grid para que possa persistir as informações default
de vários DBGrids. Dessa forma o componente pode, dinamicamente, trabalhar
com vários Dbgrids, alternadamente, na mesma instância sem perder as
config originais da cada um deles. *)

try
if not DmMangerGridZn.AdsOriginalConfig.IsEmpty then
DmMangerGridZn.ApplyFilterConfigGrid(FGrid.Name, FGrid.Owner.Name);


(* Guarda a configuração do grid feita em design *)

for i := 0 to Pred(FGrid.Columns.Count) do
begin
if Assigned(FGrid.Columns[i].Field) then
begin
(* recuperando valores para o locate *)
AuxValues := VarArrayOf([QuotedStr(FGrid.Name + FGrid.Owner.Name),
QuotedStr(FGrid.Columns[i].Field.FieldName)]);

AuxFieldsLocate := DmMangerGridZn.AdsOriginalConfigGridName.FieldName
+';' + DmMangerGridZn.AdsOriginalConfigColumnFieldName.FieldName;
(* Garante que não haverão linhas repetidas para a chave Grid/Coluna *)
if not DmMangerGridZn.AdsOriginalConfig.Locate(AuxFieldsLocate,
AuxValues, []) then
begin
DmMangerGridZn.AdsOriginalConfig.Append;
DmMangerGridZn.AdsOriginalConfigGridName.AsString :=
FGrid.Name + FGrid.Owner.Name;
DmMangerGridZn.AdsOriginalConfigColumnsIndex.AsInteger := i;
DmMangerGridZn.AdsOriginalConfigColumnTitleCaption.AsString :=
FGrid.Columns[i].Title.Caption;
DmMangerGridZn.AdsOriginalConfigColumnFieldName.AsString :=
FGrid.Columns[i].Field.FieldName;
DmMangerGridZn.AdsOriginalConfigColumnVisible.AsBoolean :=
FGrid.Columns[i].Visible;
DmMangerGridZn.AdsOriginalConfigColumnReadOnly.AsBoolean :=
FGrid.Columns[i].ReadOnly;
DmMangerGridZn.AdsOriginalConfigColumnEnabled.AsBoolean :=
FGrid.Columns[i].ReadOnly;
DmMangerGridZn.AdsOriginalConfigColumnWidth.AsInteger :=
FGrid.Columns[i].Width;
DmMangerGridZn.AdsOriginalConfigUpdateVisibility.AsBoolean :=
FGrid.Columns[i].Visible;
DmMangerGridZn.AdsOriginalConfig.Post;
end;
end;
end;
finally
DmMangerGridZn.CalcelFilterConfigGrid;
end;
end;



9.7 “LoadFileProfile”: Carregar de um arquivo o perfil para o DBGrid.

Agora vamos codificar a ação do componente ler as configurações, setadas pelo usuário, persistidas no arquivo XML, e atribuí-las ao DBGrid. Ok, neste momento eu terei os dados referentes a várias propriedades de cada coluna, mas como eu vou pegar a coluna do grid para setar esses valores? O Identificador da coluna que armazenei é o valor da propriedade “FieldName”, entretanto, no DBGrid não tenho nenhum método que me retorne uma coluna pelo “FieldName”. Portanto, antes eu preciso criar uma função que faça exatamente isso.


9.7.1 Método privado “FindColumn”: Na seção “private” declare o cabeçalho do
método.

  function FindColumn(const AFieldName: String): TColumn;



function TManagerGridZn.FindColumn(const AFieldName: String): TColumn;
var
i: Integer;
begin
Result := nil;
for i := 0 to Pred(FGrid.Columns.Count) do
begin
if FGrid.Columns[i].Field.FieldName = AFieldName then
begin
Result := FGrid.Columns[i];
Break;
end;
end;
end;


Agora sim, codificando LoadFileProfile. Mais uma vez, na seção “protected” declare o método conforme exemplificado abaixo:


TManagerGridZn = class(TComponent)
private
// Atributos
FGrid: TDBGrid;// Ponto de associação
FPopupMenu: TPopupMenu; // Ponto para associação da associação (DBGrid)
FIsPopupExistent: TPopupExistent;
DmMangerGridZn: TDmMangerGridZn;

// Métodos privados
procedure SetGrid(const Value: TDBGrid);
procedure SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean = False);
function FindColumn(const AFieldName: String): TColumn;
protected
(* Métodos que trabalham na presistência, e recuperação das alterações
efetuadas sobre as propriedades do DBGrid. *)
function BuildFilePathComplete: String;
procedure CreateFileProfileSTO; virtual;
procedure CreateFileProfile; virtual;
(*após digitar conforme linha abaixo, pressione “Ctrl + Shift + C ”*)
procedure LoadFileProfile; virtual;
procedure StoOriginalDisignConfigutarion; virtual;
procedure LoadFileProfile; virtual;
procedure StoOriginalDisignConfigutarion; virtual;
function GetDirSt: String;
(* Métodos que trabalham na montagem do popup menu *)
procedure BuildMenuPopup; virtual;
procedure CreatePopupMenuItem(const NameItem, CaptionItem: String); virtual;
procedure DestroyMenuItem; virtual;
  public



procedure TManagerGridZn.LoadFileProfile;
var
APath: String;
AuxColumn: TColumn;
begin
APath := BuildFilePathComplete;

if not FileExists(APath) then
CreateFileProfileSTO;

DmMangerGridZn.LoadFromFile(APath);
if not (DmMangerGridZn.AdsDados.IsEmpty) then
begin
while not DmMangerGridZn.AdsDados.Eof do
begin
AuxColumn := FindColumn(DmMangerGridZn.AdsDadosColumnFieldName.AsString);
if assigned(AuxColumn) then
begin
AuxColumn.Visible := DmMangerGridZn.AdsDadosColumnVisible.AsBoolean;
AuxColumn.ReadOnly := DmMangerGridZn.AdsDadosColumnReadOnly.AsBoolean;
AuxColumn.Width := DmMangerGridZn.AdsDadosColumnWidth.AsInteger;
AuxColumn.Title.Caption :=
DmMangerGridZn.AdsDadosColumnTitleCaption.AsString;
AuxColumn.Index := DmMangerGridZn.AdsDadosColumnsIndex.AsInteger;
end;
DmMangerGridZn.AdsDados.Next;
end;
end;
end;


Preciso acrescentar o “LoadFileProfile” e o "
StoOriginalDisignConfigutarion" no método “SetGrid”. Logo após a montagem do menu acrescente conforme o exemplo abaixo:

procedure TManagerGridZn.SetGrid(const Value: TDBGrid);
begin
if FGrid <> Value then
begin
FGrid := Value;

if FGrid <> nil then
begin
FGrid.FreeNotification(Self);
(* Cuida da associação, menu popup, associada ao DBGrid *)
SetPopupMenu(FGrid.PopupMenu);
(* Monta os itens de menu, os quais nos referimos
na especificação do componente *)
BuildMenuPopup;
if not (csDesigning in Self.ComponentState) then
begin
(* Carrega as configurações originais do Grid *)
StoOriginalDisignConfigutarion;
CreateFileProfile;
LoadFileProfile;
end;
end;
end;
end;



Passo 10) Métodos públicos de “TManagerGridZn”:

Vou sobre escrever o método “Create”, o “Destroy” e possibilitar a chamada ao método que recarrega o perfil a partir do arquivo XML.


public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure LoadSetup; virtual;
published
(* Interface para associação com um DBGrid *)
property Grid: TDBGrid read FGrid write SetGrid;
end;

... na seção implementation ...


constructor TManagerGridZn.Create(AOwner: TComponent);
begin
(* Chamo a herança *)
inherited Create(AOwner);
(* verificando se o componente está em tempo de execução *)
if not(csDesigning in Self.ComponentState) then
begin
(* Instancia o datamodule *)
DmMangerGridZn := TDmMangerGridZn.Create(Self);
end;
end;




destructor TManagerGridZn.Destroy;
begin
(* Se os itens do popup menu foram adicionados a
um que já existia no DBGrig preciso retirá-los. *)
if FIsPopupExistent = peExternalAssociation then
DestroymenuItem;
(*Chamando a herança *)
inherited Destroy;
end;



procedure TManagerGridZn.LoadSetup;
begin
(* permite acesso público ao método protegido *)
LoadFileProfile;
end;


Estamos próximo de poder partir para construção de um exemplo, a fim de testarmos tudo que codificamos até agora. Para garantir alguma robustez ao nosso componente vamos codificar o método “Notification” (Saiba mais em artigo anterior ...).
Na seção “protected” declare...


protected
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
(* Métodos que trabalham na presistência, e recuperação das alterações
efetuadas sobre as propriedades do DBGrid. *)
function BuildFilePathComplete: String;
procedure CreateFileProfileSTO; virtual;
procedure CreateFileProfile; virtual;
procedure LoadFileProfile; virtual;



procedure TManagerGridZn.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
//Remove a referência quando o componente externo é destuido
if (AComponent = FGrid) and (Operation = opRemove) then
FGrid := nil;
end;


Passo 11) Criando a interface para permitir ao usuário editar as propriedades da coluna do DBGrid associado ao componente.

Adicione um form a pakage “ManagerGridZnPkg”. (File ► New ► Form – Delphi for win32) . Configure conforme listado abaixo:

Nome = ProfileManagerZnFrm
Caption = ‘Setup da Coluna’;
ClientHeight = 400
ClientWidth = 613

Save a unit como “GridProfileManagerZnForm.pas



Adicione ao Form os seguintes componentes e os configure conforme lista abaixo:

1 – Tpanel (Standard):
Name = PnlBtn
Left = 0
Top = 357
Width = 613
Height = 43
Align = alBottom
BevelOuter = bvNone

1.1 Dentro do PnlBtn adicione dois TBitBtn (Additional)
Name = BtnSave
Left = 4
Top = 12
Width = 75
Height = 25
TabOrder = 0
Kind = bkOK

Name = BtnCancel: TBitBtn
Left = 508
Top = 12
Width = 75
Height = 25
TabOrder = 1
Kind = bkCancel

2 – TADODataSet
Name = AdsSetupColumn
LockType = ltBatchOptimistic

3 – TDataSource
Name = dsSetupColumn
DataSet = AdsSetupColumn

4 – TADODataSet
Name = AdsLstDados
LockType = ltBatchOptimistic

5 – TDataSource
Name = dsLstDados.
DataSet = AdsLstDados

6 – TADODataSet
Name = AdsLookupAux
LockType = ltBatchOptimistic
6.1 Adicione ao AdsLookupAux os seguintes TFields.

6.1.1 - TIntegerField:
Name = AdsLookupAuxIndexValue
FieldName = 'IndexValue'


6.1.2 – TstringField
Name = AdsLookupAuxListValue:
FieldName = 'ListValue'
Size = 4

7 – TDataSource
Name = dsLookupAux:
DataSet = AdsLookupAux

8 – TgroupBox
Name = GrpBxGrid
Top = 57
Width = 613
Height = 300
Align = alBottom
Caption = 'Configurando a Coluna : %s '

8.1 Dentro do GrpBxGrid adicione TDBGrid:
Name = GrdConfigColumn:
Left = 2
Top = 15
Width = 609
Height = 283
Align = alClient

9 – TPanel
Name = pnlTop
Width = 613
Height = 57
Align = alClient
BevelOuter = bvNone
TabOrder = 2

9.1 Dentro do pnlTop adicione os componentes:
9.1.1 TLabel
Name = Label1
Left = 108
Top = 8
Width = 83
Height = 13
Caption = 'Ordem da Coluna'

9.1.2 TLabel
Name = Label2
Left = 196
Top = 8
Width = 77
Height = 13
Caption = 'Título da Coluna'

9.1.3 TLabel
Name Label3: TLabel
Left = 376
Top = 8
Width = 37
Height = 13
Caption = 'Largura'
FocusControl = EdtLardura

9.5 TDBCheckBox
Name = CheckBxVisiblw:
Left = 16
Top = 22
Width = 89
Height = 17
Caption = 'Coluna &Vis'#237'vel'
DataField = 'ColumnVisible'
TabOrder = 0
ValueChecked = 'True'
ValueUnchecked = 'False'

9.6 TDBEdit
Name = EdtTitulo:
Left = 196
Top = 24
Width = 173
Height = 21
DataField = 'ColumnTitleCaption'
TabOrder = 2

9.7 TDBEdit
Nome = EdtLardura:
Left = 376
Top = 24
Width = 133
Height = 21
BiDiMode = bdRightToLeft
DataField = 'ColumnWidth'
TabOrder = 3

9.8 TDBLookupComboBox
Name = CmbOrdem:
Left = 108
Top = 24
Width = 81
Height = 21
DataField = 'ColumnsIndex'
KeyField = 'IndexValue'
ListField = 'ListValue'
TabOrder = 1

9.9 TGroupBox
Name = GrpBXPrevw:
Left = 544
Top = 8
Width = 65
Height = 43
Caption = 'Preview '
TabOrder = 4

9.9.1 Dentro do GrpBXPrevw adicione um TCheckBox:
Name = ChkBxPreview:
Left = 12
Top = 16
Width = 46
Height = 17
TabOrder = 0




Para a comunicação desse módulo com o que lhe externo vou definir uma propriedades que tem por objetivo diminuir ao máximo o acoplamento entre o form e o “TManagerGridZn”. Isso aumenta um pouco a complexidade na implementação inicial do form. Para um programador pouco experiente essa abordagem, além de lhe ser difícil de entender, pode lhe parecer desnecessária. Mas não vou me prender em explicar mais sobre isso agora.


unit GridProfileManagerZnForm;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DBCtrls, Mask, Grids, DBGrids, Buttons, ExtCtrls, DB, ADODB;

type
TProfileManagerZnFrm = class(TForm)
PnlBtn: TPanel;
BtnSave: TBitBtn;
btnCancel: TBitBtn;
GrpBxGrid: TGroupBox;
GrdConfigColumn: TDBGrid;
pnlTop: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
CheckBxVisiblw: TDBCheckBox;
EdtTitulo: TDBEdit;
EdtLardura: TDBEdit;
CmbOrdem: TDBLookupComboBox;
GrpBXPrevw: TGroupBox;
ChkBxPreview: TCheckBox;
AdsSetupColumn: TADODataSet;
dsSetupColumn: TDataSource;
AdsLookupAux: TADODataSet;
AdsLookupAuxIndexValue: TIntegerField;
AdsLookupAuxListValue: TStringField;
dsLookupAux: TDataSource;
AdsLstDados: TADODataSet;
dsLstDados: TDataSource;
procedure dsLstDadosDataChange(Sender: TObject; Field: TField);
private
{ Private declarations }
Public
(*Digite as propriedades conforme as linhas abaixo e pressione Ctrl + Shift+ C *)
property NameFieldColumnsIndex: String read FNameFieldColumnsIndex
write FNameFieldColumnsIndex;
property NameFieldColumnTitleCaption: String read FNameFieldColumnTitleCaption
write FNameFieldColumnTitleCaption;
property NameFieldColumnFieldName: String read FNameFieldColumnFieldName
write FNameFieldColumnFieldName;
property NameFieldColumnVisible: String read FNameFieldColumnVisible
write FNameFieldColumnVisible;
property NameFieldColumnReadOnly: String read FNameFieldColumnReadOnly
write FNameFieldColumnReadOnly;
property NameFieldColumnEnabled: String read FNameFieldColumnEnabled
write FNameFieldColumnEnabled;
property NameFieldColumnWidth: String read FNameFieldColumnWidth
write FNameFieldColumnWidth;
end;


Codificando os métodos privados do form TProfileManagerZnFrm:


private
(* Atributos *)
FNameFieldColumnsIndex: String;
FNameFieldColumnTitleCaption: String;
FNameFieldColumnFieldName: String;
FNameFieldColumnVisible: String;
FNameFieldColumnReadOnly: String;
FNameFieldColumnEnabled: String;
FNameFieldColumnWidth: String;

(* Métodos privados *)
function FindColumn(const AFieldName: String): TColumn;
procedure BuildLookupCombo(AGrid: TDBGrid);
procedure SetDados;
procedure SetupGridField;
procedure SetDadosPreview;
procedure SetupFieldChange;
public



Método que preenche a combo que permite alterar a ordem da coluna do Grid.

procedure TProfileManagerZnFrm.BuildLookupCombo(AGrid: TDBGrid);
const
AOrdem = '%dº';
var
i: Integer;
begin
// o Parâmtro AGrid aponta para o Grid que está associado a classe TManagerGridZn
(*Monta o Combo box com as ordens das colunas presentes no AGrid,
adicionando ao dataset que esta associado ao ComboBox. *)
for i := 0 to Pred(AGrid.Columns.Count) do
AdsLookupAux.AppendRecord([i, Format(AOrdem, [i + 1])]);
end;


Precisarei de duas variáveis para me auxiliar a manipulação da coluna.

FNameFieldColumnWidth: String;
FColumnConfigurated: TColumn;
FColumnPreview: TColumn;

(* Os Atributos abaixo apontarão para a coluna que precisarei manipular
para exibir ao usuário as operações que ele estará realizando*)
FColumnConfigurated: TColumn;
FColumnPreview: TColumn;

(* Métodos privados *)
function FindColumn(const AFieldName: String): TColumn;
procedure BuildLookupCombo(AGrid: TDBGrid);
procedure SetDados;
procedure SetupGridField;
procedure ClonaDataset(QryReferencia, QryClone:TADODataSet;
const nAllowCloneAllKind, nAllowCloneVisible: Boolean);


Pelo mesmo motivo na classe TManagerGridZn, o método “FindColumn” se faz necessário aqui no form TProfileManagerZnFrm.

function TProfileManagerZnFrm.FindColumn(const AFieldName: String): TColumn;
var
i: Integer;
begin
Result := nil;
for i := 0 to Pred(GrdConfigColumn.Columns.Count) do
begin
if Assigned(GrdConfigColumn.Columns[i].Field) then
if GrdConfigColumn.Columns[i].Field.FieldName = AFieldName then
begin
Result := GrdConfigColumn.Columns[i];
Break;
end;
end;
end;



procedure TProfileManagerZnFrm.SetDados;
begin
(* Atribui ao Dataset que armazena as iformações sobre a coluna,
os valores correntes na coluna cujo o usuário deseja configurar.
Esta Dataset está ponteirado com o AdsDados presente no datamodule. *)
AdsSetupColumn.FieldByName(NameFieldColumnsIndex).AsInteger :=
FColumnConfigurated.Index;
AdsSetupColumn.FieldByName(NameFieldColumnTitleCaption).AsString :=
FColumnConfigurated.Title.Caption;
AdsSetupColumn.FieldByName(NameFieldColumnFieldName).AsString :=
FColumnConfigurated.FieldName;
AdsSetupColumn.FieldByName(NameFieldColumnVisible).AsBoolean :=
FColumnConfigurated.Visible;
AdsSetupColumn.FieldByName(NameFieldColumnWidth).AsInteger :=
FColumnConfigurated.Width;

end;


Vou copiar a ClonaDataset, visto que não quero gastar mais tempo construindo uma unit de funções comuns aos módulos criados neste a artigo. Mas com certeza essa seria a melhor pratica num caso real.

procedure TProfileManagerZnFrm.ClonaDataset(QryReferencia,
QryClone: TADODataSet; const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
var
i: Integer;
ClonaKind, ClonaVisible: Boolean;
AuxField: TField;
AbookMark: String;
begin
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
ClonaVisible := not nAllowCloneVisible;
if not ClonaVisible then
ClonaVisible := (QryReferencia.Fields[i].Visible);

ClonaKind := nAllowCloneAllKind;
if not ClonaKind then
ClonaKind := (QryReferencia.Fields[i].FieldKind = fkData);

if ClonaKind and ClonaVisible then
begin
QryClone.FieldDefs.Add(QryReferencia.Fields[i].FieldName,
QryReferencia.Fields[i].DataType, QryReferencia.Fields[i].Size);
end;
end;

QryClone.CreateDataSet;
try
QryReferencia.DisableControls;
AbookMark := QryReferencia.Bookmark;
QryReferencia.First;
while not QryReferencia.Eof do
begin
QryClone.Append;
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
AuxField := QryClone.FindField(QryReferencia.Fields[i].FieldName);
if assigned(AuxField) then
begin
AuxField.Value := QryReferencia.Fields[i].Value;
AuxField.DisplayLabel := QryReferencia.Fields[i].DisplayLabel;
end;
end;
QryClone.Post;
QryReferencia.Next;
end;
finally
QryReferencia.Bookmark := AbookMark;
QryReferencia.EnableControls;
end;
end;



procedure TProfileManagerZnFrm.SetupGridField;
var
AuxField: TField;
AuxColumn: TColumn;
begin
(*Este método marca de Highlight a coluna que esta sendo configurada
pelo usuário. *)
AuxColumn := FindColumn(FColumnConfigurated.Field.FieldName);

if Assigned(AuxColumn) then
begin
AuxColumn.Title.Color := clHighlight;
AuxColumn.Title.Font.Color := clHighlightText;
AuxColumn.Color := clHighlight;
AuxColumn.Font.Color := clHighlightText;
end;

AuxField := AdsLstDados.FindField(FColumnConfigurated.Field.FieldName);
(*Seta o foco na coluna do Grid*)
if Assigned(AuxField) then
begin
AuxField.FocusControl;
GrdConfigColumn.SelectedField := AuxField;
end;

end;



O método “Execute” é o único público desta classe:


public
(*Digite conforme linha abaixo *)
function Execute(AGrid: TDBGrid; AColumn: TColumn; AdsProfile,
AdsDet: TADODataSet): Boolean;
(* Propriedades *)
property NameFieldColumnsIndex: String read FNameFieldColumnsIndex
write FNameFieldColumnsIndex;
property NameFieldColumnTitleCaption: String read FNameFieldColumnTitleCaption
write FNameFieldColumnTitleCaption;
property NameFieldColumnFieldName: String read FNameFieldColumnFieldName
write FNameFieldColumnFieldName;
property NameFieldColumnVisible: String read FNameFieldColumnVisible
write FNameFieldColumnVisible;
property NameFieldColumnReadOnly: String read FNameFieldColumnReadOnly
write FNameFieldColumnReadOnly;
property NameFieldColumnEnabled: String read FNameFieldColumnEnabled
write FNameFieldColumnEnabled;
property NameFieldColumnWidth: String read FNameFieldColumnWidth
write FNameFieldColumnWidth;
function Execute(AGrid: TDBGrid; AColumn: TColumn; AdsProfile,
AdsDet: TADODataSet): Boolean;

end;


function TProfileManagerZnFrm.Execute(AGrid: TDBGrid; AColumn: TColumn;
AdsProfile, AdsDet: TADODataSet): Boolean;
begin
(* Configurando a exibição do Form *)
Self.BorderStyle := bsDialog;
Self.Position := poScreenCenter;
Self.FormStyle := fsNormal;
(* Monda a Combo Ordem *)
BuildLookupCombo(AGrid);

(*Copiando os dados de um dataset para o outro *)
ClonaDataset(AdsDet, AdsLstDados, True, True);
AdsSetupColumn.Clone(AdsProfile);

(*Guarda a referência da colun que esta sendo alterada *)
FColumnConfigurated := AColumn;
FColumnConfigurated.Field := AColumn.Field;


(* Copiando o Grid associado a TManagerGridZn *)
GrdConfigColumn.Columns.Assign(AGrid.Columns);

(* Exibindo pro usuário a coluna que está sendo alterada *)
GrpBxGrid.Caption := Format(GrpBxGrid.Caption, [AColumn.Title.Caption]);
Self.Caption := Self.Caption + ': ' + AColumn.Title.Caption;
if not AdsSetupColumn.Locate(FNameFieldColumnFieldName,
AColumn.Field.FieldName, []) then
begin
dsSetupColumn.DataSet.Append;
SetDados(FColumnConfigurated);
end;

SetupFieldChange;
Self.ShowModal;
Result := (Self.ModalResult = mrOk)

end;


10.1 Eventos do form TProfileManagerZnFrm.

OnClick do BtnSave “BtnSaveClick”:


procedure TProfileManagerZnFrm.BtnSaveClick(Sender: TObject);
begin
if dsSetupColumn.DataSet.State in [dsEdit, dsInsert] then
dsSetupColumn.DataSet.Post;
end;


OnClick do BtnSave “BtnSaveClick”:


procedure TProfileManagerZnFrm.BtnSaveClick(Sender: TObject);
begin
if dsSetupColumn.DataSet.State in [dsEdit, dsInsert] then
dsSetupColumn.DataSet.Post;
end;


OnClick do BtnCancel “BtnCancelClick”:


procedure TProfileManagerZnFrm.btnCancelClick(Sender: TObject);
begin
if dsSetupColumn.DataSet.State in [dsEdit, dsInsert] then
dsSetupColumn.DataSet.Cancel;
end;


OnCreate do form TProfileManagerZnFrm “FormCreate”:


procedure TProfileManagerZnFrm.FormCreate(Sender: TObject);
begin
(*Inicializa o CheckBox do Preview *)
ChkBxPreview.Checked := False;
(* Ativa o ADODataset *)
AdsLookupAux.CreateDataSet;
end;

Codificando o método efetuará na interface o efeito da alteração, feia pelo usuário, da propriedade do Grid. Ou seja, um preview do valor alterado de uma das propriedades: "SetDadosPreview".



procedure TProfileManagerZnFrm.SetDadosPreview;
begin
GrdConfigColumn.Columns.BeginUpdate;
FColumnPreview := FindColumn(FColumnConfigurated.Field.FieldName);
if not Assigned(FColumnPreview) then Exit;

if ChkBxPreview.Checked then
begin
FColumnPreview.Visible :=
AdsSetupColumn.FieldByName(NameFieldColumnVisible).AsBoolean;
FColumnPreview.ReadOnly :=
AdsSetupColumn.FieldByName(NameFieldColumnReadOnly).AsBoolean;
FColumnPreview.Width :=
AdsSetupColumn.FieldByName(NameFieldColumnWidth).AsInteger;
FColumnPreview.Title.
Caption := AdsSetupColumn.FieldByName(NameFieldColumnTitleCaption).AsString;
FColumnPreview.Index :=
AdsSetupColumn.FieldByName(NameFieldColumnsIndex).AsInteger;
end
else
FColumnPreview.Assign(FColumnConfigurated);

GrdConfigColumn.Columns.EndUpdate;

end;


OnClick da combo CmbOrdem “CmbOrdemClick”:


procedure TProfileManagerZnFrm.CmbOrdemClick(Sender: TObject);
begin
SetDadosPreview;
end;


OnClick do ChkBxPreview “ChkBxPreviewClick”:


procedure TProfileManagerZnFrm.ChkBxPreviewClick(Sender: TObject);
begin
SetDadosPreview;
SetupGridField;
end;


Criarei um evento OnChange genérico para ser atribuído aos tfields do AdsSetupColumn. Declare dentro da seção “private”, conforme exemplificado abaixo:


private
(* Atributos *)
FNameFieldColumnsIndex: String;
FNameFieldColumnTitleCaption: String;
FNameFieldColumnFieldName: String;
FNameFieldColumnVisible: String;
FNameFieldColumnReadOnly: String;
FNameFieldColumnEnabled: String;
FNameFieldColumnWidth: String;

(* Os Atributos abaixo apontarão para a coluna que precisarei manipular
para exibir ao usuário as operações que ele estará realizando*)
FColumnConfigurated: TColumn;
FColumnPreview: TColumn;

(* Métodos privados *)




procedure TProfileManagerZnFrm.FieldValueChange(Sender: TField);
begin
(* Assim eu posso em tempo real exibir para o usuário
a alteração da propriedade de ele efetuou na coluna do Grid. *)
SetDadosPreview;
end;


Retornaremos agora a seção private para implementar o último método que restou.


procedure TProfileManagerZnFrm.SetupFieldChange;
var
i: Integer;
begin
(* este método procura qualquer tfield no AdsSetupColumn,
Para atribuir ao seu evento OnChange o método genérico que
Criamos anteriormente.*)
for i := 0 to Pred(AdsSetupColumn.Fields.Count) do
begin
AdsSetupColumn.Fields[i].OnChange := FieldValueChange;
end;
end;



OnShow do form TProfileManagerZnFrm “FormShow”:

procedure TProfileManagerZnFrm.FormShow(Sender: TObject);
begin
(* Quando o form for exibido os TFields do AdsSetupColumn ja terão sido
criados, portanto poderei configurá-los com o evento genérico OnChange *)
SetupGridField;
end;


Agora, voltamos nossa ateção novamente para a classe TManagerGridZn, vamos codificar o evento click do item do popup menu que chamará a interface que acabamos de criar. Para isso, Adicione na seção “uses” da implementation, a unit GridProfileManagerZnForm.pas, adicione também “ADODb”.


implementation

uses
Variants, GridProfileManagerZnForm, ADODb;


Na seção protected digite conforme exemplificado abaixo:


(* Métodos que trabalham na montagem do popup menu *)
procedure BuildMenuPopup; virtual;
procedure CreatePopupMenuItem(const NameItem, CaptionItem: String); virtual;
procedure DestroyMenuItem; virtual;

(* métodos que darão ação ao click dos intens de menu *)
procedure ConfigurarLayoutGridClick(Sender: TObject); virtual;
function GetSelectedColunm: TColumn; virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure LoadSetup; virtual;
published
(* Interface para associação com um DBGrid *)
property Grid: TDBGrid read FGrid write SetGrid;


Preciso recuperar a coluna que o usuário selecionou para ser configurada.

function TManagerGridZn.GetSelectedColunm: TColumn;
var
i: Integer;
begin
(* retorna a coluna que estiver selecionada no momento em que o usuário
clickou com o botão direito acionando o popup menu *)
Result := nil;
for i := 0 to Pred(FGrid.Columns.Count) do
begin
if FGrid.Columns[i].FieldName = FGrid.SelectedField.FieldName then
Result := FGrid.Columns[i];
if Assigned(Result) then Break;
end;
end;



procedure TManagerGridZn.ConfigurarLayoutGridClick(Sender: TObject);
var
SetupFrm: TProfileManagerZnFrm;
AuxColumn: TColumn;
AuxPath: String;
begin
AuxColumn := GetSelectedColunm;
try
SetupFrm := TProfileManagerZnFrm.Create(Self);
SetupFrm.NameFieldColumnsIndex :=
DmMangerGridZn.AdsDadosColumnsIndex.FieldName;
SetupFrm.NameFieldColumnTitleCaption :=
DmMangerGridZn.AdsDadosColumnTitleCaption.FieldName;
SetupFrm.NameFieldColumnFieldName :=
DmMangerGridZn.AdsDadosColumnFieldName.FieldName;
SetupFrm.NameFieldColumnVisible :=
DmMangerGridZn.AdsDadosColumnVisible.FieldName;
SetupFrm.NameFieldColumnReadOnly :=
DmMangerGridZn.AdsDadosColumnReadOnly.FieldName;
SetupFrm.NameFieldColumnEnabled :=
DmMangerGridZn.AdsDadosColumnEnabled.FieldName;
SetupFrm.NameFieldColumnWidth :=
DmMangerGridZn.AdsDadosColumnWidth.FieldName;

if SetupFrm.Execute(FGrid, AuxColumn, DmMangerGridZn.AdsDados,]
TADODataSet(FGrid.DataSource.DataSet)) then
begin
AuxPath := BuildFilePathComplete;
DmMangerGridZn.SaveToFile(AuxPath);
LoadFileProfile;
end;
finally
SetupFrm.Free;
end;
end;


O próximo passo é retornar a método que monta o menu popup para acrescentar a chamada ao método “ConfigurarLayoutGridClick” ao item de menu específico.


procedure TManagerGridZn.BuildMenuPopup;
var
i: Integer;
MyMenuitem: TMenuItem;
begin
(* Verifco se o componente encontra-se em run time*)
if not (csDesigning in Self.ComponentState) then
begin
if Assigned(FPopupMenu) then
begin
for i := 0 to High(PopupMenuItemName) do
begin
if (FPopupMenu.Items.Count > 0) and (i = 0) then
CreatePopupMenuItem(PopupMenuItemName[i], '-');

if (i = 1) then
CreatePopupMenuItem(PopupMenuItemName[i],
'Configurar Layuot do &Grid');

if (i = 2) then
CreatePopupMenuItem(PopupMenuItemName[i], '&Ver Colunas Invisíveis');

if (i = 3) then
CreatePopupMenuItem(PopupMenuItemName[i],
'&Restaurar Configuração Orignal');
end;

MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[1]));
if assigned(MyMenuitem) then
MyMenuitem.OnClick := ConfigurarLayoutGridClick;
(* A linha comentada com "//" será implementada mais tarde *)
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[2]));
if assigned(MyMenuitem) then
//MyMenuitem.OnClick := ConfigurarGridViewColumnLayout1Click;

end;
end;
end;


Terminarei este parte do artigo neste ponto, pois acredito que o conteúdo está extenso demais. No próximo artigo contruirei um aplicativo de exemplo, onde testaremos tudo o que codificamos até agora.

Por garantia vou colocar o código completo das units que trabalhamos neste artigo ...

Classe TManagerGridZn:

unit ManagerGridZn;

(*
Componente desenvolvido para gerenciar a configuração do layout de um determinado
DbGrid, de maneira personalizada por usuário. Inicialmente, a idéia era registrar
o componente na IDE.
Como usar o componente:
1) No evento OnCreate do form, instancie um objeto desta classe.
2) Em seguida, atribua a propriedade Grid, do objeto, o DbGrid, o qual
deseja-se permitir a personalização do layout. A partir daí o
componente trabalha sozinho.
So ... enjoy yourself.
GMotta Zn 06/11/2007

OBS: Cuidar para que o DbGrid, que se deseja permitir a manupulação de
layout não sofra, na sua implementação original, codificação sobre
o índice das colunas. Visto que, o componente TManagerGridZn permite
a reordenação das mesmas. *)

interface

uses
SysUtils, Classes, DBGrids, Menus, MangerGridZnData;

const
StoredFileDir = 'USERPROFILE';
AFolder = 'ZnGridProfile';
NameFileGrdMng = 'GridProfile%s.xml';
PopupMenuItemName: array [0..3] of String = ('ConfigurarGridSep',
'ConfigurarGridLayout', 'ConfigurarGridViewColumn',
'RestoreGridDefaultValues');



type
(*estrutura de enumerado que serve para indicar a classe se a associação ao
Popup Menu foi: Externa (O Grid já possuia um menu associado),
Criado internamente (O Grid não possuia um menu associado),
Inexistente (Não há associação ainda) *)
TPopupExistent = (peExternalAssociation, peInternalCreated, peInexistent);

(* Casse que define o componente “Gerenciador de Layout de Grid”*)
TManagerGridZn = class(TComponent)
private
// Atributos
FGrid: TDBGrid;// Ponto de associação
FPopupMenu: TPopupMenu; // Ponto para associação da associação (DBGrid)
FIsPopupExistent: TPopupExistent;
DmMangerGridZn: TDmMangerGridZn;

// Métodos privados
procedure SetGrid(const Value: TDBGrid);
procedure SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean = False);
function FindColumn(const AFieldName: String): TColumn;
protected
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
(* Métodos que trabalham na presistência, e recuperação das alterações
efetuadas sobre as propriedades do DBGrid. *)
function BuildFilePathComplete: String;
procedure CreateFileProfileSTO; virtual;
procedure CreateFileProfile; virtual;
procedure LoadFileProfile; virtual;
procedure StoOriginalDisignConfigutarion; virtual;
function GetDirSt: String;
(* Métodos que trabalham na montagem do popup menu *)
procedure BuildMenuPopup; virtual;
procedure CreatePopupMenuItem(const NameItem, CaptionItem: String); virtual;
procedure DestroyMenuItem; virtual;

(* métodos que darão ação ao click dos intens de menu *)
procedure ConfigurarLayoutGridClick(Sender: TObject); virtual;
procedure ConfigurarGridViewColumnLayoutClick(Sender: TObject); virtual;
procedure RestoreDefaultValuesLayoutClick(Sender: TObject); virtual;
function GetSelectedColunm: TColumn; virtual;
procedure RestoreDefaultValues;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure LoadSetup; virtual;
published
(* Interface para associação com um DBGrid *)
property Grid: TDBGrid read FGrid write SetGrid;
end;

procedure Register;

implementation

uses
Variants, GridProfileManagerZnForm, ADODb, SetupVisibilityColumnForm;

procedure Register;
begin
RegisterComponents('Estação Zn', [TManagerGridZn]);
end;

{ TManagerGridZn }

function TManagerGridZn.BuildFilePathComplete: String;
var
AuxPath: String;
begin
(* Busca o diretório das configrações pessoais *)
AuxPath := GetDirSt;
AuxPath := IncludeTrailingBackslash(AuxPath);
(*cancatena com o nome do arquivo *)
AuxPath := AuxPath + IncludeTrailingBackslash(AFolder);

if not DirectoryExists(AuxPath) then
CreateDir(AuxPath);
(* Acrescenta ao nome do arquivo a identificação do grid *)
AuxPath := AuxPath + Format(NameFileGrdMng, [FGrid.Name + FGrid.Owner.Name]);
Result := AuxPath;
(* inicializando a informação para a classe que o popup menu ainda não existe *)
FIsPopupExistent := peInexistent;
end;

procedure TManagerGridZn.BuildMenuPopup;
var
i: Integer;
MyMenuitem: TMenuItem;
begin
(* Verifco se o componente encontra-se em run time*)
if not (csDesigning in Self.ComponentState) then
begin
if Assigned(FPopupMenu) then
begin
for i := 0 to High(PopupMenuItemName) do
begin
if (FPopupMenu.Items.Count > 0) and (i = 0) then
CreatePopupMenuItem(PopupMenuItemName[i], '-');

if (i = 1) then
CreatePopupMenuItem(PopupMenuItemName[i], 'Configurar Layuot do &Grid');

if (i = 2) then
CreatePopupMenuItem(PopupMenuItemName[i], '&Ver Colunas Invisíveis');

if (i = 3) then
CreatePopupMenuItem(PopupMenuItemName[i],
'&Restaurar Configuração Orignal');
end;

MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[1]));
if assigned(MyMenuitem) then
MyMenuitem.OnClick := ConfigurarLayoutGridClick;
(* A linha comentada com "//" será implementada mais tarde *)
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[2]));
if assigned(MyMenuitem) then
MyMenuitem.OnClick := ConfigurarGridViewColumnLayoutClick;

MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[3]));
if assigned(MyMenuitem) then
MyMenuitem.OnClick := RestoreDefaultValuesLayoutClick;

end;
end;

end;

procedure TManagerGridZn.ConfigurarGridViewColumnLayoutClick(Sender: TObject);
var
FrmVisibilityConfig: TSetupVisibilityColumnFrm;
AuxPath: String;
AdsOrigConfigMemmento: TADODataSet;
begin
try
AdsOrigConfigMemmento :=
DmMangerGridZn.GetDefaultConfiguration(FGrid.Name, FGrid.Owner.Name);
FrmVisibilityConfig := TSetupVisibilityColumnFrm.Create(Self);
FrmVisibilityConfig.FieldEnabledVisibilityName :=
DmMangerGridZn.AdsOriginalConfigUpdateVisibility.FieldName;
FrmVisibilityConfig.TitleColumnFieldName :=
DmMangerGridZn.AdsOriginalConfigColumnTitleCaption.FieldName;
FrmVisibilityConfig.VisibleFieldName :=
DmMangerGridZn.AdsOriginalConfigColumnVisible.FieldName;
FrmVisibilityConfig.FieldNameFieldName :=
DmMangerGridZn.AdsOriginalConfigColumnFieldName.FieldName;
FrmVisibilityConfig.FieldNameGridName :=
DmMangerGridZn.AdsOriginalConfigGridName.FieldName;

if FrmVisibilityConfig.Execute(AdsOrigConfigMemmento,
DmMangerGridZn.AdsDados) then
begin
AuxPath := BuildFilePathComplete;
DmMangerGridZn.SaveToFile(AuxPath);
end;
finally
FrmVisibilityConfig.Free;
AdsOrigConfigMemmento.Close;
FreeAndNil(AdsOrigConfigMemmento);
LoadFileProfile;
end;

end;

procedure TManagerGridZn.ConfigurarLayoutGridClick(Sender: TObject);
var
SetupFrm: TProfileManagerZnFrm;
AuxColumn: TColumn;
AuxPath: String;
begin
AuxColumn := GetSelectedColunm;
try
SetupFrm := TProfileManagerZnFrm.Create(Self);
SetupFrm.NameFieldColumnsIndex :=
DmMangerGridZn.AdsDadosColumnsIndex.FieldName;
SetupFrm.NameFieldColumnTitleCaption :=
DmMangerGridZn.AdsDadosColumnTitleCaption.FieldName;
SetupFrm.NameFieldColumnFieldName :=
DmMangerGridZn.AdsDadosColumnFieldName.FieldName;
SetupFrm.NameFieldColumnVisible :=
DmMangerGridZn.AdsDadosColumnVisible.FieldName;
SetupFrm.NameFieldColumnReadOnly :=
DmMangerGridZn.AdsDadosColumnReadOnly.FieldName;
SetupFrm.NameFieldColumnEnabled :=
DmMangerGridZn.AdsDadosColumnEnabled.FieldName;
SetupFrm.NameFieldColumnWidth :=
DmMangerGridZn.AdsDadosColumnWidth.FieldName;

if SetupFrm.Execute(FGrid, AuxColumn, DmMangerGridZn.AdsDados,
TADODataSet(FGrid.DataSource.DataSet)) then
begin
AuxPath := BuildFilePathComplete;
DmMangerGridZn.SaveToFile(AuxPath);
LoadFileProfile;
end;
finally
SetupFrm.Free;
end;
end;

constructor TManagerGridZn.Create(AOwner: TComponent);
begin
(* Chamo a herança *)
inherited Create(AOwner);
(* verificando se o componente está em tempo de execução *)
if not(csDesigning in Self.ComponentState) then
begin
(* Instancia o datamodule *)
DmMangerGridZn := TDmMangerGridZn.Create(Self);
end;
end;

procedure TManagerGridZn.CreateFileProfile;
var
APath: String;
begin
APath := BuildFilePathComplete;
if not FileExists(APath) then
CreateFileProfileSTO;
end;

procedure TManagerGridZn.CreateFileProfileSTO;
var
AuxPath: String;
begin
AuxPath := BuildFilePathComplete;
DmMangerGridZn.SaveToFile(AuxPath);
end;

procedure TManagerGridZn.CreatePopupMenuItem(const NameItem,
CaptionItem: String);
var
MyMenuitem: TMenuItem;
begin
MyMenuitem := TMenuItem(Self.FindComponent(NameItem));
if not assigned(MyMenuitem) then
begin
MyMenuitem := TMenuItem.Create(Self);
with MyMenuitem do
begin
Name := NameItem;
Caption := CaptionItem;
end;
FPopupMenu.Items.Add(MyMenuitem);
end;
end;

destructor TManagerGridZn.Destroy;
begin
(* Se os itens do popup menu foram adicionados a
um que já existia no DBGrig preciso retirá-los. *)
if FIsPopupExistent = peExternalAssociation then
DestroymenuItem;
(*Chamando a herança *)
inherited Destroy;
end;

procedure TManagerGridZn.DestroyMenuItem;
var
MyMenuitem: TMenuItem;
i: Integer;
begin
if Assigned(FPopupMenu) then
begin
for i := 0 to High(PopupMenuItemName) do
begin
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[i]));
if assigned(MyMenuitem) then
begin
MyMenuitem.Free;
end;
end;
end;
end;

function TManagerGridZn.FindColumn(const AFieldName: String): TColumn;
var
i: Integer;
begin
Result := nil;
for i := 0 to Pred(FGrid.Columns.Count) do
begin
if FGrid.Columns[i].Field.FieldName = AFieldName then
begin
Result := FGrid.Columns[i];
Break;
end;
end;
end;

function TManagerGridZn.GetDirSt: String;
var
AuxPath: String;
begin
(* Busca path da variável de ambiente "%USERPROFILE%" *)
AuxPath := GetEnvironmentVariable(PChar(StoredFileDir));

(* Caso não encontre, busca path da variável de ambiente "%TEMP%" *)
if (AuxPath = '') then
AuxPath := GetEnvironmentVariable(PChar('TEMP'));

if (AuxPath = '') then
raise Exception.Create('Não é possível encontrar diretório para salvar configurações.');
Result := AuxPath;


end;

function TManagerGridZn.GetSelectedColunm: TColumn;
var
i: Integer;
begin
(* retorna a coluna que estiver selecionada no momento em que o usuário
clickou com o botão direito acionando o popup menu *)
Result := nil;
for i := 0 to Pred(FGrid.Columns.Count) do
begin
if FGrid.Columns[i].FieldName = FGrid.SelectedField.FieldName then
Result := FGrid.Columns[i];
if Assigned(Result) then Break;
end;


end;

procedure TManagerGridZn.LoadFileProfile;
var
APath: String;
AuxColumn: TColumn;
begin
APath := BuildFilePathComplete;

if not FileExists(APath) then
CreateFileProfileSTO;

DmMangerGridZn.LoadFromFile(APath);
if not (DmMangerGridZn.AdsDados.IsEmpty) then
begin
while not DmMangerGridZn.AdsDados.Eof do
begin
AuxColumn := FindColumn(DmMangerGridZn.AdsDadosColumnFieldName.AsString);
if assigned(AuxColumn) then
begin
AuxColumn.Visible := DmMangerGridZn.AdsDadosColumnVisible.AsBoolean;
AuxColumn.ReadOnly := DmMangerGridZn.AdsDadosColumnReadOnly.AsBoolean;
AuxColumn.Width := DmMangerGridZn.AdsDadosColumnWidth.AsInteger;
AuxColumn.Title.Caption := DmMangerGridZn.AdsDadosColumnTitleCaption.AsString;
AuxColumn.Index := DmMangerGridZn.AdsDadosColumnsIndex.AsInteger;
end;
DmMangerGridZn.AdsDados.Next;
end;
end;
end;

procedure TManagerGridZn.LoadSetup;
begin
(* permite acesso público ao método protegido *)
LoadFileProfile;
end;

procedure TManagerGridZn.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
//Remove a referência quando o componente externo é destuido
if (AComponent = FGrid) and (Operation = opRemove) then
FGrid := nil;
end;

procedure TManagerGridZn.RestoreDefaultValues;
var
AuxPath: String;
begin
AuxPath := BuildFilePathComplete;
if FileExists(AuxPath) then
DeleteFile(AuxPath);
CreateFileProfileSTO;
DmMangerGridZn.SaveOriginalConfiguration(AuxPath);

end;

procedure TManagerGridZn.RestoreDefaultValuesLayoutClick(Sender: TObject);
begin
RestoreDefaultValues;
LoadFileProfile;
end;

procedure TManagerGridZn.SetGrid(const Value: TDBGrid);
begin
if FGrid <> Value then
begin
FGrid := Value;

if FGrid <> nil then
begin
FGrid.FreeNotification(Self);
(* Cuida da associação, menu popup, associada ao DBGrid *)
SetPopupMenu(FGrid.PopupMenu);
(* Monta os itens de menu, os quais nos referimos
na especificação do componente *)
BuildMenuPopup;
if not (csDesigning in Self.ComponentState) then
begin
(* Carrega as configurações originais do Grid *)
StoOriginalDisignConfigutarion;
CreateFileProfile;
LoadFileProfile;
end;
end;
end;
end;

procedure TManagerGridZn.SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean);
begin
if (FPopupMenu = Value) and (Value <> nil) then Exit;

(* "nAllowCreate" define se o popup menu será sempre criado.
Considerando o caso de a mesma instância da classe manipular,
alternadamente, vários DBGrids que compartilham o mesmo popup menu. *)
If not Assigned(Value) or (nAllowCreate) then
begin
FPopupMenu := TPopupMenu.Create(Self);
FGrid.PopupMenu := FPopupMenu;
(* Avisa a classe que o popup menu foi criado *)
FIsPopupExistent := peInternalCreated;
end
else
begin
FPopupMenu := Value;
(* Avisa a classe que o popup menu já existia,
portanto possui itens. Ele foi associado com esses itens *)
FIsPopupExistent := peExternalAssociation;
end;

(* notifica a classe que o popup menu foi desassociado *)
if FPopupMenu <> nil then
FPopupMenu.FreeNotification(Self);
end;

procedure TManagerGridZn.StoOriginalDisignConfigutarion;
var
i: Integer;
AuxValues: Variant;
AuxFieldsLocate: String;
begin
(* Filtra pelo Nome do Grid para que possa persistir as informações default
de vário DBGrids. Dessa forma, o componente pode, dinamicamente, trabalhar
com vários Dbgrids, alernadamente, na mesma instância sem perder as
config originais da cada um deles. GM 07/11/2007*)

try
if not DmMangerGridZn.AdsOriginalConfig.IsEmpty then
begin
DmMangerGridZn.ApplyFilterConfigGrid(FGrid.Name, FGrid.Owner.Name);
end;

(* Guarda a configuração do grid feita em design *)

for i := 0 to Pred(FGrid.Columns.Count) do
begin
if Assigned(FGrid.Columns[i].Field) then
begin
(* recuperando valores para o locate *)
AuxValues := VarArrayOf([QuotedStr(FGrid.Name + FGrid.Owner.Name),
QuotedStr(FGrid.Columns[i].Field.FieldName)]);

AuxFieldsLocate := DmMangerGridZn.AdsOriginalConfigGridName.FieldName +';' +
DmMangerGridZn.AdsOriginalConfigColumnFieldName.FieldName;
(* Garante que não haverão linas repetidas para a chave Grid/Coluna *)
if not DmMangerGridZn.AdsOriginalConfig.Locate(AuxFieldsLocate, AuxValues,
[]) then
begin
DmMangerGridZn.AdsOriginalConfig.Append;
DmMangerGridZn.AdsOriginalConfigGridName.AsString := FGrid.Name +
FGrid.Owner.Name;
DmMangerGridZn.AdsOriginalConfigColumnsIndex.AsInteger := i;
DmMangerGridZn.AdsOriginalConfigColumnTitleCaption.AsString :=
FGrid.Columns[i].Title.Caption;
DmMangerGridZn.AdsOriginalConfigColumnFieldName.AsString :=
FGrid.Columns[i].Field.FieldName;
DmMangerGridZn.AdsOriginalConfigColumnVisible.AsBoolean :=
FGrid.Columns[i].Visible;
DmMangerGridZn.AdsOriginalConfigColumnReadOnly.AsBoolean :=
FGrid.Columns[i].ReadOnly;
DmMangerGridZn.AdsOriginalConfigColumnEnabled.AsBoolean :=
FGrid.Columns[i].ReadOnly;
DmMangerGridZn.AdsOriginalConfigColumnWidth.AsInteger :=
FGrid.Columns[i].Width;
DmMangerGridZn.AdsOriginalConfigUpdateVisibility.AsBoolean :=
FGrid.Columns[i].Visible;
DmMangerGridZn.AdsOriginalConfig.Post;
end;
end;
end;
finally
DmMangerGridZn.CalcelFilterConfigGrid;
end;
end;

end.


Datamodule TDmMangerGridZn:

unit MangerGridZnData;

interface

uses
SysUtils, Classes, DB, ADODB;

type
TDmMangerGridZn = class(TDataModule)
AdsDados: TADODataSet;
AdsDadosColumnsIndex: TIntegerField;
AdsDadosColumnTitleCaption: TStringField;
AdsDadosColumnFieldName: TStringField;
AdsDadosColumnVisible: TBooleanField;
AdsDadosColumnReadOnly: TBooleanField;
AdsDadosColumnEnabled: TBooleanField;
AdsDadosColumnWidth: TIntegerField;
AdsOriginalConfig: TADODataSet;
AdsOriginalConfigColumnsIndex: TIntegerField;
AdsOriginalConfigColumnTitleCaption: TStringField;
AdsOriginalConfigColumnFieldName: TStringField;
AdsOriginalConfigColumnVisible: TBooleanField;
AdsOriginalConfigColumnReadOnly: TBooleanField;
AdsOriginalConfigColumnEnabled: TBooleanField;
AdsOriginalConfigColumnWidth: TIntegerField;
AdsOriginalConfigUpdateVisibility: TBooleanField;
AdsOriginalConfigGridName: TStringField;
procedure DataModuleCreate(Sender: TObject);
private
procedure ClonaDataset(QryReferencia, QryClone:TADODataSet;
const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
public
procedure SaveToFile(const APath: String);
procedure LoadFromFile(const APath: String);
function GetDefaultConfiguration(const GridName, GridOwnerName: String): TADODataSet;
procedure ApplyFilterConfigGrid(const GridName, GridOwnerName: String);
procedure SaveOriginalConfiguration(const APath: String);
procedure CalcelFilterConfigGrid;
end;


implementation

{$R *.dfm}

const SaveFormat = pfXML;

{ TDmMangerGridZn }

procedure TDmMangerGridZn.ApplyFilterConfigGrid(const GridName,
GridOwnerName: String);
begin
AdsOriginalConfig.Filter := AdsOriginalConfigGridName.FieldName + ' = ' + QuotedStr(GridName + GridOwnerName);
AdsOriginalConfig.Filtered := True;
end;

procedure TDmMangerGridZn.CalcelFilterConfigGrid;
begin
AdsOriginalConfig.Filter := '';
AdsOriginalConfig.Filtered := False;
end;

procedure TDmMangerGridZn.ClonaDataset(QryReferencia, QryClone: TADODataSet;
const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
var
i: Integer;
ClonaKind, ClonaVisible: Boolean;
AuxField: TField;
AbookMark: String;
begin
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
ClonaVisible := not nAllowCloneVisible;
if not ClonaVisible then
ClonaVisible := (QryReferencia.Fields[i].Visible);

ClonaKind := nAllowCloneAllKind;
if not ClonaKind then
ClonaKind := (QryReferencia.Fields[i].FieldKind = fkData);

if ClonaKind and ClonaVisible then
begin
QryClone.FieldDefs.Add(QryReferencia.Fields[i].FieldName,
QryReferencia.Fields[i].DataType,
QryReferencia.Fields[i].Size);
end;
end;

QryClone.CreateDataSet;
try
QryReferencia.DisableControls;
AbookMark := QryReferencia.Bookmark;
QryReferencia.First;
while not QryReferencia.Eof do
begin
QryClone.Append;
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
AuxField := QryClone.FindField(QryReferencia.Fields[i].FieldName);
if assigned(AuxField) then
begin
AuxField.Value := QryReferencia.Fields[i].Value;
AuxField.DisplayLabel := QryReferencia.Fields[i].DisplayLabel;
end;
end;
QryClone.Post;
QryReferencia.Next;
end;

finally
QryReferencia.Bookmark := AbookMark;
QryReferencia.EnableControls;
end;
end;

procedure TDmMangerGridZn.DataModuleCreate(Sender: TObject);
begin
AdsDados.CreateDataSet;
AdsOriginalConfig.CreateDataSet;
end;

function TDmMangerGridZn.GetDefaultConfiguration(const GridName,
GridOwnerName: String): TADODataSet;
begin
(* retorna os dados resultantes do filtro aplicado pelo o nome do grid.
Dessa forma o componente pode dinamicamente trabalhar com vários Dbgrids,
alernadamente, na mesma instância sem preder as config originais da cada um deles. GM 07/11/2007*)
try
ApplyFilterConfigGrid(GridName, GridOwnerName);
Result := TADODataSet.Create(Self);
ClonaDataset(AdsOriginalConfig, Result, False, False);
finally
CalcelFilterConfigGrid;
end;

end;

procedure TDmMangerGridZn.LoadFromFile(const APath: String);
begin
if AdsDados.Active then
AdsDados.LoadFromFile(APath);

end;

procedure TDmMangerGridZn.SaveOriginalConfiguration(const APath: String);
begin
if (AdsOriginalConfig.Active) and not (AdsOriginalConfig.IsEmpty) then
begin
AdsDados.First;
while not AdsDados.Eof do
AdsDados.Delete;

AdsOriginalConfig.First;
while not AdsOriginalConfig.Eof do
begin
AdsDados.Append;
AdsDadosColumnsIndex.AsInteger := AdsOriginalConfigColumnsIndex.AsInteger;
AdsDadosColumnTitleCaption.AsString := AdsOriginalConfigColumnTitleCaption.AsString;
AdsDadosColumnFieldName.AsString := AdsOriginalConfigColumnFieldName.AsString;
AdsDadosColumnVisible.AsBoolean := AdsOriginalConfigColumnVisible.AsBoolean;
AdsDadosColumnVisible.AsBoolean := AdsOriginalConfigColumnVisible.AsBoolean;
AdsDadosColumnReadOnly.AsBoolean := AdsOriginalConfigColumnReadOnly.AsBoolean;
AdsDadosColumnEnabled.AsBoolean := AdsOriginalConfigColumnEnabled.AsBoolean;
AdsDadosColumnWidth.AsInteger := AdsOriginalConfigColumnWidth.AsInteger;
AdsDados.Post;
AdsOriginalConfig.Next;
end;
end;


AdsDados.SaveToFile(APath, SaveFormat);
end;

procedure TDmMangerGridZn.SaveToFile(const APath: String);
begin
(*SaveFormat é uma constante que define a esteção do arquivo
gerado pelo AdsDados, no caso ".xml"*)
if AdsDados.Active then
AdsDados.SaveToFile(APath, SaveFormat);
end;

end.



Form TProfileManagerZnFrm:

unit GridProfileManagerZnForm;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DBCtrls, Mask, Grids, DBGrids, Buttons, ExtCtrls, DB, ADODB;

type
TProfileManagerZnFrm = class(TForm)
PnlBtn: TPanel;
BtnSave: TBitBtn;
btnCancel: TBitBtn;
GrpBxGrid: TGroupBox;
GrdConfigColumn: TDBGrid;
pnlTop: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
CheckBxVisiblw: TDBCheckBox;
EdtTitulo: TDBEdit;
EdtLardura: TDBEdit;
CmbOrdem: TDBLookupComboBox;
GrpBXPrevw: TGroupBox;
ChkBxPreview: TCheckBox;
AdsSetupColumn: TADODataSet;
dsSetupColumn: TDataSource;
AdsLookupAux: TADODataSet;
AdsLookupAuxIndexValue: TIntegerField;
AdsLookupAuxListValue: TStringField;
dsLookupAux: TDataSource;
AdsLstDados: TADODataSet;
dsLstDados: TDataSource;
procedure FormShow(Sender: TObject);
procedure ChkBxPreviewClick(Sender: TObject);
procedure CmbOrdemClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure BtnSaveClick(Sender: TObject);
(*Evento criado para poser dinâmicamente
ser atribuido ao evento OnChange de um TField *)
procedure FieldValueChange(Sender: TField);
private
(* Atributos *)
FNameFieldColumnsIndex: String;
FNameFieldColumnTitleCaption: String;
FNameFieldColumnFieldName: String;
FNameFieldColumnVisible: String;
FNameFieldColumnReadOnly: String;
FNameFieldColumnEnabled: String;
FNameFieldColumnWidth: String;

(* Os Atributos abaixo apontarão para a coluna que precisarei manipular
para exibir ao usuário as operações que ele estará realizando*)
FColumnConfigurated: TColumn;
FColumnPreview: TColumn;

(* Métodos privados *)
function FindColumn(const AFieldName: String): TColumn;
procedure BuildLookupCombo(AGrid: TDBGrid);
procedure SetDados(AColumn: TColumn);
procedure SetupGridField;
procedure SetDadosPreview;
procedure SetupFieldChange;
procedure ClonaDataset(QryReferencia, QryClone:TADODataSet;
const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
procedure SortColumns;
public
function Execute(AGrid: TDBGrid; AColumn: TColumn; AdsProfile,
AdsDet: TADODataSet): Boolean;
property NameFieldColumnsIndex: String read FNameFieldColumnsIndex
write FNameFieldColumnsIndex;
property NameFieldColumnTitleCaption: String read FNameFieldColumnTitleCaption
write FNameFieldColumnTitleCaption;
property NameFieldColumnFieldName: String read FNameFieldColumnFieldName
write FNameFieldColumnFieldName;
property NameFieldColumnVisible: String read FNameFieldColumnVisible
write FNameFieldColumnVisible;
property NameFieldColumnReadOnly: String read FNameFieldColumnReadOnly
write FNameFieldColumnReadOnly;
property NameFieldColumnEnabled: String read FNameFieldColumnEnabled
write FNameFieldColumnEnabled;
property NameFieldColumnWidth: String read FNameFieldColumnWidth
write FNameFieldColumnWidth;
end;

implementation

{$R *.dfm}

{ TProfileManagerZnFrm }

procedure TProfileManagerZnFrm.btnCancelClick(Sender: TObject);
begin
if dsSetupColumn.DataSet.State in [dsEdit, dsInsert] then
dsSetupColumn.DataSet.Cancel;
end;

procedure TProfileManagerZnFrm.BtnSaveClick(Sender: TObject);
begin
if dsSetupColumn.DataSet.State in [dsEdit, dsInsert] then
dsSetupColumn.DataSet.Post;
SortColumns;
end;

procedure TProfileManagerZnFrm.BuildLookupCombo(AGrid: TDBGrid);
const
AOrdem = '%dº';
var
i: Integer;
begin
// o Parâmtro AGrid aponta para o Grid que está associado a classe TManagerGridZn
(*Monta o Combo box com as ordens das colunas presentes no AGrid,
adicionando ao dataset que esta associado ao ComboBox. *)
for i := 0 to Pred(AGrid.Columns.Count) do
AdsLookupAux.AppendRecord([i, Format(AOrdem, [i + 1])]);
//AdsSetupColumn.FieldByName(FNameFieldColumnsIndex).OnChange := nil;
(*AdsSetupColumn.Locate(FNameFieldColumnsIndex, FColumnConfigurated.Index, []); *)

end;

procedure TProfileManagerZnFrm.ChkBxPreviewClick(Sender: TObject);
begin
SetDadosPreview;
SetupGridField;
end;

procedure TProfileManagerZnFrm.ClonaDataset(QryReferencia,
QryClone: TADODataSet; const nAllowCloneAllKind, nAllowCloneVisible: Boolean);
var
i: Integer;
ClonaKind, ClonaVisible: Boolean;
AuxField: TField;
AbookMark: String;
begin
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
ClonaVisible := not nAllowCloneVisible;
if not ClonaVisible then
ClonaVisible := (QryReferencia.Fields[i].Visible);

ClonaKind := nAllowCloneAllKind;
if not ClonaKind then
ClonaKind := (QryReferencia.Fields[i].FieldKind = fkData);

if ClonaKind and ClonaVisible then
begin
QryClone.FieldDefs.Add(QryReferencia.Fields[i].FieldName,
QryReferencia.Fields[i].DataType,
QryReferencia.Fields[i].Size);
end;
end;

QryClone.CreateDataSet;
try
QryReferencia.DisableControls;
AbookMark := QryReferencia.Bookmark;
QryReferencia.First;
while not QryReferencia.Eof do
begin
QryClone.Append;
for i := 0 to Pred(QryReferencia.Fields.Count) do
begin
AuxField := QryClone.FindField(QryReferencia.Fields[i].FieldName);
if assigned(AuxField) then
begin
AuxField.Value := QryReferencia.Fields[i].Value;
AuxField.DisplayLabel := QryReferencia.Fields[i].DisplayLabel;
end;
end;
QryClone.Post;
QryReferencia.Next;
end;

finally
QryReferencia.Bookmark := AbookMark;
QryReferencia.EnableControls;
end;
end;

procedure TProfileManagerZnFrm.CmbOrdemClick(Sender: TObject);
begin
ChkBxPreview.Checked := True;
SetDadosPreview;
SetupGridField;
end;

function TProfileManagerZnFrm.Execute(AGrid: TDBGrid; AColumn: TColumn;
AdsProfile, AdsDet: TADODataSet): Boolean;
begin
(* Configurando a exibição do Form *)
Self.BorderStyle := bsDialog;
Self.Position := poScreenCenter;
Self.FormStyle := fsNormal;
(* Monda a Combo Ordem *)
BuildLookupCombo(AGrid);

(*Copiando os dados de um dataset para o outro *)
ClonaDataset(AdsDet, AdsLstDados, True, True);
AdsSetupColumn.Clone(AdsProfile);

(*Guarda a referência da colun que esta sendo alterada *)
FColumnConfigurated := AColumn;
FColumnConfigurated.Field := AColumn.Field;


(* Copiando o Grid associado a TManagerGridZn *)
GrdConfigColumn.Columns.Assign(AGrid.Columns);

(* Exibindo pro usuário a coluna que está sendo alterada *)
GrpBxGrid.Caption := Format(GrpBxGrid.Caption, [AColumn.Title.Caption]);
Self.Caption := Self.Caption + ': ' + AColumn.Title.Caption;
if not AdsSetupColumn.Locate(FNameFieldColumnFieldName, AColumn.Field.FieldName, []) then
begin
dsSetupColumn.DataSet.Append;
SetDados(FColumnConfigurated);
end;

SetupFieldChange;
Self.ShowModal;
Result := (Self.ModalResult = mrOk)

end;

procedure TProfileManagerZnFrm.FieldValueChange(Sender: TField);
begin
(* Assim eu posso em tempo real exibir para o usuário
a alteração da propriedade de ele efetuou na coluna do Grid. *)
SetDadosPreview;
end;

function TProfileManagerZnFrm.FindColumn(const AFieldName: String): TColumn;
var
i: Integer;
begin
Result := nil;
for i := 0 to Pred(GrdConfigColumn.Columns.Count) do
begin
if Assigned(GrdConfigColumn.Columns[i].Field) then
if GrdConfigColumn.Columns[i].Field.FieldName = AFieldName then
begin
Result := GrdConfigColumn.Columns[i];
Break;
end;
end;
end;

procedure TProfileManagerZnFrm.FormCreate(Sender: TObject);
begin
(*Inicializa o CheckBox do Preview *)
ChkBxPreview.Checked := False;
(* Ativa o ADODataset *)
AdsLookupAux.CreateDataSet;
end;

procedure TProfileManagerZnFrm.FormShow(Sender: TObject);
begin
(*Quando o form for exibido os TFields do AdsSetupColumn ja terão sido
criados, portanto poderei configurá-los com o evento genérico OnChange *)
SetupGridField;
end;

procedure TProfileManagerZnFrm.SetDados(AColumn: TColumn);
begin
(* Atribui ao Dataset que armazena as iformações sobre a coluna,
os valores correntes na coluna cujo o usuário deseja configurar.
Esta Dataset está ponteirado com o AdsDados presente no datamodule. *)
AdsSetupColumn.FieldByName(NameFieldColumnsIndex).AsInteger :=
AColumn.Index;
AdsSetupColumn.FieldByName(NameFieldColumnTitleCaption).AsString :=
AColumn.Title.Caption;
AdsSetupColumn.FieldByName(NameFieldColumnFieldName).AsString :=
AColumn.FieldName;
AdsSetupColumn.FieldByName(NameFieldColumnVisible).AsBoolean := AColumn.Visible;
AdsSetupColumn.FieldByName(NameFieldColumnWidth).AsInteger := AColumn.Width;

end;

procedure TProfileManagerZnFrm.SetDadosPreview;
begin
GrdConfigColumn.Columns.BeginUpdate;
FColumnPreview := FindColumn(FColumnConfigurated.Field.FieldName);
if not Assigned(FColumnPreview) then Exit;

if ChkBxPreview.Checked then
begin
FColumnPreview.Visible :=
AdsSetupColumn.FieldByName(NameFieldColumnVisible).AsBoolean;
FColumnPreview.ReadOnly :=
AdsSetupColumn.FieldByName(NameFieldColumnReadOnly).AsBoolean;
FColumnPreview.Width :=
AdsSetupColumn.FieldByName(NameFieldColumnWidth).AsInteger;
FColumnPreview.Title.
Caption := AdsSetupColumn.FieldByName(NameFieldColumnTitleCaption).AsString;
FColumnPreview.Index :=
AdsSetupColumn.FieldByName(NameFieldColumnsIndex).AsInteger;
end
else
FColumnPreview.Assign(FColumnConfigurated);

GrdConfigColumn.Columns.EndUpdate;

end;

procedure TProfileManagerZnFrm.SetupFieldChange;
var
i: Integer;
begin
(* este método procura qualquer tfield no AdsSetupColumn,
para atribuir ao seu evento OnChange o método genérico que
criamos anteriormente.*)
for i := 0 to Pred(AdsSetupColumn.Fields.Count) do
begin
AdsSetupColumn.Fields[i].OnChange := FieldValueChange;
end;
end;

procedure TProfileManagerZnFrm.SetupGridField;
var
AuxField: TField;
AuxColumn: TColumn;
begin
(*Este método marca de Highlight a coluna que esta sendo configurada
pelo usuário. *)
AuxColumn := FindColumn(FColumnConfigurated.Field.FieldName);

if Assigned(AuxColumn) then
begin
AuxColumn.Title.Color := clHighlight;
AuxColumn.Title.Font.Color := clHighlightText;
AuxColumn.Color := clHighlight;
AuxColumn.Font.Color := clHighlightText;
end;

AuxField := AdsLstDados.FindField(FColumnConfigurated.Field.FieldName);
(*Seta o foco*)
if Assigned(AuxField) then
begin
AuxField.FocusControl;
GrdConfigColumn.SelectedField := AuxField;
end;

end;

procedure TProfileManagerZnFrm.SortColumns;
var
i: Integer;
begin

for i := 0 to Pred(GrdConfigColumn.Columns.Count) do
begin

{ShowMessage(GrdConfigColumn.Columns[i].Field.FieldName + ' - '+
Format('Indice %d', [GrdConfigColumn.Columns[i].Index])); }
if AdsSetupColumn.Locate(FNameFieldColumnFieldName,
GrdConfigColumn.Columns[i].Field.FieldName, []) then
begin
AdsSetupColumn.FieldByName(FNameFieldColumnsIndex).OnChange := nil;
AdsSetupColumn.Edit;
AdsSetupColumn.FieldByName(FNameFieldColumnsIndex).AsInteger :=
GrdConfigColumn.Columns[i].Index;
end
else
begin
AdsSetupColumn.Append;
SetDados(GrdConfigColumn.Columns[i]);
end;
if AdsSetupColumn.State in [dsInsert, dsEdit] then
AdsSetupColumn.Post;
end;


end;

end.

Artigo completo (View Full Post)

 
BlogBlogs.Com.Br