SlideShare uma empresa Scribd logo
1 de 32
Baixar para ler offline
fevereiro
2015
fevereiro
2015
fevereiro
2015
03
18
Índice
Dicas The Club
30
Editorial
04
11
Autor: Hamden Vogel
05
Autor: Marlon Aparecido Branco Valentino
28
SQL Server - uso de Gatilhos
para auditoria de dados
Autor: Thiago C. Montebugnoli
Autor: Jeferson Silva de Lima
Bootstrap – Pontapé inicial
na utilização do Framework pelo
Visual Studio
FastFile - Aplicativo Leitor
MultiThread para Arquivos
Grandes em Lotes
Delphi XE5 – Adicionando
Recursos
fevereiro
2015
04
Delphi é marca registrada da Borland International,
as demais marcas citadas são registradas
pelos seus respectivos proprietários.
Thiago Montebugnoli- Editor Chefe
thiago@theclub.com.br
Caro Leitor,
Primeiramente desejo a todos boas festas neste mês de Fevereiro, pois afinal
nosso país é o único onde se comemora esta grande festa, o Carnaval. Voltando
ao nosso foco principal que são artigos sobre programação, neste mês, nosso
consultor técnico Marlon Aparecido Branco Valentino escreveu mais um artigo
sobre o assunto BootStrap denominado: “Bootstrap - Pontapé inicial na utiliza-
ção do Framework pelo Visual Studio”. Desta vez ele procurou abranger algumas
configurações e dicas importantes para quem está começando a trabalhar com
estatecnologia.Nossooutroconsultor,JefersonSilvadeLima,nosensinoucomo
adicionarrecursosemaplicativosmóveis,comoporexemplo:adicionarorecurso
de atalho na aplicação ou até mesmo as denominadas Notificações. Implemen-
tações como estas, tornam o aplicativo muito mais atraente e prático para o
usuário.JánossocolaboradorHamdenVogelredigiuoartigo“FastFile–Aplicativo
LeitorMultiThreadparaArquivosGrandesemLotes”oqualeleexplicadetalhada-
mente o funcionamento em tempo real para a leitura de um arquivo grande em
pequenos lotes. Para isto ele divide proporcionalmente em tamanhos padrões
e fornecido pela própria aplicação, apresentando uma abordagem alternativa
para os desenvolvedores realizarem uma fácil implementação em seus sistemas.
ParafinalizararevistaeuutilizeioSQLServerjuntocomosdenominadosgatilhos
para a auditoria de dados. Procurei ser bem prático e rápido para apresentar
uma alternativa para quem procura este tipo de controle, independentemente
da interface utilizada no momento.
Vou ficando por aqui,
Desejo a todos uma boa leitura e até o mês que vem!
Abraços
Av. Profº Celso Ferreira da Silva, 190
Jd. Europa - Avaré - SP - CEP 18.707-150
Informações e Suporte: (14) 3732-1529
Internet
http://www.theclub.com.br
Cadastro: cadastro@theclub.com.br
Suporte: suporte@theclub.com.br
Informações: info@theclub.com.br
Skype Cadastro: theclub_cadastro
Skype Suporte: theclub_linha1
theclub_linha2
theclub_linha3
www.twitter.com/theclubbr
Copyright The Club 2013
Diretor Técnico
Marcos César Silva
Diagramação
Vitor M. Rodrigues
Design
Vitor M. Rodrigues
Revisão
Denise Blair
Colunistas
Hamden Vogel
Jeferson Silva de Lima
Luciano Pimenta
Lucas Vieira de Oliveira
Thiago Cavalheiro Montebugnoli
Impressão e acabamento:
GRIL - Gráfica e Editora
Taquarituba-SP - Tel. (14) 3762-1345
Reprodução
A utilização, reprodução, apropriação, armazenamento em banco
de dados, sob qualquer forma ou meio, de textos, fotos e outras
criações intelectuais em cada publicação da revista “The Club
Megazine” são terminantemente proibidos sem autorização
escrita dos titulares dos direitos autorais.
Editorial
fevereiro
2015
05
Introdução
Continuando a matéria sobre o Framework do Twitter, agora partiremos
para a criação de um aplicativo para web ou web site pelo Visual Studio, a
Microsoft facilitou (e muito) esta parte, pois é possível criar um projeto pelo
VisualStudio,quejávemcomalgumaspáginasprontasparaodesenvolvimen-
to, com o layout no padrão do Bootstrap, desta forma você pode começar a
desenvolver seu aplicativo para web ou web site de maneira mais rápida, pois
nãoseránecessáriocriaraspáginasbásicascomoHome,Sobre,Contato,Login,
CadastrodeUsuário,poisessaspáginasjávemprontas,seráapenasnecessário
alterar o layout do jeito que você preferir (obs: para fazer tais alterações é
necessário ter conhecimento básico de HTML, CSS, JavaScript).
Criando um projeto no Visual Studio 2013
No andamento deste artigo utilizarei a versão do Visual Studio 2013 que
está disponível apenas em inglês. Iremos começar criando um projeto, para
acessar este recurso basta seguir o exemplo da Imagem 1, logo abaixo:
Imagem 1 Criando um Projeto
Apósseguirospassosdaimagem01,oVisualStudioabriráumajanelapara
você selecionar qual linguagem deseja utilizar, para este projeto é necessário
selecionar Visual C#(C Sharp), e depois clique em Web e ele lhe dará apenas
umaopção,a“ASP.NETWebApplication”(quesignificaAplicaçãoWeb,emuma
Bootstrap-Pontapéinicial
nautilizaçãodoFrameworkpelo
Visual Studio
traduçãolivre)selecioneestaopçãoecoloqueonomedeseuprojetonaparte
inferior da tela, se restou alguma dúvida observe na Imagem 02:
Imagem 2 Janela para criação de um projeto.
Depoisdeselecionadasasinformaçõescorretasedadoodevidonomeao
projeto,bastaclicarem“OK”elogoemseguidaoapareceráoutratelaparavocê
selecionaro“template”(emtraduçãolivresignificamodelo),eparanesteartigo
utilizaremos o modelo “MVC” (a sigla significa Model-View-Controller, que é
um padrão de arquitetura de software), observe na imagem 03 logo abaixo:
Imagem 3 Janela para seleção do modelo da aplicação da web.
Ao clicar em “OK” o Visual Studio levará alguns segundos para gerar o
projeto,assimqueeleterminar,parapodervisualizaroprojeto,compile-oem
seu navegador de preferência (irei utilizar a versão para desenvolvedores do
Mozilla Firefox), e terá este resultado exibido na imagem 4:
fevereiro
2015
06
Imagem 4 Compilando o projeto!
Este será o resultado que seu navegador exibirá após compilar o projeto:
Imagem 5 Tema padrão gerado pelo Visual Studio.
Como foi citado anteriormente, o Visual Studio gera as páginas básicas
necessárias para começar a desenvolver uma aplicação para web, que são
essas exibidas na imagem 5: Home(Inicio), About(Sobre), Contact(Contato),
Registro(Cadastro) e Log In(Entrar). Com estas páginas prontas basta come-
çar a desenvolver sua aplicação web. Caso você não tenha gostado do tema
padrão, para mudá-lo é muito fácil, o primeiro passo é entrar no site http://
bootswatch.com/eescolherumtemadesuapreferência,nomeucasoescolhi
o tema “Yeti”, quando encontrar um tema de seu agrado clique em download
e será aberta uma aba em seu navegador com o código fonte da biblioteca de
estilos do bootstrap, basta pressionar atalho CTRL + A para selecionar tudo e
CTRL+Cparacopiaroconteúdo,depoisabraoVisualStudiocomoseuprojeto
aberto e clique com o botão direito do mouse em cima da pasta “Content” e
vá até a opção “Add” depois clique em ”New Item” (Caso o Visual Studio não
esteja exibindo o “Solution Explorer“, que é o recurso que exibe as pastas do
projeto, pressione a tecla CTRL + ALT + L).
Imagem 6 Criando um arquivo CSS para o novo tema.
Depois que clicou em “New Item”, o Visual Studio exibirá uma tela para
selecionar o tipo de arquivo que deseja que ele crie, para o tema é necessário
selecionar a opção “Style Sheet” (em tradução livre significa Folha de Estilos),
e na parte inferior da tela é necessário nomear o arquivo como “bootstrap-
-theme.css”ecliqueem“Add”parafinalizar,serestoualgumadúvidaobserve
como o procedimento é feito na imagem 7 logo abaixo:
Imagem 7 Adicionando o arquivo ao projeto.
Apague a tag “body” que vem escrita por padrão no arquivo e cole o con-
teúdo que copiou do tema escolhido no site do “Bootswatch”, depois salve o
arquivo(CTRL+S),quandofeitoisso,podefecharoarquivo,emseguidadêum
duplo clique na pasta “App Start” de seu projeto e faça o mesmo para abrir
o arquivo “BundleConfig.cs”, o mesmo exibido na imagem 08 logo abaixo:
Imagem 8 Abrindo o arquivo BundleConfig.cs
QuandoeleabrirváatéalinhadecódigoqueestádeclarandooBootstrap,
como é exibido na imagem 9 abaixo:
Imagem 9 Alterando a declaração do CSS referente ao tema.
Altereadeclaraçãode“bootstrap.css”para“bootstrap-theme.css”,queé
o arquivo que criamos anteriormente com o tema escolhido do bootswatch.
bundles.Add(new StyleBundle(“~/
Content/css”).Include(
“~/
Content/bootstrap-theme.css”,
“~/
Content/site.css”));
Dependendo do tema escolhido, que no meu caso foi o “Yeti”, ao compi-
lar o projeto o resultado que obtive será exibido na imagem 10 logo abaixo:
fevereiro
2015
07
Imagem 10 Novo tema.
Aoobservaraimagem10,épossívelperceberqueapáginaficoucomum
design mais leve e agradável para olhos, isso faz com que o usuário consiga
acessar a página durante mais tempo e a partir daí basta alimentá-lo com
conteúdo e será fiel ao seu site, seja ela uma loja virtual ou site de notícias.
Como utilizar máscaras em campos
Agora mudando um pouco de assunto, vamos entrar em uma parte que
muitosprogramadoresparawebtêmdúvidasemuitasvezesacabamperdendo
muitotempoparafazercoisassimples,comoporexemploimplementarouso
de máscaras. Começando pela criação de “Inputs” (campos para inserção de
dados)commáscarasdedadoscomoasdeTelefoneeRG,queémuitosimples
de se fazer, pois já existe um poderoso jQuery plugin que faz toda a parte de
programação por traz chamado de “Masked Input”(em uma tradução livre
significa “Máscara de entrada”), a única parte que precisamos fazer é uma
função em JavaScript que define a máscara que o campo terá, enfim, mãos a
obra. O primeiro passo seria obter o plugin Masked input na versão 1.4, para
issováatéoGoogleepesquise“Maskedinput1.4download”edepoiscliqueno
segundoresultado,queodirecionaráapáginadaDigitalBush,desenvolvedora
do plugin, como é exibido na imagem 11 logo abaixo:
Imagem 11 Obtendo o plugin Masked Input, parte 1.
Ao abrir o site da Digital Bush, poderá observar que há dois links para
baixar o plugin, pode ser obtido em sua forma não comprimida que é possível
compreender o código se possuir um conhecimento avançado em JavaScript,
caso queira o arquivo compresso basta clicar na segunda opção que é exibida
na imagem 12 logo em seguida:
Imagem 12 Obtendo o plugin Masked Input, parte 2.
Quando clicar em um dos links o site lhe direcionará ao código fonte
do Masked Input, basta fazer o mesmo procedimento da troca do tema do
Bootstrap, de um “Ctrl + A” e depois “Ctrl + C” e vá até o Visual Studio e
com a seta do mouse sobre a pasta “Scripts” do projeto, clique com o botão
direito, vá em Add e depois New Item, ao abrir a tela para selecionar o tipo
do arquivo, selecione a segunda opçãpo “JavaScript File”, na parte de baixo
coloque o nome do arquivo como “maskedinput-1.4” e clique em Add, como
mostra a imagem 13:
Imagem 13 Adicionando o plugin ao projeto.
Quando o novo arquivo abrir, cole(Ctrl + V) o código fonte do plugin em
seguida salve-o(Ctrl + S) e feche-o. Depois de adicionado o plugin ao projeto,
precisamos declará-lo no arquivo “BundlesConfig.cs”, como foi feito no tema,
e neste caso estaremos declarando um pacote de “Scripts” e não de estilos,
digite as seguintes linhas de código logo abaixo de onde foi declarado o tema
anteriormente:
bundles.Add(new ScriptBundle(“~/
bundles/maskedinputjs”)
.Include(“~/Scripts/maskedinput-
1.4.js”));
Quandoterminardeadicionaraslinhasdecódigo,salveefecheoarquivo
“BundlesConfig.cs”,opróximopassoéatualizaraversãodojQueryqueégerada
peloVisualStudio,paraaversãodisponívelnositedohttp://www.jquery.com,
clique no menu “Download” quando ele abrir a página clique nos links para
baixar a versão 1.11.2, nas versões comprimidas e não comprimidas, como é
exibido na imagem 14 logo abaixo:
Imagem 14 Obtendo a versão 1.11.2 do jQuery.
fevereiro
2015
08
ApósbaixarosarquivosdojQuery,coloque-osnapastaScriptsdoprojeto,
depois de feito isso, exclua os arquivo com os respectivos nomes: jquery-
-1.10.2.intellisense; jquery-1.10.2; jquery-1.10.2.min.
Depois que os arquivos antigos foram excluídos é necessário ir abrir o
Visual Studio e adiciona-los ao projeto, para fazer isso é necessário estar com
a seta do mouse sobre a pasta Scripts, clicar com o botão direito do mouse,
ir até a opção do “Add” e depois clicar na opção “Existing Item”(em tradução
livre significa Item Existente), se houver dúvida observe a imagem 15:
Imagem 15 Adicionando os novos arquivos do jQuery ao projeto.
Quando o Visual Studio abrir o “explorer” procure os arquivos com os
respectivos nomes jquery-1.11.2 e jquery-1.11.2.min, pressione a tecla “Ctrl”
e selecione os dois e clique em Add, observe como é feito na imagem 16.
Imagem 16 Selecionando os arquivos do jQuery.
Quandoterminardeadicioná-losaoprojeto,podemospartirparaadecla-
ração dos campos que iremos definir máscaras, para isso, se estiver no Visual
Studio, vá até a pasta “Models”(que em tradução livre significa Modelos) e
abra o arquivo “AccountViewModel” e vá até a declaração da classe chamada
“RegisterViewModel”, como mostra a imagem 17 logo abaixo:
Veja a Imagem 17.
Observeocódigoemnegritoabaixocomoficaráadeclaraçãodoscampos:
[DataType(DataType.
Password)]
[Display(Name = “Confirm
password”)]
[Compare(“Password”,
ErrorMessage = “The password
and confirmation password do not
match.”)]
public string
ConfirmPassword { get; set; }
[Display(Name = “RG”)]
public string RG { get;
set; }
[Display(Name =
“Telefone”)]
public string Telefone
{ get; set; }
}
Quando finalizar a declaração, salve e feche o arquivo e vá para o explo-
rador de soluções e abra a pasta “Views”, depois “Account” e abra o arquivo
“Register.cshtml”, como é mostrado na imagem 18:
Imagem 18 Acessando o arquivo de cadastros.
Imagem 17 Declarando os campos que iremos utilizar.
fevereiro
2015
09
Comoarquivoaberto,vaiestarigualàimagem19,váparaapartedebaixo
do código como está na imagem abaixo, agora iremos criar dois “inputs” para
utilizarmosasmáscaras,easlinhasdecódigotemqueserescritasentreas“div”
tagsquedeclaramocampodeConfirmaçãodesenhaeobotãopararegistrar,
bem no lugar onde está a linha vermelha na imagem 19, caso não saiba como
criar um os campos copie o código que disponibilizarei abaixo da imagem 19.
Imagem 19 Localizando o lugar correto para criar os campos.
		
O código dos respectivos campos é o seguinte, observe que as variáveis
são iguais as que foram declaradas no arquivo “AccountViewModel”:
<div class=”form-group”>
@Html.LabelFor(m =>
m.RG, new { @class = “col-md-2
control-label “})
<div class=”col-md-10”>
@Html.TextBoxFor(m
=> m.RG, “RG”, new { @class =
“form-control” })
</div>
</div>
<div class=”form-group”>
@Html.LabelFor(m =>
m.Telefone, new { @class =
“col-md-2 control-label “})
<div class=”col-
md-10”>
@Html.
TextBoxFor(m => m.Telefone,
“Telefone”, new { @class =
“form-control” })
</div>
</div>
Depois que criarmos os campos, temos que escrever o script com a
função, mas antes é necessário declarar o plugin masked input que adicio-
namos ao projeto e criamos um pacote para ele anteriormente neste artigo,
para adicioná-lo é simples, observe que no fim do código tem uma seção de
“Scripts”, e existe dentro dela uma declaração de um validador de jQuery,
essa linha não é necessária e pode ser apagada, o código com a declaração
do pacote o plugin é o seguinte:
@section Scripts {
@Scripts.Render(“~/bundles/
maskedinputjs”)
}
Assim que declarar o plugin, podemos escrever a função, para que ele
funcione corretamente é necessário que o jQuery esteja declarado na página
mestre, o nome desta página é “_Layout” e ela fica dentro do diretório “Sha-
red” na pasta ”Views”, dê um duplo clique nela para abri-la, caso não tenha
encontrado o arquivo, observe a imagem 20 logo abaixo:
Imagem 20 Abrindo a página mestre.
Quandoaberto,observedentrodatag<head>seopacotedojQueryestá
sendo declarado, como na imagem 21:
Imagem 21 Verificando o jQuery na página mestre.
Agora que verificamos o jQuery podemos fechar a página mestre e voltar
para a página de registros e começar a escrever a função da máscara, ela tem
que ser inserida logo após os códigos do formulário de registros, observe o
código abaixo:
<div class=”form-group”>
<div class=”col-md-
offset-2 col-md-10”>
<input
fevereiro
2015
10
type=”submit” class=”btn btn-
default” value=”Register” />
</div>
</div>
<script>
$(document).
ready(function () {
$(“#RG”).
mask(“99.999.999-9”);
$(“#Telefone”).
mask(“(99)99999-9999”);
});
</script>
@section Scripts {
@Scripts.Render(“~/bundles/
maskedinputjs”)
}
Observe que a função JavaScript utiliza os ID’s que declaramos nos cam-
pos, podendo assim compilar o projeto e teremos um resultado idêntico ao
da imagem 22
Imagem 22 Resultado da máscara de RG e Telefone utilizando o plugin Masked
Input.
Conclusão
Neste artigo é possível perceber o quanto o Bootstrap juntamente com o
Visual Studio pode dar aquele pontapé inicial que estava faltando para você
começarumnovoprojeto,comosrecursosoferecidospeloFrameworkédifícil
negar que o nível de utilidade é realmente alto, pois ele facilita e agiliza muito
o desenvolvimento. Caso já utilize o Bootstrap e desconhecia a possibilidade
de obter um tema online sem ter que alterar todo o layout da sua página, a
partir deste artigo procurei apresentar vários temas disponibilizados online
gratuitamente para dar aquele up que faltava em seu projeto.
fevereiro
2015
11
D
epois de tanto pesquisar sobre essa questão de como abrir
um arquivo grande em Delphi para seu processamento,
decidi escrever sobre este tema, um tanto delicado por ser
difícilchegaraumaconclusãosatisfatóriaequeagradassem
a gregos e troianos – pois um arquivo grande poderia gerar
facilmenteumaexceçãodotipo“OutOfMemory”quantotravaraaplicaçãode
forma incessantemente demorada – alternativas como file mapping, ler cada
linha em readln ou carregar tudo em loadfromfile/loadfromstream utilizando
stringlist´s podem não ser uma ideia tão interessante – vão demorar para
processar e algumas vezes irão ocasionar o mesmo erro de falta de memória
citado acima.
A ideia que venho mostrar aqui é simples mas funciona perfeitamente.
Não é ideal para arquivos pequenos (onde a comparação com um dos modos
tradicionais de abrir arquivos) pois o “tiro poderá sair pela culatra” – ficará
maislentodoquedeveria:essainteressanteeeficientetécnicasomentedeverá
ser aplicada a arquivos grandes – preferencialmente de 100 kB para cima.
O que tem que ser feito basicamente é o seguinte:
1. Obter o arquivo grande;
2. Dividir este arquivo em vários mini-arquivos;
3. Cada arquivo terá uma thread para sua leitura;
4. Ler cada mini-arquivo isoladamente da VCL;
5. ArmazenaremcadainstânciadeumobjetoTMemooresultadoda
leitura;
6. Acessar cada parte de um objeto TMemo através de um objeto
TClientDataSet.
Basicamente o processo é este. Vamos embarcar nessa aventura ?
Primeiros passos com a leitura MultiThread
As primeiras coisas a serem notadas na construção do nosso aplicativo é
queelepróprio é“consciente”nadistribuiçãodos arquivos temporários esua
manipulação – sem desperdiçar nenhum byte ou ser lento neste processo –
todos os cálculos da administração dos tamanhos de cada arquivo bem como
FastFile–AplicativoLeitor
MultiThread para Arquivos
Grandes em Lotes
obufferredimensionadoemtempodeexecuçãoserãogerenciadospelonosso
aplicativodeformatransparenteparaousuário–eficienteecomcomponentes
progressbar para que fique ciente da execução desta tarefa.
É claro que o bom-senso é fundamental para que o programa demonstre
rapidez na sua resposta – um arquivo texto com mais de 500MB poderá levar
até 6 minutos de espera – o que foi utilizado foi um de 500MB levando em
média 3 minutos – “não podemos fazer milagre” – mas é muito útil para o
gerenciamento dos lotes gerados e o retorno é bem melhor bem como a
recuperação dos erros – tente a forma tradicional levar sempre uma exceção
de “sem memória” e consequentemente o encerramento do aplicativo – o
arquivo não será lido e o tempo que será gasto para lidar com uma solução
intermediária de leitura será superior ao tempo de resposta da nossa solução
aqui apresentada.
Foram experimentadas alternativas inviáveis, um tanto exploradas pela
Web, mas que não trouxeram uma solução que chegasse perto a de que foi
implementada aqui. Utilizar recursos de Memory Mapping Files, FileStreams,
etc, podem ser bons para arquivos não tão grandes – eles vão “travar” sua
aplicação porque a memória do Windows vão derrubá-los – assim como um
dominó.NãogastemaistempocontandocomobjetosdotipoTStringList,TList,
etc; eles não vão pensar duas vezes e vão responder que não tem a memória
que precisam – e vão deixar você na mão, infelizmente. O que poderá ser
feito é o que foi dito inúmeras vezes acima – funciona em poucos minutos – o
usuárioacompanhaoprogressodotrabalhoenquantoqueoarquivo(quenão
queria ser lido de jeito nenhum pelo Windows) é finalmente carregado – em
lotes – um por um – e sendo “printado” para a tela de sua aplicação – vemos
agora que existe uma luz no fim do túnel.
Administração do Buffer
Para que o buffer seja preenchido e lido, algumas considerações são
necessárias e são citadas abaixo:
1. O programa obtém o tamanho do arquivo em bytes (por exemplo,
um arquivo com 2887 KB será gerado com 2955545 bytes pela nossa função
de obter tamanhos de arquivos);
fevereiro
2015
12
2. O programa gerará um “tamanho padrão” dos arquivos em lotes
a serem gerados (baseando no mesmo exemplo, este arquivo terá o tama-
nho padrão de divisão dos seus arquivos em lotes de 295600 bytes, ou seja,
288,671875 KB cada um);
3. Baseado neste “tamanho padrão” o programa gerará os lotes
necessários neste tamanho para cada um, até preencher todos eles com os
dados do arquivo original;
4. O programa criará um objeto TFileStream para cada lote gerado;
5. Assim sendo, o programa criará uma thread para cada lote a fim
de ler o conteúdo deles (poderão ser geradas várias threads de uma vez –
prioridade normal);
6. Oprogramacalcularáotamanhodobufferdeleituradathreadcom
base no seguinte: 1024 (KB) vezes o tamanho do arquivo;
7. Cada thread criará um objeto TFileStream encarregado da leitura
de cada lote corrente em que esta thread administra;
8. O buffer é preenchido com este objeto TFileStream;
9. ÉcriadoumobjetoTMemoqueiráreceberoconteúdodestebuffer;
10. EsteobjetoTMemoserágerenciadointernamentepeloprograma,a
fimdeservisualizadonomomentoapropriado,vistoqueváriosdelespoderão
ser gerados (um por lote) – sendo que alguns métodos não serão utilizados
a fim de agilizar a execução deste processo de criação (como o método clear,
por exemplo);
11. Será criado um objeto TClientDataSet para o gerenciamento de
todos os lotes, a fim de mostrá-los disponíveis em uma grid e serem selecio-
nados e visualizados pelo usuário.
Segue abaixo o trecho do código-fonte responsável pela leitura do buffer,
caracter a caracter, do tipo AnsiChar. Note que foi definido um “filtro” para
esta leitura. Esta procedure se encontra na nossa thread de apoio.
procedure TFileReadThread.
FileRead;
var
Stream: TFileStream;
i: integer;
Buffer: array of AnsiChar;
//1024 (kB) x tamanho do
arquivo ...
TempStr: string;
const Allowed = [‘A’ .. ‘Z’,
‘a’ .. ‘z’, ‘0’ .. ‘9’, ‘_’,
#13, #10, ‘-’, ‘’, ‘”’, ‘!’,
‘@’
,’#’, ‘$’, ‘%’, ‘¨’, ‘*’,
‘(‘, ‘)’, ‘{‘, ‘}’, ‘[‘, ‘]’,
‘<’, ‘>’, ‘.’, ‘:’, ‘;’, ‘,’ ,
‘?’, ‘!’, ‘/’,
‘+’, ‘-’, ‘´’, ‘`’, ‘=’, ‘^’,
‘~’, ‘&’, ‘ ‘];
begin
TempStr := ‘’;
SetLength(Buffer, self.
iFileSize + 1);
Stream := TFileStream.
Create(self.strFileName
,fmOpenRead);
try
Stream.Read(Buffer[0],
self.iFileSize + 1);
finally
Stream.Free;
end;
for i := Low(Buffer) to
High(Buffer) do
if (Buffer[i] in Allowed)
then
TempStr := TempStr +
Buffer[i];
Form1.memo3.Lines.Add(‘Arquivo
‘ + Self.strFileName + ‘ lido
com sucesso.’);
Form1.CriaMemos(Self.Id,
TempStr);
// Form1.strList.
AddObject(TempStr, TObject(Self.
Id));
end;
Figura 01 – Resultado final do processo de leitura em lotes.
Figura 02 – O processo sendo lido em lotes.
fevereiro
2015
13
Figura 03 – Mais um exemplo de leitura em lotes – note que este arquivo proces-
sado em lotes tem 562 MB, sendo lido em 3 minutos. Foram criados 1000 lotes para
sua visualização. Cada lote poderá ser processado da forma mais conveniente, em um
loop ou em um evento próprio – por exemplo, para a leitura dos lotes “linha por linha”
como em um readln.
Seguemabaixotrechosdocódigo-fonteresponsávelparaofornecimento
do tamanho padrão de cada lote:
function TfrmMain.
NumberOfPartsToDivide(const
filesize: integer): integer;
var
denominator: integer;
{ In fraction, the
denominator will match the
amount of exact parts where the
file will be divided,
while the numerator is
the file size.
Em fração, o denominador
corresponderá a quantidade
de partes exatas em que será
dividido o arquivo,
enquanto que o numerador
será o tamanho do arquivo. }
begin
case length(IntToStr(Round(fil
esize/1000))) of //convert to
bytes
1: denominator := 4;
2: denominator := 8;
else
denominator :=
Round(Math.Power(10,
length(IntToStr(filesize)) - Rou
nd(length(IntToStr(filesize)) /
2)));
end;
Result := Round(filesize/
denominator) * 100;
end;
Segue abaixo a função responsável por criar os arquivos em lote e preen-
chê-los com base no tamanho padrão fornecido anteriormente:
procedure TfrmMain.
FileSplit(const StrFilename:
String);
var
StrmInput, StrmOutput :
TFileStream;
FileNumber : Integer;
FileSize: integer;
SequentialFile: string;
begin
if (StrFilename = ‘’) then
Exit;
if not
(FileExists(StrFilename)) then
Exit;
tempFolder := Biblioteca.
ExtractName(SysUtils.
ExtractFileName(StrFilename));
if SysUtils.
DirectoryExists(LocalDirectory
+ tempFolder) then SysUtils.
RemoveDir(LocalDirectory +
tempFolder);
SysUtils.
CreateDir(LocalDirectory +
tempFolder);
//FileSize :=
Round(Biblioteca.
GetFileSize(StrFilename) /
NumberOfParts);
FileSize := NumberOfPa
rtsToDivide(Biblioteca.
GetFileSize(StrFilename));
if not clOriginalFilePath.
Locate(‘FILENAME’, StrFilename,
[]) then
begin
clOriginalFilePath.Append;
clOriginalFilePath.
FieldByName(‘FILENAME’).
AsString := StrFilename;
clOriginalFilePath.
FieldByName(‘SIZEFILE’).
AsInteger := FileSize;
clOriginalFilePath.Post;
end
else
begin
clOriginalFilePath.Edit;
fevereiro
2015
14
//clOriginalFilePath.
FieldByName(‘FILENAME’).AsString
:= ExtractFileName(ffile);
clOriginalFilePath.
FieldByName(‘SIZEFILE’).
AsInteger := FileSize;
clOriginalFilePath.Post;
end;
lblFileSize.Caption :=
IntToStr(FileSize) + ‘ bytes’;
FileNumber := 1;
iTotalFiles := 0;
ProgressBar.Position := 0;
memoLog.Clear;
StrmInput := TFileStream.
Create(StrFilename,fmOpenRead
or fmShareDenyNone);
try
while StrmInput.Position <
StrmInput.Size do
begin
SequentialFile := ChangeF
ileExt((ExtractFilePath(StrFile
name) +
IncludeTrailingPathDeli
miter(tempFolder) + ExtractFile
Name(StrFilename)),’.’+Format(‘
%.03d’,[FileNumber]));
StrmOutput := TFileStream.
Create(SequentialFile
,fmCreate);
try
if StrmInput.Size -
StrmInput.Position < FileSize
then
FileSize := StrmInput.
Size - StrmInput.Position;
StrmOutput.
CopyFrom(StrmInput,FileSize);
memoLog.
Lines.Add(‘File: ‘ +
ExtractFileName(SequentialFile)
+ ‘ created successfully.’);
Application.
ProcessMessages;
finally
StrmOutput.Free;
end;
Inc(FileNumber);
Inc(iTotalFiles);
ProgressBar.Position :=
ProgressBar.Position + 1;
end;
finally
StrmInput.Free;
end;
ProgressBar.Max :=
iTotalFiles;
end;
Aplicativo para processar em lotes passo-a-passo
Inicialmente, foi concebida a ideia de realizar o processamento passo-a-
-passo, isto é, em vez de o nosso aplicativo anterior (FastFile) executar tudo
automaticamente, o usuário poderá acompanhar mais de perto de como a
coisa toda funciona.
Gerando um passo por vez poderá ter a noção de como as funções de
criação de lotes e a de leitura sincronizada (através de threads com a VCL)
interagem entre si e resultam harmoniosamente no elo de criação e leitura
das partes de um todo, “printando” o resultado em memo´s instanciados
dinamicamente (sim, não há problema desde que se tenha memória – em
um teste realizado aqui, foram criados 1000 memo´s sem problema algum).
Seguemabaixoduasfunções: umaparaobteroconteúdodeumTMemo
e a outra para checkar o seu status (ativo/destruído/não existe):
function TForm1.GetMemo(const
Id: integer): String;
begin
if MemoExists(Id) then
Result := TMemo(Self.
FindComponent(‘memo_’+
IntToStr(Id))).Text
else
Result := ‘’;
end;
procedure TForm1.
CheckStatusMemo(const Id:
integer);
var
garbage: TComponent;
begin
if MemoExists(Id) then
begin
garbage := Self.
FindComponent(‘memo_’+
IntToStr(Id));
FreeAndNil(garbage);
end;
end;
fevereiro
2015
15
Figura 04 – aplicativo passo-a-passo – função para a divisão dos arquivos
Figura 05 – aplicativo passo-a-passo – função para a união dos arquivos – note
que esta função não é necessária para o processo de leitura em lotes discutido no
nosso tema, mas útil para ilustrar o funcionamento inverso do nosso tema também.
Figura 06 – aplicativo passo-a-passo – Memo contendo o registro das operações,
como um Log.
Figura 07 – aplicativo passo-a-passo – Leitura do arquivo em Lotes.
Figura 08 – aplicativo passo-a-passo – Leitura do arquivo em Lotes, com método
de pesquisa.
Essa pesquisa por lote é interessante: o usuário digita o número do lote
desejado e obtém o conteúdo deste lote. Assim, pode-se pesquisar por vez, a
fimdeexibir(nestecaso,apenasumporvez)osdadoscontidosnele.Todosos
caracteres são os preenchidos pelo buffer das threads através de um “filtro”
(já citado acima).
Segue abaixo duas linhas do fonte responsáveis por esta função:
Memo4.Clear;
Memo4.Lines.
Add(GetMemo(StrToIntDef(Edit1.
Text, 1)));
Por fim, segue o código-fonte da thread chamada TFileReadThread:
fevereiro
2015
16
{ TFileReadThread }
constructor TFileReadThread.
Create(CreateSuspended:
Boolean;
const myTempFileName: string;
const myID, mySizeBuffer:
integer);
begin
inherited
Create(CreateSuspended);
self.strFileName :=
myTempFileName;
Self.Id := myID;
Self.iFileSize :=
mySizeBuffer;
Priority := tpNormal;
end;
destructor TFileReadThread.
Destroy;
begin
inherited;
end;
procedure TFileReadThread.
Execute;
begin
inherited;
FreeOnTerminate := True;
// if WaitForSingleObject(Mu
texHandle, INFINITE) = WAIT_
OBJECT_0 then
// begin
Synchronize(FileRead);
// end;
// ReleaseMutex(MutexHandle);
end;
procedure TFileReadThread.
FileRead;
var
Stream: TFileStream;
i: integer;
// Buffer: array[0..295600] of
AnsiChar; //1024 (kB) x size
of the file ...
Buffer: array of AnsiChar;
TempStr: string;
const Allowed = [‘A’ .. ‘Z’,
‘a’ .. ‘z’, ‘0’ .. ‘9’, ‘_’,
#13, #10, ‘-’, ‘’, ‘”’, ‘!’,
‘@’
,’#’, ‘$’, ‘%’, ‘¨’, ‘*’,
‘(‘, ‘)’, ‘{‘, ‘}’, ‘[‘, ‘]’,
‘<’, ‘>’, ‘.’, ‘:’, ‘;’, ‘,’ ,
‘?’, ‘!’, ‘/’,
‘+’, ‘-’, ‘´’, ‘`’, ‘=’, ‘^’,
‘~’, ‘&’, ‘ ‘];
begin
TempStr := ‘’;
SetLength(Buffer, self.
iFileSize + 1);
Stream := TFileStream.
Create(self.strFileName
,fmOpenRead);
try
Stream.Read(Buffer[0],
self.iFileSize + 1);
finally
Stream.Free;
end;
for i := Low(Buffer) to
High(Buffer) do
if (Buffer[i] in Allowed)
then
TempStr := TempStr +
Buffer[i];
frmMain.memoLog.
Lines.Add(‘File ‘ +
ExtractFileName(Self.
strFileName) + ‘ read
successfully.’);
frmMain.CreateMemos(Self.Id,
TempStr);
frmMain.ProgressBar.Position
:= frmMain.ProgressBar.Position
+ 1;
// Form1.strList.
AddObject(TempStr, TObject(Self.
Id));
end;
Conclusão
Foi explicado neste artigo o funcionamento em tempo real a leitura de
um arquivo grande em lotes, para isso dividindo cada um proporcionalmente
em um tamanho padrão fornecido pela própria aplicação, e assim preen-
chendo dados nestes lotes e trazendo estes dados temporariamente para os
componentes da VCL (através da instancialização de vários objetos TMemo´s,
fevereiro
2015
17
TClientDataSet´sesincronizaçõescomumTMemoparalogeumTProgressBar
para acompanhamento do processo).
Cada lote poderá ser criado e personalizado de forma livre para o im-
plementador – a nomenclatura padrão é o nome do arquivo original mais
o sequencial formatado em três casas decimais – exemplo ArquivoTal.001,
ArquivoTal.002, etc;
Foramrealizadostestesapenascomarquivosbinários/texto,processados
em todas as etapas (divisão/união/leitura temporários) sempre com fluxo de
dados do tipo TStream, aproveitando assim as funcionalidades de leitura por
posição através do seu método CopyFrom.
Oquequistrazeraquiéumaabordagemalternativaeeficienteparaalei-
turadearquivospesados,comoarquivosdecarga,porexemplo,ondesempre
sobrecarregam a entrada dos aplicativos devido ao seu tamanho – por mais
que existam soluções para contornar essa leitura grande, muitas oneram o
processadorevivedeerrosdefaltadememóriaecomtempodemasiadoparao
processamentolinhaalinha–imaginaumalinhaparacadainsertemumbanco
dedadosdeumarquivodecarga–sendomuitograndeoDelphinemvaiiniciar
a função – vai abortar com erros já citados de Exception “OutOfMemory”.
Portanto, com este aplicativo os arquivos grandes SEMPRE serão lidos,
desde que o Windows apresente memória para tal. Não é recomendável me-
móriacomaté2GBdeRAM;sempremaisdoqueissoapartirde3.Foitestado
com 3 GB com resultados satisfatórios. A leitura sempre funcionará e nunca
deixará o usuário “na mão”. Uma última e óbvia observação é a permissão
suporte@theclub.com.br
Hamden Vogel
Analista de Sistemas pós-graduado em Engenharia de Software pela
UPIS e Programador Delphi com larga experiência desde 2000, tem de-
senvolvido e vendido softwares em Delphi para a África e Estados Unidos,
além do mercado nacional. Colaborou com dicas e componentes para sites
especializadosemDelphi. Tambémdesenvolveemoutraslinguagenscomo
C/C++, ASP, PHP e .NET.
Sobre o autor
de espaço em disco e capacidade do mesmo, para permitir a leitura/escrita
e a capacidade de armazenamento dos lotes dos arquivos, respectivamente.
Essa é uma maneira multithread para agilizar a leitura dos lotes de um
arquivo em particular, dividindo uniformemente em tamanho seus respecti-
vos lotes e consumindo seus dados para dentro do programa em si, trazendo
uma forma eficiente de ler os dados de forma sequência, ordenada e prática.
Bonsestudosebonsprojetoscomesteartigo!Seguemosfontesdosprojetos
baseados neste nosso tema em anexo. Até o próximo artigo !
fevereiro
2015
18
Listagem 01: Código para criação de Triggers.
N
estemêsresolviescreverumpoucosobreoassuntoAudito-
riadedadosutilizandoosdenominadosGatinhos(Triggers)
no Banco de Dados SQL Server. Explicarei e criarei toda a
regra necessária para esta tarefa. Farei o uso da versão
2008 do SQL Server Express Edition, servindo também
de base para as outras versões. Utilizaremos também como ferramenta para
gerenciamento de dados o “Microsoft SQL Management Studio”, versão 10.
Antesdecomeçarmosaimplementaraideiadeauditoriadedadosemtabelas
do SQL Server, o tópico abaixo “Pré-requisitos” irá abranger um assunto con-
siderado primordial para a leitura deste artigo, ressaltando que este tópico já
se encontra publicado em um de nossos artigos.
Pré-Requisitos
Este tópico foi baseado no artigo “Trabalhando com o SQL Server 2008
Express Edition – Parte 2” do mês de Abril de 2011, para maiores detalhes
favor realizar a leitura do artigo na íntegra.
O que seria um Gatilho?
O gatilho é um objeto que é automaticamente executado assim que
efetuado um INSERT, DELETE ou UPDATE na tabela. Podemos criá-los de duas
maneiras,BEFORE(antes)eAFTER(após).OsgatilhosBEFOREdisparamantes
das modificações da instrução serem aplicadas, e antes de qualquer restrição
ser aplicada. Já os AFTER disparam após todas as restrições terem sido satis-
feitas, e após todas as alterações terem sido aplicadas à tabela de destino.
Como implementar?
Uma das formas mais rápidas e práticas seria com o Microsoft SQL Ma-
nagement Studio. Com o mesmo aberto deveremos clicar na tabela desejada
e escolher a Opção “Gatilho” e em seguida “Novo Gatilho”, Ver Imagem 01.
SQL Server e o uso de
Gatilhos para auditoria
de dados
Figura 01: Criar um Gatilho.
Teremos abaixo o código base para criação das TRIGGERS e em seguida a
explicação detalhada, Ver Listagem 01.
CREATE TRIGGER <Schema_Name,
sysname, Schema_Name>.<Trigger_
Name, sysname, Trigger_Name>
ON <Schema_Name, sysname,
Schema_Name>.<Table_Name,
sysname, Table_Name>
AFTER <Data_
Modification_Statements, ,
INSERT,DELETE,UPDATE>
AS
BEGIN
SET NOCOUNT ON;
END
fevereiro
2015
19
Detalhando o código da Listagem 01.
CREATE TRIGGER <Nome do Gatilho
a ser utilizado>
ON <Nome da Tabela afetada>
AFTER <Momento(Antes ou Depois)
/ da Operação (Inserir,Deletar
ou Atualizar)>
AS
BEGIN
<Código a ser programado>
END
Como funcionará a auditoria dos dados em nossa tabela do SQL Server?
O exemplo se baseará na auditoria em uma tabela, por exemplo a de
Clientes. Teremos todos os detalhes dos comandos de Inserção, Atualização e
Exclusãodosregistros.Nossosdadosserãoarmazenadosemmaisoutrasduas
tabelas, sendo a de LOGS e LOGS_DATA, um clássico relacionamento Mestre-
-Detalhe. Teremos os campos detalhados abaixo:
Tabela LOGS
ID_LOGS: campo auto-incremento (chave primária) para se relacionar
com a tabela LOGS_DATA;
CODIGO: código do Cliente;
DATA_HORA: Data/Hora da operação;
OPERACAO: tipo de operação [(I)nclusão,(A)lteração, (E)xclusão];
USUARIOID: código do usuário.
Tabela LOGS_DATA
ID_LOGS_DATA: campo auto-incremento (chave primária);
ID_LOGS: chave estrangeira, campo resposnsável pelo relacionamento
com a tabela LOGS;
NOME CAMPO: Nome do Campo da tabela de Clientes;
TIPO_CAMPO: Tipo de campo utilizado;
VALOR: Conteúdo do campo.
Para exemplificar melhor, veremos maiores detalhes do esquema na
Imagem 02.
Os Comandos de
Insert, Update e Delete
serão executados com
base na tabela de
CLIENTES.
O resultado da execução
dos Gatilhos serão armaze-
nados nas tabelas LOGS e
LOGS_DATA.
Figura 02: Esquema de funcionamento.
fevereiro
2015
20
Listagem 02: Script da tabela Clientes.
Listagem 03: Script da tabela Logs.
Listagem 04: Script da tabela Logs_Data.
Definindo a Tabela Principal (CLIENTES)
O script de criação da tabela de clientes poderemos conferir na Listagem
02.
CREATE TABLE [dbo].[CLIENTES](
[CODIGO] [int] IDENTITY(1,1)
NOT NULL,
[NOME] [varchar](50) NULL,
[TIPO] [int] NULL,
[CIDADE] [varchar](50) NULL,
[UF] [varchar](2) NULL,
[DOCUMENTO] [varchar](20)
NULL,
[DATANASCIMENTO] [date] NULL,
[SEXO] [varchar](1) NULL,
[TELEFONE] [varchar](20) NULL,
[OBSERVACAO] [varchar](80)
NULL,
[USUARIOID] [int] NULL,
CONSTRAINT [PK_CLIENTES]
PRIMARY KEY CLUSTERED
(
[CODIGO] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_
ROW_LOCKS = ON, ALLOW_PAGE_
LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Definindo as Tabelas de Logs (LOGS e LOGS_DATA)
Para a tabela de Logs teremos o script de criação conforme Listagem 03.
CREATE TABLE [dbo].[LOGS](
[ID_LOGS] [int] IDENTITY(1,1)
NOT NULL,
[CODIGO] [int] NULL,
[DATA_HORA] [datetime] NULL,
[OPERACAO] [varchar](1) NULL,
[USUARIOID] [int] NULL,
CONSTRAINT [PK_CADASTRO_UNICO_
LOGS] PRIMARY KEY CLUSTERED
(
[ID_LOGS] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_
ROW_LOCKS = ON, ALLOW_PAGE_
LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Já para a tabela Detalhe Logs_Data, o script responsável pela criação da
tabela poderá ser consultado na Listagem 04.
CREATE TABLE [dbo].[LOGS_DATA](
[ID_LOGS_DATA] [int]
IDENTITY(1,1) NOT NULL,
[ID_LOGS] [int] NULL,
[NOME_CAMPO] [varchar](50)
NULL,
[TIPO_CAMPO] [varchar](50)
NULL,
[VALOR] [varchar](80) NULL,
CONSTRAINT [PK_LOGS_DATA]
PRIMARY KEY CLUSTERED
(
[ID_LOGS_DATA] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_
ROW_LOCKS = ON, ALLOW_PAGE_
LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Criando o Gatilho de “Insert”
Ogatilho“INSERT_LOGS”farásempreotrabalhoapósainserçãodealgum
registro, Ver código comentado na listagem 05.
CREATE TRIGGER [dbo].[INSERT_
LOGS]
ON [dbo].[CLIENTES]
fevereiro
2015
21
Listagem 05: Gatilho Insert_Logs.
AFTER INSERT
AS
BEGIN
DECLARE @CODIGO INT
DECLARE @NOME VARCHAR(50)
DECLARE @TIPO INT
DECLARE @CIDADE VARCHAR(50)
DECLARE @UF VARCHAR(2)
DECLARE @DOCUMENTO VARCHAR(20)
DECLARE @DATANASCIMENTO DATE
DECLARE @SEXO VARCHAR(1)
DECLARE @TELEFONE varchar(20)
DECLARE @OBSERVACAO
varchar(80)
DECLARE @USUARIOID int
DECLARE @ID_LOGS int
			
Para cada campo a ser inserido foi necessário criar uma variável.
SELECT
@CODIGO = CODIGO,
@NOME = NOME,
@TIPO = TIPO,
@CIDADE = CIDADE,
@UF = UF,
@DOCUMENTO = DOCUMENTO,
@DATANASCIMENTO =
DATANASCIMENTO,
@SEXO = SEXO,
@TELEFONE = TELEFONE,
@OBSERVACAO = OBSERVACAO,
@USUARIOID = USUARIOID
FROM Inserted
O próximo passo será de alimentar todas as variáveis com os registros
inseridos na tabela de Clientes. Podemos realizar esta tarefa com o comando
“Inserted”, o mesmo responsável pelo estado de inserção dos dados.
INSERT INTO LOGS (CODIGO,
DATA_HORA, OPERACAO, USUARIOID)
VALUES
(@CODIGO,GETDATE(),’I’, @
USUARIOID)
Para inserir na tabela “Mestre” denominada Logs faremos um simples
INSERT,passandoporparâmetroocódigooriundodavariáveldeclaradaacima,
a data atual utilizando o comando “GetDate()”, a operação “I” que significa
modo de inserção e o usuário ativo no momento.
SELECT @ID_LOGS = ID_LOGS
FROM LOGS INSERTED
Já para inserirmos os dados na tabela “Detalhe” Logs_data o primeiro
passo deveremos realizar um select usando o ID_LOGS para localizarmos o
registro corrente, com o auxílio do comando “Inserted”.
IF (@CODIGO IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’CODIGO’,’INT’,@
CODIGO)
END
IF (@NOME IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’NOME’,’VARCHAR’,@
NOME)
END
IF (@TIPO IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’TIPO’,’INT’,@TIPO)
END
IF (@CIDADE IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’CIDADE’,’VARCHAR’,@
CIDADE)
END
fevereiro
2015
22
IF (@UF IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’UF’,’VARCHAR’,@UF)
END
IF (@DOCUMENTO IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’DOCUMENTO’,’VARCHAR’,@
DOCUMENTO)
END
IF (@DATANASCIMENTO IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’DATANASCIMENTO’,’DATE’,@
DATANASCIMENTO)
END
IF (@SEXO IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’SEXO’,’VARCHAR’,@
SEXO)
END
IF (@TELEFONE IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’TELEFONE’,’VARCHAR’,@
TELEFONE)
END
IF (@OBSERVACAO IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’OBSERVACAO’,’VARCHAR’,@
OBSERVACAO)
END
IF (@USUARIOID IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’USUARIOID’,’INT’,@
USUARIOID)
END
END
Nos próximos passos faremos uma verificação campo a campo se os
mesmos não se encontram nulos e logo em seguida executaremos um Insert
na tabela Logs_data, respeitando a ordem dos campos Id_Logs, Nome, Tipo
e o Valor do campo.
Criando o Gatilho de “Update”
O gatilho “UPDATE_LOGS” fará sempre o trabalho após a atualização de
algum registro, Ver código comentado na listagem 06.
CREATE TRIGGER [dbo].[UPDATE_
LOGS]
ON [dbo].[CLIENTES]
AFTER UPDATE
AS
BEGIN
DECLARE @CODIGO INT
DECLARE @NOME VARCHAR(50)
DECLARE @TIPO INT
DECLARE @CIDADE VARCHAR(50)
DECLARE @UF VARCHAR(2)
DECLARE @DOCUMENTO VARCHAR(20)
DECLARE @DATANASCIMENTO DATE
DECLARE @SEXO VARCHAR(1)
DECLARE @TELEFONE varchar(20)
DECLARE @OBSERVACAO
varchar(80)
DECLARE @USUARIOID int
DECLARE @ID_LOGS int
Para cada campo a ser atualizado foi necessário criar uma variável.
fevereiro
2015
23
SELECT
@CODIGO =
ISNULL(CODIGO,0),
@NOME = ISNULL(NOME,’
‘),
@TIPO = ISNULL(TIPO,’ ‘),
@CIDADE = ISNULL(CIDADE,’ ‘),
@UF = ISNULL(UF,’ ‘),
@DOCUMENTO =
ISNULL(DOCUMENTO,’ ‘),
@DATANASCIMENTO =
ISNULL(DATANASCIMENTO,’ ‘),
@SEXO = ISNULL(SEXO,’ ‘),
@TELEFONE = ISNULL(TELEFONE, ‘
‘),
@OBSERVACAO =
ISNULL(OBSERVACAO,’ ‘),
@USUARIOID =
ISNULL(USUARIOID,’ ‘)
FROM INSERTED
O próximo passo será de alimentar todas as variáveis com os registros
atualizadosnatabeladeClientes.Podemosrealizarestatarefacomocomando
“Inserted” e analisando se o campo está nulo com a função “ISNULL”.
DECLARE @NOME_OLD VARCHAR(40)
DECLARE @TIPO_OLD INT
DECLARE @CIDADE_OLD
VARCHAR(50)
DECLARE @UF_OLD VARCHAR(2)
DECLARE @DOCUMENTO_OLD
VARCHAR(20)
DECLARE @DATANASCIMENTO_OLD
DATE
DECLARE @SEXO_OLD VARCHAR(1)
DECLARE @TELEFONE_OLD
VARCHAR(20)
DECLARE @OBSERVACAO_OLD
VARCHAR(80)
DECLARE @USUARIOID_OLD int
Nas variáveis acima deveremos armazenar todos os valores antigos dos
campos que foram atualizados.
SELECT
@NOME_OLD =
ISNULL(NOME,’ ‘),
@TIPO_OLD = ISNULL(TIPO,’ ‘),
@CIDADE_OLD = ISNULL(CIDADE,’
‘),
@UF_OLD = ISNULL(UF,’ ‘),
@DOCUMENTO_OLD =
ISNULL(DOCUMENTO,’ ‘),
@DATANASCIMENTO_OLD =
ISNULL(DATANASCIMENTO,’ ‘),
@SEXO_OLD = ISNULL(SEXO,’ ‘),
@TELEFONE_OLD =
ISNULL(TELEFONE, ‘ ‘),
@OBSERVACAO_OLD =
ISNULL(OBSERVACAO,’ ‘),
@USUARIOID_OLD =
ISNULL(USUARIOID,’ ‘)
FROM DELETED
		
Poderemos recuperar estes campos utilizando o comando “Deleted”.
INSERT INTO LOGS (CODIGO, DATA_
HORA, OPERACAO, USUARIOID)
VALUES
(@CODIGO,GETDATE(),’A’, @
USUARIOID)
Seguindo a mesma lógica descrita anteriormente, a inserção na tabela
“Mestre” denominada Logs faremos um simples INSERT, passando por parâ-
metro o código oriundo da variável declarada acima, a data atual utilizando o
comando “GetDate()”, a operação “A” que significa modo de atualização e o
usuário ativo no momento.
SELECT @ID_LOGS = ID_LOGS
FROM LOGS INSERTED
Parainserirmososdadosnatabela“Detalhe”Logs_dataoprimeiropasso
deveremos realizar um select usando o ID_LOGS para localizarmos o registro
corrente, com o auxílio do comando “Inserted”.
fevereiro
2015
24
Listagem 06: Gatilho Update_Logs.
IF (UPDATE(NOME) AND (@NOME
<> @NOME_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’NOME’,’VARCHAR’,@
NOME)
END
IF (UPDATE(TIPO) AND (@TIPO <>
@TIPO_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’TIPO’,’INT’,@TIPO)
END
IF (UPDATE(CIDADE) AND (@
CIDADE <> @CIDADE_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’CIDADE’,’VARCHAR’,@
CIDADE)
END
IF (UPDATE(UF) AND (@UF <> @
UF_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’UF’,’VARCHAR’,@UF)
END
IF (UPDATE(DOCUMENTO) AND (@
DOCUMENTO <> @DOCUMENTO_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’DOCUMENTO’,’VARCHAR’,@
DOCUMENTO)
END
IF (UPDATE(DATANASCIMENTO)
AND (@DATANASCIMENTO <> @
DATANASCIMENTO_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’DATANASCIMENTO’,’DATE’,@
DATANASCIMENTO)
END
IF (UPDATE(SEXO) AND (@SEXO <>
@SEXO_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’SEXO’,’VARCHAR’,@
SEXO)
END
IF (UPDATE(TELEFONE) AND (@
TELEFONE <> @TELEFONE_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’TELEFONE’,’VARCHAR’,@
TELEFONE)
END
IF (UPDATE(OBSERVACAO) AND (@
OBSERVACAO <> @OBSERVACAO_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’OBSERVACAO’,’VARCHAR’,@
OBSERVACAO)
END
IF (UPDATE(USUARIOID) AND (@
USUARIOID <> @USUARIOID_OLD))
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’USUARIOID’,’INT’,@
USUARIOID)
END
END
fevereiro
2015
25
Listagem 07: Gatilho Delete_Logs.
Por final faremos uma verificação campo a campo se os mesmos estão
em modo de Update e comparamos o valor antigo com o valor novo, caso
encontre alguma divergência executaremos um Insert na tabela Logs_data,
respeitando a ordem dos campos ID_LOGS, Nome, Tipo e o Valor do campo.
Criando o Gatilho de “Delete”
Ogatilho“DELETE_LOGS”farásempreotrabalhoapósaExclusãodealgum
registro, Ver código comentado na listagem 07.
CREATE TRIGGER [dbo].[DELETE_
LOGS]
ON [dbo].[CLIENTES]
AFTER DELETE
AS
BEGIN
DECLARE @ID_LOGS INT
DECLARE @CODIGO_OLD INT
DECLARE @NOME_OLD
VARCHAR(40)
DECLARE @TIPO_OLD INT
DECLARE @CIDADE_OLD
VARCHAR(50)
DECLARE @UF_OLD VARCHAR(2)
DECLARE @DOCUMENTO_OLD
VARCHAR(20)
DECLARE @DATANASCIMENTO_OLD
DATE
DECLARE @SEXO_OLD
VARCHAR(1)
DECLARE @TELEFONE_OLD
VARCHAR(20)
DECLARE @OBSERVACAO_OLD
VARCHAR(80)
DECLARE @USUARIOID_OLD int
Para cada campo a ser atualizado foi necessário criar uma variável.
SELECT
@CODIGO_OLD = CODIGO,
@NOME_OLD = NOME,
@TIPO_OLD = TIPO,
@CIDADE_OLD = CIDADE,
@UF_OLD = UF,
@DOCUMENTO_OLD = DOCUMENTO,
@DATANASCIMENTO_OLD =
DATANASCIMENTO,
@SEXO_OLD = SEXO,
@TELEFONE_OLD = TELEFONE,
@OBSERVACAO_OLD = OBSERVACAO,
@USUARIOID_OLD = USUARIOID
FROM DELETED
O próximo passo será de alimentar todas as variáveis com os registros
excluídos na tabela de Clientes. Podemos realizar esta tarefa com o comando
“Deleted”.
INSERT INTO LOGS (CODIGO,
DATA_HORA, OPERACAO, USUARIOID)
VALUES
(@CODIGO_OLD,GETDATE(),’E’, @
USUARIOID_OLD)
Novamentefaremosainserçãonatabela“Mestre”comocomandoINSERT,
passandoporparâmetroocódigooriundodavariáveldeclaradaacima,adata
atualutilizandoocomando“GetDate()”,aoperação“E”quesignificamodode
exclusão e o usuário ativo no momento.
SELECT @ID_LOGS = ID_LOGS
FROM LOGS INSERTED
Para a tabela “Detalhe” Logs_data deveremos realizar um select usando
o ID_LOGS para localizarmos o registro corrente, com o auxílio do comando
“Inserted”.
IF (@CODIGO_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’CODIGO’,’INT’,@
CODIGO_OLD)
END
IF (@NOME_OLD IS NOT NULL)
fevereiro
2015
26
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’NOME’,’VARCHAR’,@
NOME_OLD)
END
IF (@TIPO_OLD IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’TIPO’,’INT’,@TIPO_
OLD)
END
IF (@CIDADE_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’CIDADE’,’VARCHAR’,@
CIDADE_OLD)
END
IF (@UF_OLD IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’UF’,’VARCHAR’,@
UF_OLD)
END
IF (@DOCUMENTO_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’DOCUMENTO’,’VARCHAR’,@
DOCUMENTO_OLD)
END
IF (@DATANASCIMENTO_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’DATANASCIMENTO’,’DATE’,@
DATANASCIMENTO_OLD)
END
IF (@SEXO_OLD IS NOT NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’SEXO’,’VARCHAR’,@
SEXO_OLD)
END
IF (@TELEFONE_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’TELEFONE’,’VARCHAR’,@
TELEFONE_OLD)
END
IF (@OBSERVACAO_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_
LOGS,’OBSERVACAO’,’VARCHAR’,@
OBSERVACAO_OLD)
END
IF (@USUARIOID_OLD IS NOT
NULL)
BEGIN
INSERT INTO LOGS_DATA (ID_
LOGS, NOME_CAMPO, TIPO_CAMPO,
VALOR) VALUES
(@ID_LOGS,’USUARIOID’,’INT’,@
USUARIOID_OLD)
END
END
Esta etapa será idêntica aos gatilhos criados anteriormente, pois realiza-
mos a verificação se o campo não encontra-se nulo realizando a inserção dos
fevereiro
2015
27
dados corretamente.
Poderemos conferir no Banco os objetos criados, Ver Imagem 03.
Figura 03: Objetos criados com sucesso.
Testando os gatilhos
Os gatilhos são objetos executados diretamente no Banco de Dados, de
acordocomaoperaçãodesejada.Paradispará-lospoderíamosutilizarqualquer
interface mas para nosso teste a forma mais rápida para realizar esta tarefa
seria diretamente pelo SSMSE (SQL Server Management Studio Express).
Abrindo a tabela de Clientes poderemos inserir, alterar ou excluir alguns
dadoseapósumaconsultanastabelasLogseLogs_Datateremosumresultado
idêntico ao da Figura 04.
Conclusões
O uso de Triggers(gatilhos) em auditoria de sistemas é uma prática muito
usual,poistodootrabalhoficaacargodoservidor,livrandoainterfacedolado
cliente para esta tarefa. Procurei neste artigo explorar um exemplo de como
deveremosimplementarestatécnicautilizandooSQLServer.Fiquemavontade
para modificar e adaptar estas rotinas conforme a necessidade do momento.
Um abraço a todos e até o mês que vem!
Figura 04: Resultado final.
thiago@theclub.com.br
Thiago Cavalheiro Montebugnoli
adora aprender novas tecnologias. Formado pela Faculdade de Tecnologia de Botucatu
– SP (FATEC), já desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco
de Dados SQL Server e Firebird. Atualmente trabalha no Centro de Processamento de Dados da
Prefeitura Municipal de Itaí-SP é colunista mensal da Revista The Club Megazine e é consultor
Técnico do The Club. Possui as seguintes certificações: MCP - Microsoft Certified Professional,
MCTS-MicrosoftCertifiedTechnologySpecialist,MCAD-MicrosoftCertifiedApplicationDeveloper
e MCSD - Microsoft Certified Solution Developer.
Sobre o autor
fevereiro
2015
28
Listagem 1 – Uses.
Listagem 2 – Atalho.
Nesteartigovamosdemonstraralgumasfunçõesbásicas,masimportantes
para o bom uso de seu aplicativo.
Codificação
Primeiramente vamos criar o recurso de ‘Atalho’ do seu App, ou seja,
ao instalarmos o interessante é que o mesmo já seja exibido na tela inicial,
portanto, vamos iniciar a codificação.
AdicioneasUsesqueutilizaremosnos2exemplos,‘Atalho’e‘Notificações’.
FMX.Platform.Android,
FMX.MobilePreview,
FMX.Helpers.Android,
Androidapi.JNI.
GraphicsContentViewText,
AndroidAPI.jni.OS,
Androidapi.JNI.JavaTypes,
AndroidApi.Jni.App,
AndroidApi.JniBridge,
FMX.Notification;
Apósissovamoscriaroprocedimentoresponsávelpelacriaçãodoatalho.
Em um de nossos artigos:
Delphi XE5 - Trabalhando com Intents: http://theclub.com.br/Restrito/
Revistas/201407/dxe51407.aspx
Vimos como trabalhar com Intents no Android, portanto, vamos repetir
o mesmo processo aqui.
DelphiXE5–Adicionando
Recursos
procedure Atalho;
var
Intent: JIntent;
addIntent: JIntent;
I : Integer;
wIcone : JIntent_
ShortcutIconResource;
begin
Intent := TJIntent.JavaClass.
init(SharedActivityContext,
SharedActivityContext.
getClass);
Intent.setAction(TJIntent.
JavaClass.ACTION_MAIN);
addIntent := TJIntent.Create;
addIntent.putExtra(TJIntent.
JavaClass.EXTRA_SHORTCUT_INTENT,
TJParcelable.Wrap((Intent as
ILocalObject).GetObjectID));
addIntent.putExtra(TJIntent.
JavaClass.EXTRA_SHORTCUT_NAME,
StringToJString(Application.
Title));
addIntent.
setAction(StringToJString(‘com.
android.launcher.action.
INSTALL_SHORTCUT’));
I := SharedActivity.
getResources.getIdentifier(Str
ingToJString(‘ic_launcher’),
StringToJString(‘drawable’),
StringToJString(‘com.
embarcadero.Project1’));
wIcone := TJIntent_
fevereiro
2015
29
Listagem 3 – Informações do Atalho.
Listagem 4 – Permissão.
Listagem 5 - AndroidManifest.template.xml.
ShortcutIconResource.JavaClass.
fromContext(SharedActivityConte
xt, I);
addIntent.putExtra(TJIntent.
JavaClass.EXTRA_SHORTCUT_
ICON_RESOURCE, TJParcelable.
Wrap((wIcone as ILocalObject).
GetObjectID));
SharedActivityContext.
sendBroadcast(addIntent);
end;
Neste procedimento é necessário indicar o nome da imagem e de seu
projeto para a criação do atalho, conforme trecho abaixo:
I := SharedActivity.
getResources.getIdentifier(Str
ingToJString(‘ic_launcher’),
StringToJString(‘drawable’),
StringToJString(‘com.
embarcadero.Project1’));
Com toda a codificação pronta resta apenas ‘liberar’ a permissão de uso
em seu ‘AndroidManifest.template.xml’, portanto, adicione a seguinte linha
em seu arquivo:
<uses-permission
android:name=”com.android.
launcher.permission.INSTALL_
SHORTCUT”/>
<?xml version=”1.0”
encoding=”utf-8”?>
<!-- BEGIN_INCLUDE(manifest)
-->
<manifest xmlns:android=”http://
schemas.android.com/apk/res/
android”
package=”%package%”
android:versionCode=”%v
ersionCode%”
android:versionName=”%v
ersionName%”>
<!-- This is the platform
API where NativeActivity was
introduced. -->
<uses-sdk android:minSdkVer
sion=”%minSdkVersion%” />
<uses-permission
android:name=”com.android.
launcher.permission.INSTALL_
SHORTCUT”/>
<%uses-permission%>
<application android:persis
tent=”%persistent%”
android:restoreAnyVersi
on=”%restoreAnyVersion%”
android:label=”%label%”
android:installLocation
=”%installLocation%”
android:debuggable=”%de
buggable%”
android:largeHeap=”%largeHeap%”
android:icon=”%icon%”
android:theme=”%theme%”>
<!-- Our activity is
a subclass of the built-in
NativeActivity framework class.
This will take
care of integrating with our
NDK code. -->
<activity
android:name=”com.embarcadero.
firemonkey.FMXNativeActivity”
android:label=”%activityLabel%”
android:configCh
anges=”orientation|keyboardHidd
en”>
<!-- Tell
NativeActivity the name of our
.so -->
<meta-data
android:name=”android.app.lib_
name”
android:value=”%libNameValue%”
/>
<intent-filter>
<action
android:name=”android.intent.
action.MAIN” />
<category
android:name=”android.intent.
category.LAUNCHER” />
</intent-filter>
</activity>
<receiver
android:name=”com.embarcadero.
firemonkey.notifications.
FMXNotificationAlarm” />
fevereiro
2015
30
Listagem 4 – Notificação.
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
Apósfinalizarmosessasconfiguraçõesbastaadicionaraprocedure
‘Atalho’ em seu projeto e se tudo correu bem seu ícone será criado na tela
inicial conforme imagem abaixo:
Imagem 1 – Atalho.
Agora vamos adicionar o segundo recurso que é utilizado por todos os
aplicativos que conhecemos que são as ‘Notificações’, aquelas mensagens
que são exibidas avisando se alguma mensagem chegou ou se há alguma
atualização disponível.
O Delphi já possui um componente próprio para trabalharmos com este
recurso,portanto,adicioneemseuprojetoocomponente‘TNotificationCenter’
em nosso exemplo o nomeamos de ‘Mensagem’. Para a criação e exibição da
mensagem em seu aparelho podemos utilizar a procedure ‘Notificacao’, veja
que indicamos 2 informações básicas.
‘AlertBody’ = É a mensagem que
deseja exibir;
‘FireDate’ = O momento em que
deseja exibir a mensagem.
procedure Notificacao;
Var
Notification: TNotification;
begin
If Notificacao.Supported Then
begin
Notification := Notificacao.
CreateNotification;
Try
Notification.AlertBody :=
‘The-Club’;
Notification.FireDate :=
Now;
Notificacao.ScheduleNotificat
ion(Notification);
Finally
Notification.DisposeOf;
end;
end;
Após isso basta chamar este procedimento, veja imagem abaixo:
Imagem 2 – Notificação.
Conclusão
Neste artigo vimos como adicionar dois recursos muito úteis ao dia-a-dia
de um aplicativo móvel, os recursos apresentados podem ser melhorados e
utilizados para diversas funções.
Espero que tenham gostado desta dica e até a próxima!
suporte@theclub.com.br
Jeferson Silva de Lima
Consultor The Club.
Sobre o autor
fevereiro
2015
05
fevereiro
2015

Mais conteúdo relacionado

Semelhante a Bootstrap e máscaras no Visual Studio

Unidade 5 - Preparando Apresentações com o BR Office Impress
Unidade 5  - Preparando Apresentações com o BR Office ImpressUnidade 5  - Preparando Apresentações com o BR Office Impress
Unidade 5 - Preparando Apresentações com o BR Office ImpressRogerio P C do Nascimento
 
Alta Performance em Aplicações Web
Alta Performance em Aplicações WebAlta Performance em Aplicações Web
Alta Performance em Aplicações WebAnderson Aguiar
 
Otimizacao Front-End para WordPress - OlhoSEO 2013
Otimizacao Front-End para WordPress - OlhoSEO 2013Otimizacao Front-End para WordPress - OlhoSEO 2013
Otimizacao Front-End para WordPress - OlhoSEO 2013Guga Alves
 
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...Dextra Sistemas / Etec Itu
 
Surpreenda-se com o Silverlight 3
Surpreenda-se com o Silverlight 3Surpreenda-se com o Silverlight 3
Surpreenda-se com o Silverlight 3Rodrigo Kono
 
Parse Push Notification - O Lado negro da força
Parse Push Notification - O Lado negro da forçaParse Push Notification - O Lado negro da força
Parse Push Notification - O Lado negro da forçaRudson Lima
 
Guião demotécnica
Guião demotécnicaGuião demotécnica
Guião demotécnicaSilvio Dias
 
Guiao demotecnica
Guiao demotecnicaGuiao demotecnica
Guiao demotecnicaSilvio Dias
 
Infraestrutura como código Terraform aws openshift Ansible
Infraestrutura como código Terraform aws openshift AnsibleInfraestrutura como código Terraform aws openshift Ansible
Infraestrutura como código Terraform aws openshift AnsibleClaudemir de Almeida Rosa
 
HTML5 e CSS3 – rápido e eficaz para o presente
HTML5 e CSS3 – rápido e eficaz para o presenteHTML5 e CSS3 – rápido e eficaz para o presente
HTML5 e CSS3 – rápido e eficaz para o presentept_programar
 
Manual Detalhado de Instrução ao Basecamp
Manual Detalhado de Instrução ao BasecampManual Detalhado de Instrução ao Basecamp
Manual Detalhado de Instrução ao BasecampErickSerrat
 
Design System: Dominando o Design at Scale
Design System: Dominando o Design at ScaleDesign System: Dominando o Design at Scale
Design System: Dominando o Design at ScaleGuilherme Gonzalez
 
1402 - Revista - AspNet.pdf
1402 - Revista - AspNet.pdf1402 - Revista - AspNet.pdf
1402 - Revista - AspNet.pdfjoaoJunior93
 

Semelhante a Bootstrap e máscaras no Visual Studio (20)

Unidade 5 - Preparando Apresentações com o BR Office Impress
Unidade 5  - Preparando Apresentações com o BR Office ImpressUnidade 5  - Preparando Apresentações com o BR Office Impress
Unidade 5 - Preparando Apresentações com o BR Office Impress
 
Unidade 7 slides digital na escola
Unidade 7 slides digital na escolaUnidade 7 slides digital na escola
Unidade 7 slides digital na escola
 
Unidade 7 Slides digital na escola
Unidade 7 Slides digital na escolaUnidade 7 Slides digital na escola
Unidade 7 Slides digital na escola
 
Alta Performance em Aplicações Web
Alta Performance em Aplicações WebAlta Performance em Aplicações Web
Alta Performance em Aplicações Web
 
Otimizacao Front-End para WordPress - OlhoSEO 2013
Otimizacao Front-End para WordPress - OlhoSEO 2013Otimizacao Front-End para WordPress - OlhoSEO 2013
Otimizacao Front-End para WordPress - OlhoSEO 2013
 
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
VSSUMMIT 2023 - Como partir do zero e entregar uma API Profissional com .NET ...
 
Surpreenda-se com o Silverlight 3
Surpreenda-se com o Silverlight 3Surpreenda-se com o Silverlight 3
Surpreenda-se com o Silverlight 3
 
Parse Push Notification - O Lado negro da força
Parse Push Notification - O Lado negro da forçaParse Push Notification - O Lado negro da força
Parse Push Notification - O Lado negro da força
 
Guião demotécnica
Guião demotécnicaGuião demotécnica
Guião demotécnica
 
Guiao demotecnica
Guiao demotecnicaGuiao demotecnica
Guiao demotecnica
 
Dreamweaver aula 1
Dreamweaver aula 1Dreamweaver aula 1
Dreamweaver aula 1
 
Infraestrutura como código Terraform aws openshift Ansible
Infraestrutura como código Terraform aws openshift AnsibleInfraestrutura como código Terraform aws openshift Ansible
Infraestrutura como código Terraform aws openshift Ansible
 
HTML5 e CSS3 – rápido e eficaz para o presente
HTML5 e CSS3 – rápido e eficaz para o presenteHTML5 e CSS3 – rápido e eficaz para o presente
HTML5 e CSS3 – rápido e eficaz para o presente
 
Manual Detalhado de Instrução ao Basecamp
Manual Detalhado de Instrução ao BasecampManual Detalhado de Instrução ao Basecamp
Manual Detalhado de Instrução ao Basecamp
 
Revista programar 23
Revista programar 23Revista programar 23
Revista programar 23
 
Design System: Dominando o Design at Scale
Design System: Dominando o Design at ScaleDesign System: Dominando o Design at Scale
Design System: Dominando o Design at Scale
 
Tutorial codeigniter
Tutorial codeigniterTutorial codeigniter
Tutorial codeigniter
 
1402 - Revista - AspNet.pdf
1402 - Revista - AspNet.pdf1402 - Revista - AspNet.pdf
1402 - Revista - AspNet.pdf
 
Web components
Web componentsWeb components
Web components
 
Web components
Web componentsWeb components
Web components
 

Bootstrap e máscaras no Visual Studio

  • 3. fevereiro 2015 03 18 Índice Dicas The Club 30 Editorial 04 11 Autor: Hamden Vogel 05 Autor: Marlon Aparecido Branco Valentino 28 SQL Server - uso de Gatilhos para auditoria de dados Autor: Thiago C. Montebugnoli Autor: Jeferson Silva de Lima Bootstrap – Pontapé inicial na utilização do Framework pelo Visual Studio FastFile - Aplicativo Leitor MultiThread para Arquivos Grandes em Lotes Delphi XE5 – Adicionando Recursos
  • 4. fevereiro 2015 04 Delphi é marca registrada da Borland International, as demais marcas citadas são registradas pelos seus respectivos proprietários. Thiago Montebugnoli- Editor Chefe thiago@theclub.com.br Caro Leitor, Primeiramente desejo a todos boas festas neste mês de Fevereiro, pois afinal nosso país é o único onde se comemora esta grande festa, o Carnaval. Voltando ao nosso foco principal que são artigos sobre programação, neste mês, nosso consultor técnico Marlon Aparecido Branco Valentino escreveu mais um artigo sobre o assunto BootStrap denominado: “Bootstrap - Pontapé inicial na utiliza- ção do Framework pelo Visual Studio”. Desta vez ele procurou abranger algumas configurações e dicas importantes para quem está começando a trabalhar com estatecnologia.Nossooutroconsultor,JefersonSilvadeLima,nosensinoucomo adicionarrecursosemaplicativosmóveis,comoporexemplo:adicionarorecurso de atalho na aplicação ou até mesmo as denominadas Notificações. Implemen- tações como estas, tornam o aplicativo muito mais atraente e prático para o usuário.JánossocolaboradorHamdenVogelredigiuoartigo“FastFile–Aplicativo LeitorMultiThreadparaArquivosGrandesemLotes”oqualeleexplicadetalhada- mente o funcionamento em tempo real para a leitura de um arquivo grande em pequenos lotes. Para isto ele divide proporcionalmente em tamanhos padrões e fornecido pela própria aplicação, apresentando uma abordagem alternativa para os desenvolvedores realizarem uma fácil implementação em seus sistemas. ParafinalizararevistaeuutilizeioSQLServerjuntocomosdenominadosgatilhos para a auditoria de dados. Procurei ser bem prático e rápido para apresentar uma alternativa para quem procura este tipo de controle, independentemente da interface utilizada no momento. Vou ficando por aqui, Desejo a todos uma boa leitura e até o mês que vem! Abraços Av. Profº Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150 Informações e Suporte: (14) 3732-1529 Internet http://www.theclub.com.br Cadastro: cadastro@theclub.com.br Suporte: suporte@theclub.com.br Informações: info@theclub.com.br Skype Cadastro: theclub_cadastro Skype Suporte: theclub_linha1 theclub_linha2 theclub_linha3 www.twitter.com/theclubbr Copyright The Club 2013 Diretor Técnico Marcos César Silva Diagramação Vitor M. Rodrigues Design Vitor M. Rodrigues Revisão Denise Blair Colunistas Hamden Vogel Jeferson Silva de Lima Luciano Pimenta Lucas Vieira de Oliveira Thiago Cavalheiro Montebugnoli Impressão e acabamento: GRIL - Gráfica e Editora Taquarituba-SP - Tel. (14) 3762-1345 Reprodução A utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da revista “The Club Megazine” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais. Editorial
  • 5. fevereiro 2015 05 Introdução Continuando a matéria sobre o Framework do Twitter, agora partiremos para a criação de um aplicativo para web ou web site pelo Visual Studio, a Microsoft facilitou (e muito) esta parte, pois é possível criar um projeto pelo VisualStudio,quejávemcomalgumaspáginasprontasparaodesenvolvimen- to, com o layout no padrão do Bootstrap, desta forma você pode começar a desenvolver seu aplicativo para web ou web site de maneira mais rápida, pois nãoseránecessáriocriaraspáginasbásicascomoHome,Sobre,Contato,Login, CadastrodeUsuário,poisessaspáginasjávemprontas,seráapenasnecessário alterar o layout do jeito que você preferir (obs: para fazer tais alterações é necessário ter conhecimento básico de HTML, CSS, JavaScript). Criando um projeto no Visual Studio 2013 No andamento deste artigo utilizarei a versão do Visual Studio 2013 que está disponível apenas em inglês. Iremos começar criando um projeto, para acessar este recurso basta seguir o exemplo da Imagem 1, logo abaixo: Imagem 1 Criando um Projeto Apósseguirospassosdaimagem01,oVisualStudioabriráumajanelapara você selecionar qual linguagem deseja utilizar, para este projeto é necessário selecionar Visual C#(C Sharp), e depois clique em Web e ele lhe dará apenas umaopção,a“ASP.NETWebApplication”(quesignificaAplicaçãoWeb,emuma Bootstrap-Pontapéinicial nautilizaçãodoFrameworkpelo Visual Studio traduçãolivre)selecioneestaopçãoecoloqueonomedeseuprojetonaparte inferior da tela, se restou alguma dúvida observe na Imagem 02: Imagem 2 Janela para criação de um projeto. Depoisdeselecionadasasinformaçõescorretasedadoodevidonomeao projeto,bastaclicarem“OK”elogoemseguidaoapareceráoutratelaparavocê selecionaro“template”(emtraduçãolivresignificamodelo),eparanesteartigo utilizaremos o modelo “MVC” (a sigla significa Model-View-Controller, que é um padrão de arquitetura de software), observe na imagem 03 logo abaixo: Imagem 3 Janela para seleção do modelo da aplicação da web. Ao clicar em “OK” o Visual Studio levará alguns segundos para gerar o projeto,assimqueeleterminar,parapodervisualizaroprojeto,compile-oem seu navegador de preferência (irei utilizar a versão para desenvolvedores do Mozilla Firefox), e terá este resultado exibido na imagem 4:
  • 6. fevereiro 2015 06 Imagem 4 Compilando o projeto! Este será o resultado que seu navegador exibirá após compilar o projeto: Imagem 5 Tema padrão gerado pelo Visual Studio. Como foi citado anteriormente, o Visual Studio gera as páginas básicas necessárias para começar a desenvolver uma aplicação para web, que são essas exibidas na imagem 5: Home(Inicio), About(Sobre), Contact(Contato), Registro(Cadastro) e Log In(Entrar). Com estas páginas prontas basta come- çar a desenvolver sua aplicação web. Caso você não tenha gostado do tema padrão, para mudá-lo é muito fácil, o primeiro passo é entrar no site http:// bootswatch.com/eescolherumtemadesuapreferência,nomeucasoescolhi o tema “Yeti”, quando encontrar um tema de seu agrado clique em download e será aberta uma aba em seu navegador com o código fonte da biblioteca de estilos do bootstrap, basta pressionar atalho CTRL + A para selecionar tudo e CTRL+Cparacopiaroconteúdo,depoisabraoVisualStudiocomoseuprojeto aberto e clique com o botão direito do mouse em cima da pasta “Content” e vá até a opção “Add” depois clique em ”New Item” (Caso o Visual Studio não esteja exibindo o “Solution Explorer“, que é o recurso que exibe as pastas do projeto, pressione a tecla CTRL + ALT + L). Imagem 6 Criando um arquivo CSS para o novo tema. Depois que clicou em “New Item”, o Visual Studio exibirá uma tela para selecionar o tipo de arquivo que deseja que ele crie, para o tema é necessário selecionar a opção “Style Sheet” (em tradução livre significa Folha de Estilos), e na parte inferior da tela é necessário nomear o arquivo como “bootstrap- -theme.css”ecliqueem“Add”parafinalizar,serestoualgumadúvidaobserve como o procedimento é feito na imagem 7 logo abaixo: Imagem 7 Adicionando o arquivo ao projeto. Apague a tag “body” que vem escrita por padrão no arquivo e cole o con- teúdo que copiou do tema escolhido no site do “Bootswatch”, depois salve o arquivo(CTRL+S),quandofeitoisso,podefecharoarquivo,emseguidadêum duplo clique na pasta “App Start” de seu projeto e faça o mesmo para abrir o arquivo “BundleConfig.cs”, o mesmo exibido na imagem 08 logo abaixo: Imagem 8 Abrindo o arquivo BundleConfig.cs QuandoeleabrirváatéalinhadecódigoqueestádeclarandooBootstrap, como é exibido na imagem 9 abaixo: Imagem 9 Alterando a declaração do CSS referente ao tema. Altereadeclaraçãode“bootstrap.css”para“bootstrap-theme.css”,queé o arquivo que criamos anteriormente com o tema escolhido do bootswatch. bundles.Add(new StyleBundle(“~/ Content/css”).Include( “~/ Content/bootstrap-theme.css”, “~/ Content/site.css”)); Dependendo do tema escolhido, que no meu caso foi o “Yeti”, ao compi- lar o projeto o resultado que obtive será exibido na imagem 10 logo abaixo:
  • 7. fevereiro 2015 07 Imagem 10 Novo tema. Aoobservaraimagem10,épossívelperceberqueapáginaficoucomum design mais leve e agradável para olhos, isso faz com que o usuário consiga acessar a página durante mais tempo e a partir daí basta alimentá-lo com conteúdo e será fiel ao seu site, seja ela uma loja virtual ou site de notícias. Como utilizar máscaras em campos Agora mudando um pouco de assunto, vamos entrar em uma parte que muitosprogramadoresparawebtêmdúvidasemuitasvezesacabamperdendo muitotempoparafazercoisassimples,comoporexemploimplementarouso de máscaras. Começando pela criação de “Inputs” (campos para inserção de dados)commáscarasdedadoscomoasdeTelefoneeRG,queémuitosimples de se fazer, pois já existe um poderoso jQuery plugin que faz toda a parte de programação por traz chamado de “Masked Input”(em uma tradução livre significa “Máscara de entrada”), a única parte que precisamos fazer é uma função em JavaScript que define a máscara que o campo terá, enfim, mãos a obra. O primeiro passo seria obter o plugin Masked input na versão 1.4, para issováatéoGoogleepesquise“Maskedinput1.4download”edepoiscliqueno segundoresultado,queodirecionaráapáginadaDigitalBush,desenvolvedora do plugin, como é exibido na imagem 11 logo abaixo: Imagem 11 Obtendo o plugin Masked Input, parte 1. Ao abrir o site da Digital Bush, poderá observar que há dois links para baixar o plugin, pode ser obtido em sua forma não comprimida que é possível compreender o código se possuir um conhecimento avançado em JavaScript, caso queira o arquivo compresso basta clicar na segunda opção que é exibida na imagem 12 logo em seguida: Imagem 12 Obtendo o plugin Masked Input, parte 2. Quando clicar em um dos links o site lhe direcionará ao código fonte do Masked Input, basta fazer o mesmo procedimento da troca do tema do Bootstrap, de um “Ctrl + A” e depois “Ctrl + C” e vá até o Visual Studio e com a seta do mouse sobre a pasta “Scripts” do projeto, clique com o botão direito, vá em Add e depois New Item, ao abrir a tela para selecionar o tipo do arquivo, selecione a segunda opçãpo “JavaScript File”, na parte de baixo coloque o nome do arquivo como “maskedinput-1.4” e clique em Add, como mostra a imagem 13: Imagem 13 Adicionando o plugin ao projeto. Quando o novo arquivo abrir, cole(Ctrl + V) o código fonte do plugin em seguida salve-o(Ctrl + S) e feche-o. Depois de adicionado o plugin ao projeto, precisamos declará-lo no arquivo “BundlesConfig.cs”, como foi feito no tema, e neste caso estaremos declarando um pacote de “Scripts” e não de estilos, digite as seguintes linhas de código logo abaixo de onde foi declarado o tema anteriormente: bundles.Add(new ScriptBundle(“~/ bundles/maskedinputjs”) .Include(“~/Scripts/maskedinput- 1.4.js”)); Quandoterminardeadicionaraslinhasdecódigo,salveefecheoarquivo “BundlesConfig.cs”,opróximopassoéatualizaraversãodojQueryqueégerada peloVisualStudio,paraaversãodisponívelnositedohttp://www.jquery.com, clique no menu “Download” quando ele abrir a página clique nos links para baixar a versão 1.11.2, nas versões comprimidas e não comprimidas, como é exibido na imagem 14 logo abaixo: Imagem 14 Obtendo a versão 1.11.2 do jQuery.
  • 8. fevereiro 2015 08 ApósbaixarosarquivosdojQuery,coloque-osnapastaScriptsdoprojeto, depois de feito isso, exclua os arquivo com os respectivos nomes: jquery- -1.10.2.intellisense; jquery-1.10.2; jquery-1.10.2.min. Depois que os arquivos antigos foram excluídos é necessário ir abrir o Visual Studio e adiciona-los ao projeto, para fazer isso é necessário estar com a seta do mouse sobre a pasta Scripts, clicar com o botão direito do mouse, ir até a opção do “Add” e depois clicar na opção “Existing Item”(em tradução livre significa Item Existente), se houver dúvida observe a imagem 15: Imagem 15 Adicionando os novos arquivos do jQuery ao projeto. Quando o Visual Studio abrir o “explorer” procure os arquivos com os respectivos nomes jquery-1.11.2 e jquery-1.11.2.min, pressione a tecla “Ctrl” e selecione os dois e clique em Add, observe como é feito na imagem 16. Imagem 16 Selecionando os arquivos do jQuery. Quandoterminardeadicioná-losaoprojeto,podemospartirparaadecla- ração dos campos que iremos definir máscaras, para isso, se estiver no Visual Studio, vá até a pasta “Models”(que em tradução livre significa Modelos) e abra o arquivo “AccountViewModel” e vá até a declaração da classe chamada “RegisterViewModel”, como mostra a imagem 17 logo abaixo: Veja a Imagem 17. Observeocódigoemnegritoabaixocomoficaráadeclaraçãodoscampos: [DataType(DataType. Password)] [Display(Name = “Confirm password”)] [Compare(“Password”, ErrorMessage = “The password and confirmation password do not match.”)] public string ConfirmPassword { get; set; } [Display(Name = “RG”)] public string RG { get; set; } [Display(Name = “Telefone”)] public string Telefone { get; set; } } Quando finalizar a declaração, salve e feche o arquivo e vá para o explo- rador de soluções e abra a pasta “Views”, depois “Account” e abra o arquivo “Register.cshtml”, como é mostrado na imagem 18: Imagem 18 Acessando o arquivo de cadastros. Imagem 17 Declarando os campos que iremos utilizar.
  • 9. fevereiro 2015 09 Comoarquivoaberto,vaiestarigualàimagem19,váparaapartedebaixo do código como está na imagem abaixo, agora iremos criar dois “inputs” para utilizarmosasmáscaras,easlinhasdecódigotemqueserescritasentreas“div” tagsquedeclaramocampodeConfirmaçãodesenhaeobotãopararegistrar, bem no lugar onde está a linha vermelha na imagem 19, caso não saiba como criar um os campos copie o código que disponibilizarei abaixo da imagem 19. Imagem 19 Localizando o lugar correto para criar os campos. O código dos respectivos campos é o seguinte, observe que as variáveis são iguais as que foram declaradas no arquivo “AccountViewModel”: <div class=”form-group”> @Html.LabelFor(m => m.RG, new { @class = “col-md-2 control-label “}) <div class=”col-md-10”> @Html.TextBoxFor(m => m.RG, “RG”, new { @class = “form-control” }) </div> </div> <div class=”form-group”> @Html.LabelFor(m => m.Telefone, new { @class = “col-md-2 control-label “}) <div class=”col- md-10”> @Html. TextBoxFor(m => m.Telefone, “Telefone”, new { @class = “form-control” }) </div> </div> Depois que criarmos os campos, temos que escrever o script com a função, mas antes é necessário declarar o plugin masked input que adicio- namos ao projeto e criamos um pacote para ele anteriormente neste artigo, para adicioná-lo é simples, observe que no fim do código tem uma seção de “Scripts”, e existe dentro dela uma declaração de um validador de jQuery, essa linha não é necessária e pode ser apagada, o código com a declaração do pacote o plugin é o seguinte: @section Scripts { @Scripts.Render(“~/bundles/ maskedinputjs”) } Assim que declarar o plugin, podemos escrever a função, para que ele funcione corretamente é necessário que o jQuery esteja declarado na página mestre, o nome desta página é “_Layout” e ela fica dentro do diretório “Sha- red” na pasta ”Views”, dê um duplo clique nela para abri-la, caso não tenha encontrado o arquivo, observe a imagem 20 logo abaixo: Imagem 20 Abrindo a página mestre. Quandoaberto,observedentrodatag<head>seopacotedojQueryestá sendo declarado, como na imagem 21: Imagem 21 Verificando o jQuery na página mestre. Agora que verificamos o jQuery podemos fechar a página mestre e voltar para a página de registros e começar a escrever a função da máscara, ela tem que ser inserida logo após os códigos do formulário de registros, observe o código abaixo: <div class=”form-group”> <div class=”col-md- offset-2 col-md-10”> <input
  • 10. fevereiro 2015 10 type=”submit” class=”btn btn- default” value=”Register” /> </div> </div> <script> $(document). ready(function () { $(“#RG”). mask(“99.999.999-9”); $(“#Telefone”). mask(“(99)99999-9999”); }); </script> @section Scripts { @Scripts.Render(“~/bundles/ maskedinputjs”) } Observe que a função JavaScript utiliza os ID’s que declaramos nos cam- pos, podendo assim compilar o projeto e teremos um resultado idêntico ao da imagem 22 Imagem 22 Resultado da máscara de RG e Telefone utilizando o plugin Masked Input. Conclusão Neste artigo é possível perceber o quanto o Bootstrap juntamente com o Visual Studio pode dar aquele pontapé inicial que estava faltando para você começarumnovoprojeto,comosrecursosoferecidospeloFrameworkédifícil negar que o nível de utilidade é realmente alto, pois ele facilita e agiliza muito o desenvolvimento. Caso já utilize o Bootstrap e desconhecia a possibilidade de obter um tema online sem ter que alterar todo o layout da sua página, a partir deste artigo procurei apresentar vários temas disponibilizados online gratuitamente para dar aquele up que faltava em seu projeto.
  • 11. fevereiro 2015 11 D epois de tanto pesquisar sobre essa questão de como abrir um arquivo grande em Delphi para seu processamento, decidi escrever sobre este tema, um tanto delicado por ser difícilchegaraumaconclusãosatisfatóriaequeagradassem a gregos e troianos – pois um arquivo grande poderia gerar facilmenteumaexceçãodotipo“OutOfMemory”quantotravaraaplicaçãode forma incessantemente demorada – alternativas como file mapping, ler cada linha em readln ou carregar tudo em loadfromfile/loadfromstream utilizando stringlist´s podem não ser uma ideia tão interessante – vão demorar para processar e algumas vezes irão ocasionar o mesmo erro de falta de memória citado acima. A ideia que venho mostrar aqui é simples mas funciona perfeitamente. Não é ideal para arquivos pequenos (onde a comparação com um dos modos tradicionais de abrir arquivos) pois o “tiro poderá sair pela culatra” – ficará maislentodoquedeveria:essainteressanteeeficientetécnicasomentedeverá ser aplicada a arquivos grandes – preferencialmente de 100 kB para cima. O que tem que ser feito basicamente é o seguinte: 1. Obter o arquivo grande; 2. Dividir este arquivo em vários mini-arquivos; 3. Cada arquivo terá uma thread para sua leitura; 4. Ler cada mini-arquivo isoladamente da VCL; 5. ArmazenaremcadainstânciadeumobjetoTMemooresultadoda leitura; 6. Acessar cada parte de um objeto TMemo através de um objeto TClientDataSet. Basicamente o processo é este. Vamos embarcar nessa aventura ? Primeiros passos com a leitura MultiThread As primeiras coisas a serem notadas na construção do nosso aplicativo é queelepróprio é“consciente”nadistribuiçãodos arquivos temporários esua manipulação – sem desperdiçar nenhum byte ou ser lento neste processo – todos os cálculos da administração dos tamanhos de cada arquivo bem como FastFile–AplicativoLeitor MultiThread para Arquivos Grandes em Lotes obufferredimensionadoemtempodeexecuçãoserãogerenciadospelonosso aplicativodeformatransparenteparaousuário–eficienteecomcomponentes progressbar para que fique ciente da execução desta tarefa. É claro que o bom-senso é fundamental para que o programa demonstre rapidez na sua resposta – um arquivo texto com mais de 500MB poderá levar até 6 minutos de espera – o que foi utilizado foi um de 500MB levando em média 3 minutos – “não podemos fazer milagre” – mas é muito útil para o gerenciamento dos lotes gerados e o retorno é bem melhor bem como a recuperação dos erros – tente a forma tradicional levar sempre uma exceção de “sem memória” e consequentemente o encerramento do aplicativo – o arquivo não será lido e o tempo que será gasto para lidar com uma solução intermediária de leitura será superior ao tempo de resposta da nossa solução aqui apresentada. Foram experimentadas alternativas inviáveis, um tanto exploradas pela Web, mas que não trouxeram uma solução que chegasse perto a de que foi implementada aqui. Utilizar recursos de Memory Mapping Files, FileStreams, etc, podem ser bons para arquivos não tão grandes – eles vão “travar” sua aplicação porque a memória do Windows vão derrubá-los – assim como um dominó.NãogastemaistempocontandocomobjetosdotipoTStringList,TList, etc; eles não vão pensar duas vezes e vão responder que não tem a memória que precisam – e vão deixar você na mão, infelizmente. O que poderá ser feito é o que foi dito inúmeras vezes acima – funciona em poucos minutos – o usuárioacompanhaoprogressodotrabalhoenquantoqueoarquivo(quenão queria ser lido de jeito nenhum pelo Windows) é finalmente carregado – em lotes – um por um – e sendo “printado” para a tela de sua aplicação – vemos agora que existe uma luz no fim do túnel. Administração do Buffer Para que o buffer seja preenchido e lido, algumas considerações são necessárias e são citadas abaixo: 1. O programa obtém o tamanho do arquivo em bytes (por exemplo, um arquivo com 2887 KB será gerado com 2955545 bytes pela nossa função de obter tamanhos de arquivos);
  • 12. fevereiro 2015 12 2. O programa gerará um “tamanho padrão” dos arquivos em lotes a serem gerados (baseando no mesmo exemplo, este arquivo terá o tama- nho padrão de divisão dos seus arquivos em lotes de 295600 bytes, ou seja, 288,671875 KB cada um); 3. Baseado neste “tamanho padrão” o programa gerará os lotes necessários neste tamanho para cada um, até preencher todos eles com os dados do arquivo original; 4. O programa criará um objeto TFileStream para cada lote gerado; 5. Assim sendo, o programa criará uma thread para cada lote a fim de ler o conteúdo deles (poderão ser geradas várias threads de uma vez – prioridade normal); 6. Oprogramacalcularáotamanhodobufferdeleituradathreadcom base no seguinte: 1024 (KB) vezes o tamanho do arquivo; 7. Cada thread criará um objeto TFileStream encarregado da leitura de cada lote corrente em que esta thread administra; 8. O buffer é preenchido com este objeto TFileStream; 9. ÉcriadoumobjetoTMemoqueiráreceberoconteúdodestebuffer; 10. EsteobjetoTMemoserágerenciadointernamentepeloprograma,a fimdeservisualizadonomomentoapropriado,vistoqueváriosdelespoderão ser gerados (um por lote) – sendo que alguns métodos não serão utilizados a fim de agilizar a execução deste processo de criação (como o método clear, por exemplo); 11. Será criado um objeto TClientDataSet para o gerenciamento de todos os lotes, a fim de mostrá-los disponíveis em uma grid e serem selecio- nados e visualizados pelo usuário. Segue abaixo o trecho do código-fonte responsável pela leitura do buffer, caracter a caracter, do tipo AnsiChar. Note que foi definido um “filtro” para esta leitura. Esta procedure se encontra na nossa thread de apoio. procedure TFileReadThread. FileRead; var Stream: TFileStream; i: integer; Buffer: array of AnsiChar; //1024 (kB) x tamanho do arquivo ... TempStr: string; const Allowed = [‘A’ .. ‘Z’, ‘a’ .. ‘z’, ‘0’ .. ‘9’, ‘_’, #13, #10, ‘-’, ‘’, ‘”’, ‘!’, ‘@’ ,’#’, ‘$’, ‘%’, ‘¨’, ‘*’, ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[‘, ‘]’, ‘<’, ‘>’, ‘.’, ‘:’, ‘;’, ‘,’ , ‘?’, ‘!’, ‘/’, ‘+’, ‘-’, ‘´’, ‘`’, ‘=’, ‘^’, ‘~’, ‘&’, ‘ ‘]; begin TempStr := ‘’; SetLength(Buffer, self. iFileSize + 1); Stream := TFileStream. Create(self.strFileName ,fmOpenRead); try Stream.Read(Buffer[0], self.iFileSize + 1); finally Stream.Free; end; for i := Low(Buffer) to High(Buffer) do if (Buffer[i] in Allowed) then TempStr := TempStr + Buffer[i]; Form1.memo3.Lines.Add(‘Arquivo ‘ + Self.strFileName + ‘ lido com sucesso.’); Form1.CriaMemos(Self.Id, TempStr); // Form1.strList. AddObject(TempStr, TObject(Self. Id)); end; Figura 01 – Resultado final do processo de leitura em lotes. Figura 02 – O processo sendo lido em lotes.
  • 13. fevereiro 2015 13 Figura 03 – Mais um exemplo de leitura em lotes – note que este arquivo proces- sado em lotes tem 562 MB, sendo lido em 3 minutos. Foram criados 1000 lotes para sua visualização. Cada lote poderá ser processado da forma mais conveniente, em um loop ou em um evento próprio – por exemplo, para a leitura dos lotes “linha por linha” como em um readln. Seguemabaixotrechosdocódigo-fonteresponsávelparaofornecimento do tamanho padrão de cada lote: function TfrmMain. NumberOfPartsToDivide(const filesize: integer): integer; var denominator: integer; { In fraction, the denominator will match the amount of exact parts where the file will be divided, while the numerator is the file size. Em fração, o denominador corresponderá a quantidade de partes exatas em que será dividido o arquivo, enquanto que o numerador será o tamanho do arquivo. } begin case length(IntToStr(Round(fil esize/1000))) of //convert to bytes 1: denominator := 4; 2: denominator := 8; else denominator := Round(Math.Power(10, length(IntToStr(filesize)) - Rou nd(length(IntToStr(filesize)) / 2))); end; Result := Round(filesize/ denominator) * 100; end; Segue abaixo a função responsável por criar os arquivos em lote e preen- chê-los com base no tamanho padrão fornecido anteriormente: procedure TfrmMain. FileSplit(const StrFilename: String); var StrmInput, StrmOutput : TFileStream; FileNumber : Integer; FileSize: integer; SequentialFile: string; begin if (StrFilename = ‘’) then Exit; if not (FileExists(StrFilename)) then Exit; tempFolder := Biblioteca. ExtractName(SysUtils. ExtractFileName(StrFilename)); if SysUtils. DirectoryExists(LocalDirectory + tempFolder) then SysUtils. RemoveDir(LocalDirectory + tempFolder); SysUtils. CreateDir(LocalDirectory + tempFolder); //FileSize := Round(Biblioteca. GetFileSize(StrFilename) / NumberOfParts); FileSize := NumberOfPa rtsToDivide(Biblioteca. GetFileSize(StrFilename)); if not clOriginalFilePath. Locate(‘FILENAME’, StrFilename, []) then begin clOriginalFilePath.Append; clOriginalFilePath. FieldByName(‘FILENAME’). AsString := StrFilename; clOriginalFilePath. FieldByName(‘SIZEFILE’). AsInteger := FileSize; clOriginalFilePath.Post; end else begin clOriginalFilePath.Edit;
  • 14. fevereiro 2015 14 //clOriginalFilePath. FieldByName(‘FILENAME’).AsString := ExtractFileName(ffile); clOriginalFilePath. FieldByName(‘SIZEFILE’). AsInteger := FileSize; clOriginalFilePath.Post; end; lblFileSize.Caption := IntToStr(FileSize) + ‘ bytes’; FileNumber := 1; iTotalFiles := 0; ProgressBar.Position := 0; memoLog.Clear; StrmInput := TFileStream. Create(StrFilename,fmOpenRead or fmShareDenyNone); try while StrmInput.Position < StrmInput.Size do begin SequentialFile := ChangeF ileExt((ExtractFilePath(StrFile name) + IncludeTrailingPathDeli miter(tempFolder) + ExtractFile Name(StrFilename)),’.’+Format(‘ %.03d’,[FileNumber])); StrmOutput := TFileStream. Create(SequentialFile ,fmCreate); try if StrmInput.Size - StrmInput.Position < FileSize then FileSize := StrmInput. Size - StrmInput.Position; StrmOutput. CopyFrom(StrmInput,FileSize); memoLog. Lines.Add(‘File: ‘ + ExtractFileName(SequentialFile) + ‘ created successfully.’); Application. ProcessMessages; finally StrmOutput.Free; end; Inc(FileNumber); Inc(iTotalFiles); ProgressBar.Position := ProgressBar.Position + 1; end; finally StrmInput.Free; end; ProgressBar.Max := iTotalFiles; end; Aplicativo para processar em lotes passo-a-passo Inicialmente, foi concebida a ideia de realizar o processamento passo-a- -passo, isto é, em vez de o nosso aplicativo anterior (FastFile) executar tudo automaticamente, o usuário poderá acompanhar mais de perto de como a coisa toda funciona. Gerando um passo por vez poderá ter a noção de como as funções de criação de lotes e a de leitura sincronizada (através de threads com a VCL) interagem entre si e resultam harmoniosamente no elo de criação e leitura das partes de um todo, “printando” o resultado em memo´s instanciados dinamicamente (sim, não há problema desde que se tenha memória – em um teste realizado aqui, foram criados 1000 memo´s sem problema algum). Seguemabaixoduasfunções: umaparaobteroconteúdodeumTMemo e a outra para checkar o seu status (ativo/destruído/não existe): function TForm1.GetMemo(const Id: integer): String; begin if MemoExists(Id) then Result := TMemo(Self. FindComponent(‘memo_’+ IntToStr(Id))).Text else Result := ‘’; end; procedure TForm1. CheckStatusMemo(const Id: integer); var garbage: TComponent; begin if MemoExists(Id) then begin garbage := Self. FindComponent(‘memo_’+ IntToStr(Id)); FreeAndNil(garbage); end; end;
  • 15. fevereiro 2015 15 Figura 04 – aplicativo passo-a-passo – função para a divisão dos arquivos Figura 05 – aplicativo passo-a-passo – função para a união dos arquivos – note que esta função não é necessária para o processo de leitura em lotes discutido no nosso tema, mas útil para ilustrar o funcionamento inverso do nosso tema também. Figura 06 – aplicativo passo-a-passo – Memo contendo o registro das operações, como um Log. Figura 07 – aplicativo passo-a-passo – Leitura do arquivo em Lotes. Figura 08 – aplicativo passo-a-passo – Leitura do arquivo em Lotes, com método de pesquisa. Essa pesquisa por lote é interessante: o usuário digita o número do lote desejado e obtém o conteúdo deste lote. Assim, pode-se pesquisar por vez, a fimdeexibir(nestecaso,apenasumporvez)osdadoscontidosnele.Todosos caracteres são os preenchidos pelo buffer das threads através de um “filtro” (já citado acima). Segue abaixo duas linhas do fonte responsáveis por esta função: Memo4.Clear; Memo4.Lines. Add(GetMemo(StrToIntDef(Edit1. Text, 1))); Por fim, segue o código-fonte da thread chamada TFileReadThread:
  • 16. fevereiro 2015 16 { TFileReadThread } constructor TFileReadThread. Create(CreateSuspended: Boolean; const myTempFileName: string; const myID, mySizeBuffer: integer); begin inherited Create(CreateSuspended); self.strFileName := myTempFileName; Self.Id := myID; Self.iFileSize := mySizeBuffer; Priority := tpNormal; end; destructor TFileReadThread. Destroy; begin inherited; end; procedure TFileReadThread. Execute; begin inherited; FreeOnTerminate := True; // if WaitForSingleObject(Mu texHandle, INFINITE) = WAIT_ OBJECT_0 then // begin Synchronize(FileRead); // end; // ReleaseMutex(MutexHandle); end; procedure TFileReadThread. FileRead; var Stream: TFileStream; i: integer; // Buffer: array[0..295600] of AnsiChar; //1024 (kB) x size of the file ... Buffer: array of AnsiChar; TempStr: string; const Allowed = [‘A’ .. ‘Z’, ‘a’ .. ‘z’, ‘0’ .. ‘9’, ‘_’, #13, #10, ‘-’, ‘’, ‘”’, ‘!’, ‘@’ ,’#’, ‘$’, ‘%’, ‘¨’, ‘*’, ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[‘, ‘]’, ‘<’, ‘>’, ‘.’, ‘:’, ‘;’, ‘,’ , ‘?’, ‘!’, ‘/’, ‘+’, ‘-’, ‘´’, ‘`’, ‘=’, ‘^’, ‘~’, ‘&’, ‘ ‘]; begin TempStr := ‘’; SetLength(Buffer, self. iFileSize + 1); Stream := TFileStream. Create(self.strFileName ,fmOpenRead); try Stream.Read(Buffer[0], self.iFileSize + 1); finally Stream.Free; end; for i := Low(Buffer) to High(Buffer) do if (Buffer[i] in Allowed) then TempStr := TempStr + Buffer[i]; frmMain.memoLog. Lines.Add(‘File ‘ + ExtractFileName(Self. strFileName) + ‘ read successfully.’); frmMain.CreateMemos(Self.Id, TempStr); frmMain.ProgressBar.Position := frmMain.ProgressBar.Position + 1; // Form1.strList. AddObject(TempStr, TObject(Self. Id)); end; Conclusão Foi explicado neste artigo o funcionamento em tempo real a leitura de um arquivo grande em lotes, para isso dividindo cada um proporcionalmente em um tamanho padrão fornecido pela própria aplicação, e assim preen- chendo dados nestes lotes e trazendo estes dados temporariamente para os componentes da VCL (através da instancialização de vários objetos TMemo´s,
  • 17. fevereiro 2015 17 TClientDataSet´sesincronizaçõescomumTMemoparalogeumTProgressBar para acompanhamento do processo). Cada lote poderá ser criado e personalizado de forma livre para o im- plementador – a nomenclatura padrão é o nome do arquivo original mais o sequencial formatado em três casas decimais – exemplo ArquivoTal.001, ArquivoTal.002, etc; Foramrealizadostestesapenascomarquivosbinários/texto,processados em todas as etapas (divisão/união/leitura temporários) sempre com fluxo de dados do tipo TStream, aproveitando assim as funcionalidades de leitura por posição através do seu método CopyFrom. Oquequistrazeraquiéumaabordagemalternativaeeficienteparaalei- turadearquivospesados,comoarquivosdecarga,porexemplo,ondesempre sobrecarregam a entrada dos aplicativos devido ao seu tamanho – por mais que existam soluções para contornar essa leitura grande, muitas oneram o processadorevivedeerrosdefaltadememóriaecomtempodemasiadoparao processamentolinhaalinha–imaginaumalinhaparacadainsertemumbanco dedadosdeumarquivodecarga–sendomuitograndeoDelphinemvaiiniciar a função – vai abortar com erros já citados de Exception “OutOfMemory”. Portanto, com este aplicativo os arquivos grandes SEMPRE serão lidos, desde que o Windows apresente memória para tal. Não é recomendável me- móriacomaté2GBdeRAM;sempremaisdoqueissoapartirde3.Foitestado com 3 GB com resultados satisfatórios. A leitura sempre funcionará e nunca deixará o usuário “na mão”. Uma última e óbvia observação é a permissão suporte@theclub.com.br Hamden Vogel Analista de Sistemas pós-graduado em Engenharia de Software pela UPIS e Programador Delphi com larga experiência desde 2000, tem de- senvolvido e vendido softwares em Delphi para a África e Estados Unidos, além do mercado nacional. Colaborou com dicas e componentes para sites especializadosemDelphi. Tambémdesenvolveemoutraslinguagenscomo C/C++, ASP, PHP e .NET. Sobre o autor de espaço em disco e capacidade do mesmo, para permitir a leitura/escrita e a capacidade de armazenamento dos lotes dos arquivos, respectivamente. Essa é uma maneira multithread para agilizar a leitura dos lotes de um arquivo em particular, dividindo uniformemente em tamanho seus respecti- vos lotes e consumindo seus dados para dentro do programa em si, trazendo uma forma eficiente de ler os dados de forma sequência, ordenada e prática. Bonsestudosebonsprojetoscomesteartigo!Seguemosfontesdosprojetos baseados neste nosso tema em anexo. Até o próximo artigo !
  • 18. fevereiro 2015 18 Listagem 01: Código para criação de Triggers. N estemêsresolviescreverumpoucosobreoassuntoAudito- riadedadosutilizandoosdenominadosGatinhos(Triggers) no Banco de Dados SQL Server. Explicarei e criarei toda a regra necessária para esta tarefa. Farei o uso da versão 2008 do SQL Server Express Edition, servindo também de base para as outras versões. Utilizaremos também como ferramenta para gerenciamento de dados o “Microsoft SQL Management Studio”, versão 10. Antesdecomeçarmosaimplementaraideiadeauditoriadedadosemtabelas do SQL Server, o tópico abaixo “Pré-requisitos” irá abranger um assunto con- siderado primordial para a leitura deste artigo, ressaltando que este tópico já se encontra publicado em um de nossos artigos. Pré-Requisitos Este tópico foi baseado no artigo “Trabalhando com o SQL Server 2008 Express Edition – Parte 2” do mês de Abril de 2011, para maiores detalhes favor realizar a leitura do artigo na íntegra. O que seria um Gatilho? O gatilho é um objeto que é automaticamente executado assim que efetuado um INSERT, DELETE ou UPDATE na tabela. Podemos criá-los de duas maneiras,BEFORE(antes)eAFTER(após).OsgatilhosBEFOREdisparamantes das modificações da instrução serem aplicadas, e antes de qualquer restrição ser aplicada. Já os AFTER disparam após todas as restrições terem sido satis- feitas, e após todas as alterações terem sido aplicadas à tabela de destino. Como implementar? Uma das formas mais rápidas e práticas seria com o Microsoft SQL Ma- nagement Studio. Com o mesmo aberto deveremos clicar na tabela desejada e escolher a Opção “Gatilho” e em seguida “Novo Gatilho”, Ver Imagem 01. SQL Server e o uso de Gatilhos para auditoria de dados Figura 01: Criar um Gatilho. Teremos abaixo o código base para criação das TRIGGERS e em seguida a explicação detalhada, Ver Listagem 01. CREATE TRIGGER <Schema_Name, sysname, Schema_Name>.<Trigger_ Name, sysname, Trigger_Name> ON <Schema_Name, sysname, Schema_Name>.<Table_Name, sysname, Table_Name> AFTER <Data_ Modification_Statements, , INSERT,DELETE,UPDATE> AS BEGIN SET NOCOUNT ON; END
  • 19. fevereiro 2015 19 Detalhando o código da Listagem 01. CREATE TRIGGER <Nome do Gatilho a ser utilizado> ON <Nome da Tabela afetada> AFTER <Momento(Antes ou Depois) / da Operação (Inserir,Deletar ou Atualizar)> AS BEGIN <Código a ser programado> END Como funcionará a auditoria dos dados em nossa tabela do SQL Server? O exemplo se baseará na auditoria em uma tabela, por exemplo a de Clientes. Teremos todos os detalhes dos comandos de Inserção, Atualização e Exclusãodosregistros.Nossosdadosserãoarmazenadosemmaisoutrasduas tabelas, sendo a de LOGS e LOGS_DATA, um clássico relacionamento Mestre- -Detalhe. Teremos os campos detalhados abaixo: Tabela LOGS ID_LOGS: campo auto-incremento (chave primária) para se relacionar com a tabela LOGS_DATA; CODIGO: código do Cliente; DATA_HORA: Data/Hora da operação; OPERACAO: tipo de operação [(I)nclusão,(A)lteração, (E)xclusão]; USUARIOID: código do usuário. Tabela LOGS_DATA ID_LOGS_DATA: campo auto-incremento (chave primária); ID_LOGS: chave estrangeira, campo resposnsável pelo relacionamento com a tabela LOGS; NOME CAMPO: Nome do Campo da tabela de Clientes; TIPO_CAMPO: Tipo de campo utilizado; VALOR: Conteúdo do campo. Para exemplificar melhor, veremos maiores detalhes do esquema na Imagem 02. Os Comandos de Insert, Update e Delete serão executados com base na tabela de CLIENTES. O resultado da execução dos Gatilhos serão armaze- nados nas tabelas LOGS e LOGS_DATA. Figura 02: Esquema de funcionamento.
  • 20. fevereiro 2015 20 Listagem 02: Script da tabela Clientes. Listagem 03: Script da tabela Logs. Listagem 04: Script da tabela Logs_Data. Definindo a Tabela Principal (CLIENTES) O script de criação da tabela de clientes poderemos conferir na Listagem 02. CREATE TABLE [dbo].[CLIENTES]( [CODIGO] [int] IDENTITY(1,1) NOT NULL, [NOME] [varchar](50) NULL, [TIPO] [int] NULL, [CIDADE] [varchar](50) NULL, [UF] [varchar](2) NULL, [DOCUMENTO] [varchar](20) NULL, [DATANASCIMENTO] [date] NULL, [SEXO] [varchar](1) NULL, [TELEFONE] [varchar](20) NULL, [OBSERVACAO] [varchar](80) NULL, [USUARIOID] [int] NULL, CONSTRAINT [PK_CLIENTES] PRIMARY KEY CLUSTERED ( [CODIGO] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ ROW_LOCKS = ON, ALLOW_PAGE_ LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] Definindo as Tabelas de Logs (LOGS e LOGS_DATA) Para a tabela de Logs teremos o script de criação conforme Listagem 03. CREATE TABLE [dbo].[LOGS]( [ID_LOGS] [int] IDENTITY(1,1) NOT NULL, [CODIGO] [int] NULL, [DATA_HORA] [datetime] NULL, [OPERACAO] [varchar](1) NULL, [USUARIOID] [int] NULL, CONSTRAINT [PK_CADASTRO_UNICO_ LOGS] PRIMARY KEY CLUSTERED ( [ID_LOGS] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ ROW_LOCKS = ON, ALLOW_PAGE_ LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] Já para a tabela Detalhe Logs_Data, o script responsável pela criação da tabela poderá ser consultado na Listagem 04. CREATE TABLE [dbo].[LOGS_DATA]( [ID_LOGS_DATA] [int] IDENTITY(1,1) NOT NULL, [ID_LOGS] [int] NULL, [NOME_CAMPO] [varchar](50) NULL, [TIPO_CAMPO] [varchar](50) NULL, [VALOR] [varchar](80) NULL, CONSTRAINT [PK_LOGS_DATA] PRIMARY KEY CLUSTERED ( [ID_LOGS_DATA] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ ROW_LOCKS = ON, ALLOW_PAGE_ LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] Criando o Gatilho de “Insert” Ogatilho“INSERT_LOGS”farásempreotrabalhoapósainserçãodealgum registro, Ver código comentado na listagem 05. CREATE TRIGGER [dbo].[INSERT_ LOGS] ON [dbo].[CLIENTES]
  • 21. fevereiro 2015 21 Listagem 05: Gatilho Insert_Logs. AFTER INSERT AS BEGIN DECLARE @CODIGO INT DECLARE @NOME VARCHAR(50) DECLARE @TIPO INT DECLARE @CIDADE VARCHAR(50) DECLARE @UF VARCHAR(2) DECLARE @DOCUMENTO VARCHAR(20) DECLARE @DATANASCIMENTO DATE DECLARE @SEXO VARCHAR(1) DECLARE @TELEFONE varchar(20) DECLARE @OBSERVACAO varchar(80) DECLARE @USUARIOID int DECLARE @ID_LOGS int Para cada campo a ser inserido foi necessário criar uma variável. SELECT @CODIGO = CODIGO, @NOME = NOME, @TIPO = TIPO, @CIDADE = CIDADE, @UF = UF, @DOCUMENTO = DOCUMENTO, @DATANASCIMENTO = DATANASCIMENTO, @SEXO = SEXO, @TELEFONE = TELEFONE, @OBSERVACAO = OBSERVACAO, @USUARIOID = USUARIOID FROM Inserted O próximo passo será de alimentar todas as variáveis com os registros inseridos na tabela de Clientes. Podemos realizar esta tarefa com o comando “Inserted”, o mesmo responsável pelo estado de inserção dos dados. INSERT INTO LOGS (CODIGO, DATA_HORA, OPERACAO, USUARIOID) VALUES (@CODIGO,GETDATE(),’I’, @ USUARIOID) Para inserir na tabela “Mestre” denominada Logs faremos um simples INSERT,passandoporparâmetroocódigooriundodavariáveldeclaradaacima, a data atual utilizando o comando “GetDate()”, a operação “I” que significa modo de inserção e o usuário ativo no momento. SELECT @ID_LOGS = ID_LOGS FROM LOGS INSERTED Já para inserirmos os dados na tabela “Detalhe” Logs_data o primeiro passo deveremos realizar um select usando o ID_LOGS para localizarmos o registro corrente, com o auxílio do comando “Inserted”. IF (@CODIGO IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’CODIGO’,’INT’,@ CODIGO) END IF (@NOME IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’NOME’,’VARCHAR’,@ NOME) END IF (@TIPO IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’TIPO’,’INT’,@TIPO) END IF (@CIDADE IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’CIDADE’,’VARCHAR’,@ CIDADE) END
  • 22. fevereiro 2015 22 IF (@UF IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’UF’,’VARCHAR’,@UF) END IF (@DOCUMENTO IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’DOCUMENTO’,’VARCHAR’,@ DOCUMENTO) END IF (@DATANASCIMENTO IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’DATANASCIMENTO’,’DATE’,@ DATANASCIMENTO) END IF (@SEXO IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’SEXO’,’VARCHAR’,@ SEXO) END IF (@TELEFONE IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’TELEFONE’,’VARCHAR’,@ TELEFONE) END IF (@OBSERVACAO IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’OBSERVACAO’,’VARCHAR’,@ OBSERVACAO) END IF (@USUARIOID IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’USUARIOID’,’INT’,@ USUARIOID) END END Nos próximos passos faremos uma verificação campo a campo se os mesmos não se encontram nulos e logo em seguida executaremos um Insert na tabela Logs_data, respeitando a ordem dos campos Id_Logs, Nome, Tipo e o Valor do campo. Criando o Gatilho de “Update” O gatilho “UPDATE_LOGS” fará sempre o trabalho após a atualização de algum registro, Ver código comentado na listagem 06. CREATE TRIGGER [dbo].[UPDATE_ LOGS] ON [dbo].[CLIENTES] AFTER UPDATE AS BEGIN DECLARE @CODIGO INT DECLARE @NOME VARCHAR(50) DECLARE @TIPO INT DECLARE @CIDADE VARCHAR(50) DECLARE @UF VARCHAR(2) DECLARE @DOCUMENTO VARCHAR(20) DECLARE @DATANASCIMENTO DATE DECLARE @SEXO VARCHAR(1) DECLARE @TELEFONE varchar(20) DECLARE @OBSERVACAO varchar(80) DECLARE @USUARIOID int DECLARE @ID_LOGS int Para cada campo a ser atualizado foi necessário criar uma variável.
  • 23. fevereiro 2015 23 SELECT @CODIGO = ISNULL(CODIGO,0), @NOME = ISNULL(NOME,’ ‘), @TIPO = ISNULL(TIPO,’ ‘), @CIDADE = ISNULL(CIDADE,’ ‘), @UF = ISNULL(UF,’ ‘), @DOCUMENTO = ISNULL(DOCUMENTO,’ ‘), @DATANASCIMENTO = ISNULL(DATANASCIMENTO,’ ‘), @SEXO = ISNULL(SEXO,’ ‘), @TELEFONE = ISNULL(TELEFONE, ‘ ‘), @OBSERVACAO = ISNULL(OBSERVACAO,’ ‘), @USUARIOID = ISNULL(USUARIOID,’ ‘) FROM INSERTED O próximo passo será de alimentar todas as variáveis com os registros atualizadosnatabeladeClientes.Podemosrealizarestatarefacomocomando “Inserted” e analisando se o campo está nulo com a função “ISNULL”. DECLARE @NOME_OLD VARCHAR(40) DECLARE @TIPO_OLD INT DECLARE @CIDADE_OLD VARCHAR(50) DECLARE @UF_OLD VARCHAR(2) DECLARE @DOCUMENTO_OLD VARCHAR(20) DECLARE @DATANASCIMENTO_OLD DATE DECLARE @SEXO_OLD VARCHAR(1) DECLARE @TELEFONE_OLD VARCHAR(20) DECLARE @OBSERVACAO_OLD VARCHAR(80) DECLARE @USUARIOID_OLD int Nas variáveis acima deveremos armazenar todos os valores antigos dos campos que foram atualizados. SELECT @NOME_OLD = ISNULL(NOME,’ ‘), @TIPO_OLD = ISNULL(TIPO,’ ‘), @CIDADE_OLD = ISNULL(CIDADE,’ ‘), @UF_OLD = ISNULL(UF,’ ‘), @DOCUMENTO_OLD = ISNULL(DOCUMENTO,’ ‘), @DATANASCIMENTO_OLD = ISNULL(DATANASCIMENTO,’ ‘), @SEXO_OLD = ISNULL(SEXO,’ ‘), @TELEFONE_OLD = ISNULL(TELEFONE, ‘ ‘), @OBSERVACAO_OLD = ISNULL(OBSERVACAO,’ ‘), @USUARIOID_OLD = ISNULL(USUARIOID,’ ‘) FROM DELETED Poderemos recuperar estes campos utilizando o comando “Deleted”. INSERT INTO LOGS (CODIGO, DATA_ HORA, OPERACAO, USUARIOID) VALUES (@CODIGO,GETDATE(),’A’, @ USUARIOID) Seguindo a mesma lógica descrita anteriormente, a inserção na tabela “Mestre” denominada Logs faremos um simples INSERT, passando por parâ- metro o código oriundo da variável declarada acima, a data atual utilizando o comando “GetDate()”, a operação “A” que significa modo de atualização e o usuário ativo no momento. SELECT @ID_LOGS = ID_LOGS FROM LOGS INSERTED Parainserirmososdadosnatabela“Detalhe”Logs_dataoprimeiropasso deveremos realizar um select usando o ID_LOGS para localizarmos o registro corrente, com o auxílio do comando “Inserted”.
  • 24. fevereiro 2015 24 Listagem 06: Gatilho Update_Logs. IF (UPDATE(NOME) AND (@NOME <> @NOME_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’NOME’,’VARCHAR’,@ NOME) END IF (UPDATE(TIPO) AND (@TIPO <> @TIPO_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’TIPO’,’INT’,@TIPO) END IF (UPDATE(CIDADE) AND (@ CIDADE <> @CIDADE_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’CIDADE’,’VARCHAR’,@ CIDADE) END IF (UPDATE(UF) AND (@UF <> @ UF_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’UF’,’VARCHAR’,@UF) END IF (UPDATE(DOCUMENTO) AND (@ DOCUMENTO <> @DOCUMENTO_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’DOCUMENTO’,’VARCHAR’,@ DOCUMENTO) END IF (UPDATE(DATANASCIMENTO) AND (@DATANASCIMENTO <> @ DATANASCIMENTO_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’DATANASCIMENTO’,’DATE’,@ DATANASCIMENTO) END IF (UPDATE(SEXO) AND (@SEXO <> @SEXO_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’SEXO’,’VARCHAR’,@ SEXO) END IF (UPDATE(TELEFONE) AND (@ TELEFONE <> @TELEFONE_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’TELEFONE’,’VARCHAR’,@ TELEFONE) END IF (UPDATE(OBSERVACAO) AND (@ OBSERVACAO <> @OBSERVACAO_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’OBSERVACAO’,’VARCHAR’,@ OBSERVACAO) END IF (UPDATE(USUARIOID) AND (@ USUARIOID <> @USUARIOID_OLD)) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’USUARIOID’,’INT’,@ USUARIOID) END END
  • 25. fevereiro 2015 25 Listagem 07: Gatilho Delete_Logs. Por final faremos uma verificação campo a campo se os mesmos estão em modo de Update e comparamos o valor antigo com o valor novo, caso encontre alguma divergência executaremos um Insert na tabela Logs_data, respeitando a ordem dos campos ID_LOGS, Nome, Tipo e o Valor do campo. Criando o Gatilho de “Delete” Ogatilho“DELETE_LOGS”farásempreotrabalhoapósaExclusãodealgum registro, Ver código comentado na listagem 07. CREATE TRIGGER [dbo].[DELETE_ LOGS] ON [dbo].[CLIENTES] AFTER DELETE AS BEGIN DECLARE @ID_LOGS INT DECLARE @CODIGO_OLD INT DECLARE @NOME_OLD VARCHAR(40) DECLARE @TIPO_OLD INT DECLARE @CIDADE_OLD VARCHAR(50) DECLARE @UF_OLD VARCHAR(2) DECLARE @DOCUMENTO_OLD VARCHAR(20) DECLARE @DATANASCIMENTO_OLD DATE DECLARE @SEXO_OLD VARCHAR(1) DECLARE @TELEFONE_OLD VARCHAR(20) DECLARE @OBSERVACAO_OLD VARCHAR(80) DECLARE @USUARIOID_OLD int Para cada campo a ser atualizado foi necessário criar uma variável. SELECT @CODIGO_OLD = CODIGO, @NOME_OLD = NOME, @TIPO_OLD = TIPO, @CIDADE_OLD = CIDADE, @UF_OLD = UF, @DOCUMENTO_OLD = DOCUMENTO, @DATANASCIMENTO_OLD = DATANASCIMENTO, @SEXO_OLD = SEXO, @TELEFONE_OLD = TELEFONE, @OBSERVACAO_OLD = OBSERVACAO, @USUARIOID_OLD = USUARIOID FROM DELETED O próximo passo será de alimentar todas as variáveis com os registros excluídos na tabela de Clientes. Podemos realizar esta tarefa com o comando “Deleted”. INSERT INTO LOGS (CODIGO, DATA_HORA, OPERACAO, USUARIOID) VALUES (@CODIGO_OLD,GETDATE(),’E’, @ USUARIOID_OLD) Novamentefaremosainserçãonatabela“Mestre”comocomandoINSERT, passandoporparâmetroocódigooriundodavariáveldeclaradaacima,adata atualutilizandoocomando“GetDate()”,aoperação“E”quesignificamodode exclusão e o usuário ativo no momento. SELECT @ID_LOGS = ID_LOGS FROM LOGS INSERTED Para a tabela “Detalhe” Logs_data deveremos realizar um select usando o ID_LOGS para localizarmos o registro corrente, com o auxílio do comando “Inserted”. IF (@CODIGO_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’CODIGO’,’INT’,@ CODIGO_OLD) END IF (@NOME_OLD IS NOT NULL)
  • 26. fevereiro 2015 26 BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’NOME’,’VARCHAR’,@ NOME_OLD) END IF (@TIPO_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’TIPO’,’INT’,@TIPO_ OLD) END IF (@CIDADE_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’CIDADE’,’VARCHAR’,@ CIDADE_OLD) END IF (@UF_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’UF’,’VARCHAR’,@ UF_OLD) END IF (@DOCUMENTO_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’DOCUMENTO’,’VARCHAR’,@ DOCUMENTO_OLD) END IF (@DATANASCIMENTO_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’DATANASCIMENTO’,’DATE’,@ DATANASCIMENTO_OLD) END IF (@SEXO_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’SEXO’,’VARCHAR’,@ SEXO_OLD) END IF (@TELEFONE_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’TELEFONE’,’VARCHAR’,@ TELEFONE_OLD) END IF (@OBSERVACAO_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_ LOGS,’OBSERVACAO’,’VARCHAR’,@ OBSERVACAO_OLD) END IF (@USUARIOID_OLD IS NOT NULL) BEGIN INSERT INTO LOGS_DATA (ID_ LOGS, NOME_CAMPO, TIPO_CAMPO, VALOR) VALUES (@ID_LOGS,’USUARIOID’,’INT’,@ USUARIOID_OLD) END END Esta etapa será idêntica aos gatilhos criados anteriormente, pois realiza- mos a verificação se o campo não encontra-se nulo realizando a inserção dos
  • 27. fevereiro 2015 27 dados corretamente. Poderemos conferir no Banco os objetos criados, Ver Imagem 03. Figura 03: Objetos criados com sucesso. Testando os gatilhos Os gatilhos são objetos executados diretamente no Banco de Dados, de acordocomaoperaçãodesejada.Paradispará-lospoderíamosutilizarqualquer interface mas para nosso teste a forma mais rápida para realizar esta tarefa seria diretamente pelo SSMSE (SQL Server Management Studio Express). Abrindo a tabela de Clientes poderemos inserir, alterar ou excluir alguns dadoseapósumaconsultanastabelasLogseLogs_Datateremosumresultado idêntico ao da Figura 04. Conclusões O uso de Triggers(gatilhos) em auditoria de sistemas é uma prática muito usual,poistodootrabalhoficaacargodoservidor,livrandoainterfacedolado cliente para esta tarefa. Procurei neste artigo explorar um exemplo de como deveremosimplementarestatécnicautilizandooSQLServer.Fiquemavontade para modificar e adaptar estas rotinas conforme a necessidade do momento. Um abraço a todos e até o mês que vem! Figura 04: Resultado final. thiago@theclub.com.br Thiago Cavalheiro Montebugnoli adora aprender novas tecnologias. Formado pela Faculdade de Tecnologia de Botucatu – SP (FATEC), já desenvolveu softwares utilizando a plataforma .NET, Delphi junto com Banco de Dados SQL Server e Firebird. Atualmente trabalha no Centro de Processamento de Dados da Prefeitura Municipal de Itaí-SP é colunista mensal da Revista The Club Megazine e é consultor Técnico do The Club. Possui as seguintes certificações: MCP - Microsoft Certified Professional, MCTS-MicrosoftCertifiedTechnologySpecialist,MCAD-MicrosoftCertifiedApplicationDeveloper e MCSD - Microsoft Certified Solution Developer. Sobre o autor
  • 28. fevereiro 2015 28 Listagem 1 – Uses. Listagem 2 – Atalho. Nesteartigovamosdemonstraralgumasfunçõesbásicas,masimportantes para o bom uso de seu aplicativo. Codificação Primeiramente vamos criar o recurso de ‘Atalho’ do seu App, ou seja, ao instalarmos o interessante é que o mesmo já seja exibido na tela inicial, portanto, vamos iniciar a codificação. AdicioneasUsesqueutilizaremosnos2exemplos,‘Atalho’e‘Notificações’. FMX.Platform.Android, FMX.MobilePreview, FMX.Helpers.Android, Androidapi.JNI. GraphicsContentViewText, AndroidAPI.jni.OS, Androidapi.JNI.JavaTypes, AndroidApi.Jni.App, AndroidApi.JniBridge, FMX.Notification; Apósissovamoscriaroprocedimentoresponsávelpelacriaçãodoatalho. Em um de nossos artigos: Delphi XE5 - Trabalhando com Intents: http://theclub.com.br/Restrito/ Revistas/201407/dxe51407.aspx Vimos como trabalhar com Intents no Android, portanto, vamos repetir o mesmo processo aqui. DelphiXE5–Adicionando Recursos procedure Atalho; var Intent: JIntent; addIntent: JIntent; I : Integer; wIcone : JIntent_ ShortcutIconResource; begin Intent := TJIntent.JavaClass. init(SharedActivityContext, SharedActivityContext. getClass); Intent.setAction(TJIntent. JavaClass.ACTION_MAIN); addIntent := TJIntent.Create; addIntent.putExtra(TJIntent. JavaClass.EXTRA_SHORTCUT_INTENT, TJParcelable.Wrap((Intent as ILocalObject).GetObjectID)); addIntent.putExtra(TJIntent. JavaClass.EXTRA_SHORTCUT_NAME, StringToJString(Application. Title)); addIntent. setAction(StringToJString(‘com. android.launcher.action. INSTALL_SHORTCUT’)); I := SharedActivity. getResources.getIdentifier(Str ingToJString(‘ic_launcher’), StringToJString(‘drawable’), StringToJString(‘com. embarcadero.Project1’)); wIcone := TJIntent_
  • 29. fevereiro 2015 29 Listagem 3 – Informações do Atalho. Listagem 4 – Permissão. Listagem 5 - AndroidManifest.template.xml. ShortcutIconResource.JavaClass. fromContext(SharedActivityConte xt, I); addIntent.putExtra(TJIntent. JavaClass.EXTRA_SHORTCUT_ ICON_RESOURCE, TJParcelable. Wrap((wIcone as ILocalObject). GetObjectID)); SharedActivityContext. sendBroadcast(addIntent); end; Neste procedimento é necessário indicar o nome da imagem e de seu projeto para a criação do atalho, conforme trecho abaixo: I := SharedActivity. getResources.getIdentifier(Str ingToJString(‘ic_launcher’), StringToJString(‘drawable’), StringToJString(‘com. embarcadero.Project1’)); Com toda a codificação pronta resta apenas ‘liberar’ a permissão de uso em seu ‘AndroidManifest.template.xml’, portanto, adicione a seguinte linha em seu arquivo: <uses-permission android:name=”com.android. launcher.permission.INSTALL_ SHORTCUT”/> <?xml version=”1.0” encoding=”utf-8”?> <!-- BEGIN_INCLUDE(manifest) --> <manifest xmlns:android=”http:// schemas.android.com/apk/res/ android” package=”%package%” android:versionCode=”%v ersionCode%” android:versionName=”%v ersionName%”> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVer sion=”%minSdkVersion%” /> <uses-permission android:name=”com.android. launcher.permission.INSTALL_ SHORTCUT”/> <%uses-permission%> <application android:persis tent=”%persistent%” android:restoreAnyVersi on=”%restoreAnyVersion%” android:label=”%label%” android:installLocation =”%installLocation%” android:debuggable=”%de buggable%” android:largeHeap=”%largeHeap%” android:icon=”%icon%” android:theme=”%theme%”> <!-- Our activity is a subclass of the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name=”com.embarcadero. firemonkey.FMXNativeActivity” android:label=”%activityLabel%” android:configCh anges=”orientation|keyboardHidd en”> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name=”android.app.lib_ name” android:value=”%libNameValue%” /> <intent-filter> <action android:name=”android.intent. action.MAIN” /> <category android:name=”android.intent. category.LAUNCHER” /> </intent-filter> </activity> <receiver android:name=”com.embarcadero. firemonkey.notifications. FMXNotificationAlarm” />
  • 30. fevereiro 2015 30 Listagem 4 – Notificação. </application> </manifest> <!-- END_INCLUDE(manifest) --> Apósfinalizarmosessasconfiguraçõesbastaadicionaraprocedure ‘Atalho’ em seu projeto e se tudo correu bem seu ícone será criado na tela inicial conforme imagem abaixo: Imagem 1 – Atalho. Agora vamos adicionar o segundo recurso que é utilizado por todos os aplicativos que conhecemos que são as ‘Notificações’, aquelas mensagens que são exibidas avisando se alguma mensagem chegou ou se há alguma atualização disponível. O Delphi já possui um componente próprio para trabalharmos com este recurso,portanto,adicioneemseuprojetoocomponente‘TNotificationCenter’ em nosso exemplo o nomeamos de ‘Mensagem’. Para a criação e exibição da mensagem em seu aparelho podemos utilizar a procedure ‘Notificacao’, veja que indicamos 2 informações básicas. ‘AlertBody’ = É a mensagem que deseja exibir; ‘FireDate’ = O momento em que deseja exibir a mensagem. procedure Notificacao; Var Notification: TNotification; begin If Notificacao.Supported Then begin Notification := Notificacao. CreateNotification; Try Notification.AlertBody := ‘The-Club’; Notification.FireDate := Now; Notificacao.ScheduleNotificat ion(Notification); Finally Notification.DisposeOf; end; end; Após isso basta chamar este procedimento, veja imagem abaixo: Imagem 2 – Notificação. Conclusão Neste artigo vimos como adicionar dois recursos muito úteis ao dia-a-dia de um aplicativo móvel, os recursos apresentados podem ser melhorados e utilizados para diversas funções. Espero que tenham gostado desta dica e até a próxima! suporte@theclub.com.br Jeferson Silva de Lima Consultor The Club. Sobre o autor