DataSnap com Stored Procedures (1/3)

A natureza do Delphi DataSnap é utilizar SQL dinâmico, que é construído e enviado em tempo de execução para o servidor. Talvez por isso muita gente não se dê conta que pode usar Stored Procedures (SP) em um servidor de aplicação DataSnap. Na verdade, nada nos impede usar SP para obter dados ou para resolver atualizações no banco de dados e os SP podem ser muito úteis.

Vantagens de usar Stored Procedures:

Desvantagens de usar Stored Procedures:

Usar SP não fere nenhum princípio de desenvolvimento multicamadas. De fato, o SQL fica no servidor, não no cliente. Tem gente que prefere não ter nenhuma lógica no banco de dados e tratá-lo como um simples repositório de dados. Outros, pelo contrário, entendem que toda lógica de acesso a dados precisa estar no servidor de banco de dados. Finalmente, há aqueles, entre os quais eu me incluo, que preferem um equilíbrio, pesando custos e benefícios caso a caso.

Nos próximos artigos vou mostrar como usar SP para obter dados do servidor de aplicação e como usar SP para atualizar os dados em um ApplyUpdates.

Scroll Grid Font Size

Eu uso a toda hora Ctrl+Scroll do mouse para alterar o tamanho da fonte de uma página no browser. Daí eu fiquei com vontade de implementar isso no Delphi. Por que não um TDBGrid com a mesma funcionalidade?

No exemplo da imagem anterior, se você deixar o mouse sobre o grid e girar o botão de rolagem para baixo enquanto mantém a tecla Control pressionada a fonte será aumentada até ficar como a imagem abaixo.

Semelhante ao que fizemos anteriormente para implementar a rolagem pelo mouse em um TScrollBox, tudo que é preciso é sobrescrever os eventos OnMouseWheelDown e OnMouseWheelUp com o tratamento adequado para aumentar ou diminuir o tamanho da fonte do grid, respectivamente.

O código principal é este:

procedure TMainForm.DBGrid1MouseWheelDown(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  if ControlKeyPressed then
  begin
    ChangeGridFontSize(DBGrid1, +1);
    Handled := True;
  end;
end;
 
procedure TMainForm.DBGrid1MouseWheelUp(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  if ControlKeyPressed then
  begin
    ChangeGridFontSize(DBGrid1, -1);
    Handled := True;
  end;
end;

Se a tecla Control não estiver pressionada, o valor de Handled continua False e o funcionamento normal da classe prossegue, rolando as linhas do grid. Se o resultado da função ControlKeyPressed (veja abaixo) é True, o botão Control está pressionado e devemos modificar o tamanho da fonte.

function ControlKeyPressed: Boolean;
begin
  Result := Boolean(GetKeyState(VK_LCONTROL) shr 31) or
    Boolean(GetKeyState(VK_RCONTROL) shr 31);
end;

GetKeyState é uma função da API do Windows e a forma (bizarra) de interpretar seu resultado é verificar se o bit mais significante do resultado é 1. Na função eu verifico se o botão esquerdo ou o direito está pressionado naquele momento.

A mudança de tamanho da fonte em si é realizada pela rotina ChangeGridFontSize:

type
  TDBGridHack = class(TDBGrid);
 
procedure ChangeGridFontSize(Grid: TDBGrid; Delta: Integer);
var
  Size: Integer;
begin
  Size := Grid.Font.Size + Delta;
  if (6 <= Size) and (Size <= 32) then
  begin
    Grid.Font.Size := Size;
    TDBGridHack(Grid).LayoutChanged;
  end;
end;

Primeiro note que a procedure recebe um parâmetro Delta que pode ser positivo ou negativo. Se positivo, aumenta a fonte. Se negativo, diminui. A rotina limita o novo tamanho de fonte à faixa 6..32 por segurança.

E o que é esse TDBGridHack? Err… bem… digamos que é um “código de flexibilização”. :-)

LayoutChanged é um método protegido de TDBGrid. Para poder usá-lo, precisamos declarar no escopo da unit uma classe descendente de TDBGrid. Dessa forma a nova classe será nossa “amiga” e poderemos usar seus métodos protegidos (mas não conte isso pra ninguém!). LayoutChanged é chamado para corrigir o número de linhas exibidas no grid quando a fonte é diminuida.

Você pode fazer o download desse projeto de exemplo e experimentar a mesma técnica com outros tipos de componente.

TcxGrid como ListBox (parte 2/2)

Na sequência (sem trema, pelas novas regras) do artigo anterior, veja como criar um descendente de TcxCustomDataSource para editar um TStringList (ou qualquer TStrings) com o componente TcxGrid.

Os componentes da DevExpress são excelentes exemplos de boa programação orientada a objetos. O conceito de GridView, por exemplo, é abstraído de tal forma que podemos ter visualização como tabelas com linhas e colunas, gráficos ou cartões (”cards”), como os do gerenciador de contatos do Outlook. Por essa razão as “colunas” de um GridView são referidas como “Items”, já que não faria sentido chamar de coluna uma série de gráfico ou uma linha de um cartão.

A fonte de dados que alimenta um grid é também generalizada pela classe TcxCustomDataSource. A descendente TcxDBDataSource é especializada para trabalhar com datasets e implementa filtragem, agrupamento e cálculo de sumários. O componente TcxGrid não presume nada sobre a forma como os dados são obtidos, lidos e escritos. Lida apenas com os métodos e propriedades de TcxCustomDataSource e com isso permite o polimorfismo.

É fácil implementar um novo descendente de TcxCustomDataSource para lidar com TStrings ao invés de TDataSet. No exemplo abaixo são implementados apenas os métodos básicos que precisam ser sobrescritos em qualquer descendente de TcxCustomDataSource.

O resultado é um list box editável. Você pode usar as teclas de atalho “Ins” para inserir uma linha e “Ctrl+Del” para deletar. Também pode adicionar uma linha no final da lista no modo “append”.

{**********************************************}
{                                              }
{  cxStringsDataSource                         }
{  by Daniel Maltarolli                        }
{  http://singularsistemas.com.br/blog         }
{                                              }
{  01) 2008-05-22 Initial release              }
{                                              }
{**********************************************}
 
unit cxStringsDataSource;
 
interface
 
uses Classes, cxCustomData;
 
type
  TcxStringsDataSource = class(TcxCustomDataSource)
  private
    FStrings: TStrings;
  protected
    function AppendRecord: TcxDataRecordHandle; override;
    procedure DeleteRecord(ARecordHandle: TcxDataRecordHandle); override;
    function GetRecordCount: Integer; override;
    function GetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant; override;
    function InsertRecord(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle; override;
    procedure SetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle; const AValue: Variant); override;
  public
    constructor Create(Strings: TStrings);
  end;
 
implementation
 
uses SysUtils, Variants;
 
{ TcxStringsDataSource }
 
constructor TcxStringsDataSource.Create(Strings: TStrings);
begin
  inherited Create;
  FStrings := Strings;
end;
 
function TcxStringsDataSource.AppendRecord: TcxDataRecordHandle;
begin
  FStrings.Append(EmptyStr);
  DataChanged;
  Result := TcxDataRecordHandle(FStrings.Count - 1);
end;
 
procedure TcxStringsDataSource.DeleteRecord(
  ARecordHandle: TcxDataRecordHandle);
begin
  FStrings.Delete(Integer(ARecordHandle));
  DataChanged;
end;
 
function TcxStringsDataSource.GetRecordCount: Integer;
begin
  Result := FStrings.Count;
end;
 
function TcxStringsDataSource.GetValue(ARecordHandle: TcxDataRecordHandle;
  AItemHandle: TcxDataItemHandle): Variant;
var
  Index: Integer;
begin
  Index := Integer(ARecordHandle);
  Result := FStrings.Strings[Index];
end;
 
function TcxStringsDataSource.InsertRecord(
  ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle;
var
  Index: Integer;
begin
  Index := Integer(ARecordHandle);
  FStrings.Insert(Index, EmptyStr);
  DataChanged;
  Result := ARecordHandle;
end;
 
procedure TcxStringsDataSource.SetValue(ARecordHandle: TcxDataRecordHandle;
  AItemHandle: TcxDataItemHandle; const AValue: Variant);
var
  Index: Integer;
begin
  Index := Integer(ARecordHandle);
  FStrings.Strings[Index] := VarToStrDef(AValue, EmptyStr);
end;
 
end.

Você precisa atribuir por código um objeto TcxStringsDataSource a GridView.DataController.CustomDataSource. É importante que o GridView seja um TcxTableView, não um TcxDBTableView. Para isso você precisará primeiro deletar no Form Designer o TcxDBTableView que é criado por default, criar um novo TableView e atribuí-lo a GridLevel1.

As propriedades do GridView são semelhantes àquelas usadas na parte um, porém deixando o grid editável e “OptionsSelection.CellSelect = True”.

Veja um exemplo completo de utilização no arquivo anexo.

Next Page →