terça-feira, 26 de fevereiro de 2008

Construção de Componentes - VI

Seguindo a mesma linha do último exemplo de criação de componentes, para consolidar os pontos discutidos, construiremos outro exemplo bem semelhante ao anterior mudando um pouco a abordagem quanto a funcionalidade do componente.
O principal objetivo deste será oferecer um solução abstrata para uma funcionalidade tipo “Localizar” (tipo um “find”) para um conjunto de registros listados num DBGrid. Basta clickar com o botão direito do mouse na coluna a qual se deseja fazer a busca, o componente dinamicamente cria um menu popup, com um item cujo a descrição seja “Focalizar [título da coluna]”. Efetuado o click sobe o item de menu, o sistema exibe uma janela de diálogo solicitando digitação do valor para busca e em seguida efetua a busca. Localizando no Grid o dado desejado.


Descrição da funcionalidade:

  1. Usuário clicka com o botão direito do mouse sobre a coluna do Grid, a qual deseja localizar um registro.
  2. Sistema exibe o popup menu com um item de localizar.
  3. Usuário clicka na opção de localizar.
  4. Sistema Exibe tela de diálogo para usuário digitar o valor a ser localizado.
  5. Usuário digita o valor e clicka em “Ok”, confirmando a solicitação de localizar.
  6. Sistema localiza, focando a célula onde se encontra o registro desejado.
Construindo o Componente

Se você não sabe iniciar, no Delphi, a construção de um componente siga as instruções dos artigos anteriores.

Codifique conforme código abaixo a classe TFinderDataZN.


unit FinderDataZN;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, DbGrids, ADODb, Db, Menus;

type
(* Para indicar a Classe se o Popup Menu foi outo-criado, ou já estava associado
ao DbGrid que "foi" associado ao TFinderDataZN *)
TPopupExistent = (peExternalAssociation, peInternalCreated, peInexistent);

TFinderDataZN = class(TComponent)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
Property ReturnValueStr: String read FReturnValueStr;
Property ReturnValueNum: Double read FReturnValueNum;
property Grid: TDBGrid read FGrid write SetGrid;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('EstacaoZN', [TFinderDataZN]);
end;

end.


Codificando a principal propriedade “SetGrid”. Isto porque, essa propriedade vai preparar o Grid e configurar a classe para seu principal fim. Observe que no método “BuildMenuPopup” vou atribuir um evento ao OnClick do item de menu dinamicamente, da mesma forma que fizemos no artigo anterior, contudo neste momento vou deixar essa atribuição comentada. Pois, pretendo implementar a procedure “LocalizarClick” mais adiante. Preste a atenção na linha “/MyMenuitem.OnClick := LocalizarClick”, do método “BuildMenuPopup”, ela estará comentada. O mesmo serve para o método “SetPopupMenu”, visto que o evento PopupMenuPopup, o qual atribuirei ao evento “OnPopup” do Popup Menu associado ao Grid.


unit FinderDataZN;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, DbGrids, ADODb, Db, Menus;

type
(* Para indicar a Classe se o Popup Menu foi outo-criado, ou já estava associado
ao DbGrid que "foi" associado ao TFinderDataZN *)
TPopupExistent = (peExternalAssociation, peInternalCreated, peInexistent);

TFinderDataZN = class(TComponent)
private
FReturnValueNum: Double;
FReturnValueStr: String;
FGrid: TDBGrid;
FADataSet: TCustomADODataSet;
FPopupMenu: TPopupMenu;
FIsPopupExistent: TPopupExistent;
FSelectedColumn: TColumn;
FAFieldSearch: TField; // Será usado para setar foco

procedure SetGrid(const Value: TDBGrid);
procedure SetADataSet(const Value: TCustomADODataSet);
procedure SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean = False);

protected
procedure CreatePopupMenuItem(const NameItem,
CaptionItem: String); virtual;
procedure BuildMenuPopup; virtual;
procedure SetSelectedColumn(const Value: TColumn); virtual;
function GetSelectedColunm: TColumn; virtual;
public
{ Public declarations }
published
Property ReturnValueStr: String read FReturnValueStr;
Property ReturnValueNum: Double read FReturnValueNum;
property Grid: TDBGrid read FGrid write SetGrid;
end;

procedure Register;

implementation

const
PopupMenuItemName: array [0..1] of String = ('ConfigurarGridSep',
'fdLocalizar');
AMenuItemCaption = 'Localizar %s';

procedure Register;
begin
RegisterComponents('EstacaoZN', [TFinderDataZN]);
end;

{ TFinderDataZN }

procedure TFinderDataZN.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],
Format(AMenuItemCaption, [FGrid.Columns[0].Title.Caption]));
end;

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

end;
end;
end;

procedure TFinderDataZN.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;

function TFinderDataZN.GetSelectedColunm: TColumn;
var
i: Integer;
AuxField: TField;
begin
Result := nil;
for i := 0 to Pred(FGrid.Columns.Count) do
begin
AuxField := FGrid.Columns[i].Field;
if Assigned(AuxField) then
if AuxField.FieldName = FGrid.SelectedField.FieldName then
Result := FGrid.Columns[i];
if Assigned(Result) then Break;
end;

end;

procedure TFinderDataZN.SetADataSet(const Value: TCustomADODataSet);
begin
FADataSet := Value;
end;

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

if FGrid <> nil then
begin
FGrid.FreeNotification(Self);
SetSelectedColumn(GetSelectedColunm);
FADataSet := TCustomADODataSet(FGrid.DataSource.DataSet);
(* 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;

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

If not Assigned(Value) or (nAllowCreate) then
begin
FPopupMenu := TPopupMenu.Create(Self);
FGrid.PopupMenu := FPopupMenu;
FIsPopupExistent := peInternalCreated;
end
else
begin
FPopupMenu := Value;
FIsPopupExistent := peExternalAssociation;
end;

if FPopupMenu <> nil then
begin
FPopupMenu.FreeNotification(Self);
//FPopupMenu.OnPopup := PopupMenuPopup;
end;
end;

procedure TFinderDataZN.SetSelectedColumn(const Value: TColumn);
begin
(* Armazena a coluna do Grid aqual foi clickada com o
botão direito do mouse, para capturar informações como: Titúlo e etc.. *)
FSelectedColumn := Value;

if FSelectedColumn <> nil then
FAFieldSearch := FSelectedColumn.Field;
end;

end.




Codificando as procedures que serão atribuídas aos eventos :
“procedure LocalizarClick(Sender: TObject)” para efetuará a localização do registro desejado.

“procedure PopupMenuPopup(Sender: TObject) ”, para criar o caption, o título do item de menu, referente ao registro o qual será localizado.

protected
procedure CreatePopupMenuItem(const NameItem,
CaptionItem: String); virtual;
procedure BuildMenuPopup; virtual;
procedure SetSelectedColumn(const Value: TColumn); virtual;
function GetSelectedColunm: TColumn; virtual;
(* Eventos que serão atribuidos dinamicamente *)
procedure LocalizarClick(Sender: TObject); virtual;
procedure PopupMenuPopup(Sender: TObject);


Primeiro a implementação da nova procedure que vai setar o título o item de menu dinamicamente de acordo com o título da coluna do Grid:



procedure TFinderDataZN.PopupMenuPopup(Sender: TObject);
var
MyMenuitem: TMenuItem;
begin
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[1]));
if assigned(MyMenuitem) then
begin
SetSelectedColumn(GetSelectedColunm);
MyMenuitem.Caption := Format(AMenuItemCaption, [FSelectedColumn.Title.Caption]);
end;
end;


A funcionalidade de Localizar precisa exibir uma janela (tela) para que o usuário digite o valor desejado. Logo, precisamos, antes de codificar criarmos um novo form. Adicione a classe um novo Form, propriedade name = “FindDataDlgFormZN”, salve a unit com o nome de “FindDataDlgFrmZN”. Segue o código do dfm do form:

object FindDataDlgFormZN: TFindDataDlgFormZN
Left = 192
Top = 107
Width = 189
Height = 138
Caption = 'Localizar %s'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 12
Top = 12
Width = 126
Height = 13
Caption = 'Digite o Código do Serviço'
end
object EdtDado: TEdit
Left = 12
Top = 33
Width = 159
Height = 21
TabOrder = 0
end
object PnlBottom: TPanel
Left = 0
Top = 67
Width = 181
Height = 44
Align = alBottom
BevelOuter = bvNone
TabOrder = 1
object BtnCancel: TBitBtn
Left = 100
Top = 12
Width = 75
Height = 25
TabOrder = 0
Kind = bkCancel
end
object BtnOk: TBitBtn
Left = 12
Top = 12
Width = 75
Height = 25
TabOrder = 1
Kind = bkOK
end
end
end

No form codifique o evento OnCloseQuery e um método público “Execute” conforme exemplificado abaixo:

unit FindDataDlgFrmZN;

interface

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

type
TFindDataDlgFormZN = class(TForm)
Label1: TLabel;
EdtDado: TEdit;
PnlBottom: TPanel;
BtnCancel: TBitBtn;
BtnOk: TBitBtn;
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private declarations }
public
function Execute: Boolean;
end;

var
FindDataDlgFormZN: TFindDataDlgFormZN;

implementation

{$R *.dfm}

procedure TFindDataDlgFormZN.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
begin
if (Self.ModalResult = MrOk) and (Trim(EdtDado.Text) = '') then
ModalResult := mrCancel;
end;

function TFindDataDlgFormZN.Execute: Boolean;
begin
Self.BorderStyle := bsDialog;
Self.FormStyle := fsNormal;
Self.Position := poMainFormCenter;

Result := (Self.ShowModal = mrOk);

end;

end.


Implementado o procedimento que vai gerenciar a localização do registro “function InputSearchValue”.

protected
procedure CreatePopupMenuItem(const NameItem,
CaptionItem: String); virtual;
procedure BuildMenuPopup; virtual;
procedure SetSelectedColumn(const Value: TColumn); virtual;
function GetSelectedColunm: TColumn; virtual;
(* Eventos que serão atribuidos dinamicamente *)
procedure LocalizarClick(Sender: TObject); virtual;
procedure PopupMenuPopup(Sender: TObject);
(*método que vai gerenciar a localização do registro *)
function InputSearchValue: String; virtual;
public
{ Public declarations }
published
Property ReturnValueStr: String read FReturnValueStr;
Property ReturnValueNum: Double read FReturnValueNum;
property Grid: TDBGrid read FGrid write SetGrid;
end;


Adicione na clausula “uses” da seção “implamentation” a unit da classe “TFinderDataZN”, a unit do form que acabamos de criar. Na seção “private” defina um novo campo FAFieldSearch: TField;

procedure Register;

implementation

uses FindDataDlgFrmZN;

const
PopupMenuItemName: array [0..1] of String = ('ConfigurarGridSep',
'fdLocalizar');
AMenuItemCaption = 'Localizar %s';



var
FindDataDlgForm: TFindDataDlgForm;
AuxCaption: String;
begin

if (FADataSet.IsEmpty) then Exit;


try
FindDataDlgForm := TFindDataDlgForm.Create(Self);

FindDataDlgForm.Caption := Format(FindDataDlgForm.Caption,
[FSelectedColumn.Title.Caption]);

FindDataDlgForm.Label1.Caption := FSelectedColumn.Title.Caption;

if FindDataDlgForm.Execute then
begin
Result := FindDataDlgForm.EdtDado.Text;
if FADataSet.Locate(FSelectedColumn.Field.FieldName, FindDataDlgForm.EdtDado.Text, []) then
begin
FAFieldSearch.FocusControl;
if FGrid.CanFocus then
FGrid.SetFocus
end;
end;

finally
FindDataDlgForm.Free;
end;


Agora, para finalizar , o método “LocalizarClick”.


procedure TFinderDataZN.LocalizarClick(Sender: TObject);
var
AValue: String;
AValueNum: Double;
begin

try
AValue := InputSearchValue;
except
on Exception do
AValue := '';
end;

end;


Não esqueça de descomentar as linhas dos métodos “BuildMenuPopup” e “SetPopupMenu”.

Segue o código completo do componente:

unit FinderDataZN;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, DbGrids, ADODb, Db, Menus;

type
(* Para indicar a Classe se o Popup Menu foi outo-criado, ou já estava associado
ao DbGrid que "foi" associado ao TFinderDataZN *)
TPopupExistent = (peExternalAssociation, peInternalCreated, peInexistent);

TFinderDataZN = class(TComponent)
private
FReturnValueNum: Double;
FReturnValueStr: String;
FGrid: TDBGrid;
FADataSet: TCustomADODataSet;
FPopupMenu: TPopupMenu;
FIsPopupExistent: TPopupExistent;
FSelectedColumn: TColumn;
FAFieldSearch: TField;

procedure SetGrid(const Value: TDBGrid);
procedure SetADataSet(const Value: TCustomADODataSet);
procedure SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean = False);

protected
procedure CreatePopupMenuItem(const NameItem,
CaptionItem: String); virtual;
procedure BuildMenuPopup; virtual;
procedure SetSelectedColumn(const Value: TColumn); virtual;
function GetSelectedColunm: TColumn; virtual;
(* Eventos que serão atribuidos dinamicamente *)
procedure LocalizarClick(Sender: TObject); virtual;
procedure PopupMenuPopup(Sender: TObject);
(*método que vai gerenciar a localização do registro *)
function InputSearchValue: String; virtual;
public
{ Public declarations }
published
Property ReturnValueStr: String read FReturnValueStr;
Property ReturnValueNum: Double read FReturnValueNum;
property Grid: TDBGrid read FGrid write SetGrid;
end;

procedure Register;

implementation

uses FindDataDlgFrmZN;

const
PopupMenuItemName: array [0..1] of String = ('ConfigurarGridSep',
'fdLocalizar');
AMenuItemCaption = 'Localizar %s';

procedure Register;
begin
RegisterComponents('EstacaoZN', [TFinderDataZN]);
end;

{ TFinderDataZN }

procedure TFinderDataZN.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],
Format(AMenuItemCaption, [FGrid.Columns[0].Title.Caption]));
end;

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

end;
end;
end;

procedure TFinderDataZN.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;

function TFinderDataZN.GetSelectedColunm: TColumn;
var
i: Integer;
AuxField: TField;
begin
Result := nil;
for i := 0 to Pred(FGrid.Columns.Count) do
begin
AuxField := FGrid.Columns[i].Field;
if Assigned(AuxField) then
if AuxField.FieldName = FGrid.SelectedField.FieldName then
Result := FGrid.Columns[i];
if Assigned(Result) then Break;
end;

end;

function TFinderDataZN.InputSearchValue: String;
var
FindDataDlgForm: TFindDataDlgFormZN;
AuxCaption: String;
begin

if (FADataSet.IsEmpty) then Exit;


try
FindDataDlgForm := TFindDataDlgFormZN.Create(Self);

FindDataDlgForm.Caption := Format(FindDataDlgForm.Caption,
[FSelectedColumn.Title.Caption]);

FindDataDlgForm.Label1.Caption := FSelectedColumn.Title.Caption;

if FindDataDlgForm.Execute then
begin
Result := FindDataDlgForm.EdtDado.Text;
if FADataSet.Locate(FSelectedColumn.Field.FieldName, FindDataDlgForm.EdtDado.Text, []) then
begin
FAFieldSearch.FocusControl;
if FGrid.CanFocus then
FGrid.SetFocus
end;
end;

finally
FindDataDlgForm.Free;
end;
end;

procedure TFinderDataZN.LocalizarClick(Sender: TObject);
var
AValue: String;
AValueNum: Double;
begin

try
AValue := InputSearchValue;
except
on Exception do
AValue := '';
end;

end;

procedure TFinderDataZN.PopupMenuPopup(Sender: TObject);
var
MyMenuitem: TMenuItem;
begin
MyMenuitem := TMenuItem(Self.FindComponent(PopupMenuItemName[1]));
if assigned(MyMenuitem) then
begin
SetSelectedColumn(GetSelectedColunm);
MyMenuitem.Caption := Format(AMenuItemCaption, [FSelectedColumn.Title.Caption]);
end;

end;

procedure TFinderDataZN.SetADataSet(const Value: TCustomADODataSet);
begin
FADataSet := Value;
end;

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

if FGrid <> nil then
begin
FGrid.FreeNotification(Self);
SetSelectedColumn(GetSelectedColunm);
FADataSet := TCustomADODataSet(FGrid.DataSource.DataSet);
(* 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;

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

If not Assigned(Value) or (nAllowCreate) then
begin
FPopupMenu := TPopupMenu.Create(Self);
FGrid.PopupMenu := FPopupMenu;
FIsPopupExistent := peInternalCreated;
end
else
begin
FPopupMenu := Value;
FIsPopupExistent := peExternalAssociation;
end;

if FPopupMenu <> nil then
begin
FPopupMenu.FreeNotification(Self);
FPopupMenu.OnPopup := PopupMenuPopup;
end;
end;

procedure TFinderDataZN.SetSelectedColumn(const Value: TColumn);
begin
(* Armazena a coluna do Grid aqual foi clickada com o
botão direito do mouse, para capturar informações como: Titúlo e etc.. *)
FSelectedColumn := Value;

if FSelectedColumn <> nil then
FAFieldSearch := FSelectedColumn.Field;
end;

end.


Nenhum comentário:

Postar um comentário