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:
- SP podem incluir lógica condicional, cursores, chamar outros SP, funções, etc.
- SP são parametrizáveis (TParams)
- SP são transações atômicas
- SP são escritos em SQL
- SP podem ser facilmente atualizados com o sistema no ar
- SP têm plano de execução previamente “compilado”
Desvantagens de usar Stored Procedures:
- SP não são portáveis entre bancos de dados diferentes
- SP são somente-leitura
- SP de SELECT dificultam a geração automática de SQL de atualização
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.
