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

 
BlogBlogs.Com.Br