25 de fevereiro de 2010

Design Patterns com Delphi : Chain Of Responsability - parte II

Do ponto de vista da complexidade da hierarquia de classes, a implementação de uma solução para a Cadeia de Responsabilidade não apresenta grandes desafios. As classes do tipo Handler e Concrete Handler mantêm entre si uma relação simples de herança - incluí no post anterior descrições para os tipos de classe participantes; lá também citei um exemplo de uso para o Pattern. Reproduzo abaixo o mesmo diagrama daquele post para mostrar as relações entre as classes do exemplo proposto:
Diagrama UML para Chain Of Responsability
Então, para implementar a solução, devemos primeiro criar a classe Handler como sendo abstrata para introduzir as funcionalidades que estarão disponíveis na Cadeia de Responsabilidade. Depois, criamos as heranças dessa classe (os Concrete Handlers) para implementar as regras específicas para cada nível de responsabilidade, conforme ditado pelas regras de negócio.
No exemplo, as classes concretas dizem respeito à necessidade de aprovação de documentos e cada uma delas aplica suas próprias regras de acordo com o poder de decisão necessário para efetuar uma aprovação.
type
TWAprovacao = class
{ ... }
protected
_Sucessor: TWAprovacao;
{ ... }

procedure SetSucessor (aSucessor: TWAprovacao);
function PrecisaAprovacao (aDoc: TWDocumento) : boolean;virtual;abstract;
procedure RequisitaAprovacao (aDoc: TWDocumento);virtual;abstract;
end;

TWAprovacaoBasica = class(TWAprovacao)
{ ... }
function PrecisaAprovacao (aDoc: TWDocumento) : boolean;override;
procedure RequisitaAprovacao (aDoc: TWDocumento);override;
end;

TWAprovacaoGerente = class(TWAprovacao)
{ ... }
function PrecisaAprovacao (aDoc: TWDocumento) : boolean;override;
procedure RequisitaAprovacao (aDoc: TWDocumento);override;
end;
Veja que na classe base TWAprovacao há um membro (variável) chamado _Sucessor. Esse membro representa o próximo nível a ser consultado na hierarquia de responsabilidades. Assim, quando as regras de uma classe não permitem que ela tome uma decisão, ela repassa para seu "sucessor" a responsabilidade de decidir - daí o nome de Cadeia de Responsabilidade para o Pattern. As funções que verificam a aprovação em cada classe, então, devem levar o sucessor em conta:
function TWAprovacaoBasica.PrecisaAprovacao (aDoc: TWDocumento) : boolean;
begin;
Result := true;
{ Regra básica para determinar se o documento precisará de uma aprovação }
if (aDoc.Valor > 1500) then begin
if (_Sucessor <> Nil) then
Result := _Sucessor.PrecisaAprovacao (aDoc);
end
else
Result := false;
end;

procedure TWAprovacaoBasica.RequisitaAprovacao (aDoc: TWDocumento);
begin;
if PrecisaAutorizacao then begin
{ Executa ações necessárias para obter aprovação básica. Exemplos: envio de email, gravação no BD, etc}
end;
end;

function TWAprovacaoGerente.PrecisaAprovacao (aDoc: TWDocumento) : boolean;
begin;
Result := true;
{ Regra aplicada a gerentes para determinar se o documento precisará de uma aprovação }
if (aDoc.Valor > 8000) then begin
if (_Sucessor <> Nil) then
Result := _Sucessor.PrecisaAprovacao (aDoc);
end
else
Result := false;
end;

procedure TWAprovacaoGerente.RequisitaAprovacao (aDoc: TWDocumento);
begin;
if PrecisaAutorizacao then begin
{ Executa ações necessárias para obter aprovação para Gerentes. Exemplos: envio de email, gravação no BD ou outra específica. }
end;
end;
A chave para o funcionamento da solução está na forma como o encadeamento é montado. Neste ponto, invariavelmente teremos que utilizar algum dos padrões dedicados à flexibilizar a criação de instâncias de classes, tal como o Factory Method:
function GetAprovacaoFromFactory (AUsuario: TWUsuario): TWAprovacao;
var lUltSucessor, lAux : TWAprovacao;
begin
{ A aprovação básica sempre existe e é o ponto de entrada das aprovações }
Result := TWAprovacaoBasica.Create;
lUltSucessor := Result;

if AUsuario.TipoCargo >= tcGerente then begin
lAux := TWAprovacaoGerente.Create;
lUltSucessor.SetSucessor (lAux);
lUltSucessor := lAux;
end;

{ Cria sucessivamente a hierarquia de aprovações ... }
if AUsuario.TipoCargo >= tcDiretor then begin
lAux := TWAprovacaoDiretor.Create;
lUltSucessor.SetSucessor (lAux);
lUltSucessor := lAux;
end;
{ ... }
end;
Observe que a montagem da cadeia de responsabilidades começa pelo nível hierárquico mais baixo - que está sempre presente - e vai crescendo conforme aumenta o cargo do Usuário que está solicitando a aprovação.

A função que obtem a instância da hierarquia de aprovações pode, então, ser utilizada dentro da classe do tipo Client para realizar as operações necessárias. No diagrama do início do post, o tipo Client é encarnado pela classe TWBusinessObj. Se for preciso modificar as regras de negócio ou os relacionamentos existentes na cadeia, a classe Client fará uso das novas regras sem que seja necessário modificá-la - graças ao grau baixo de acoplamento existente.

As regras apresentadas neste exemplo são deliberadamente simples para reforçar o conceito por trás do Design Pattern. Obviamente, as regras de aprovação podem ser muita mais complexas, envolvendo outros elementos e cenários existentes numa aplicação real, assim como a montagem da hierarquia de aprovação pode gerar uma cadeia muito mais extensa.

19 de fevereiro de 2010

Design Patterns com Delphi : Chain Of Responsability

Já comentei aqui no blog a respeito dos Design Patterns Criacionais (relativos à forma como os objetos são criados num programa) e dos Estruturais (que abordam as associações estruturais entre classes e objetos, isto é, os relacionamentos entre as classes para garantir a coesão de um projeto). Ficaram faltando ainda os Design Patterns Comportamentais, que são aqueles que se preocupam com as interações existentes entre objetos. Essas interações devem ser concebidas de maneira que os objetos possam trocar mensagens entre si (se comunicar) e ainda assim permanecerem fracamente acoplados, isto é, sem que uns tenham dependências rígidas com os outros ou que tenham que conhecer detalhes profundos da implementação uns dos outros.

O primeiro desses padrões que tratarei aqui é Cadeia de Responsabilidade. Nele, a requisição feita por um objeto Cliente é transmitida ao longo de uma sequência de Classes até que a requisição seja suprida por uma delas. Como a ideia é manter as classes o mais desacopladas possível, esse tipo de transmissão da mensagem (requisição) é baseado no conceito de "orientação a dados", ou seja, a estruturação dos dados trafegados na mensagem é o único ponto de contato entre as classes. Aquela Classe que recebe a mensagem deve analisar os dados recebidos e, se não for capaz de atender à requisição, ela deverá retransmitir a mensagem para o objeto seguinte na hierarquia.

Exemplos típicos da aplicação do padrão Cadeia de Responsabilidade envolvem alçadas de aprovação. Imagine, por exemplo, o sistema de compras de uma companhia, onde os Compradores podem submeter os pedidos de compra sem necessitar uma aprovação se o pedido tiver certas características (digamos, um limite máximo de valor) mas que, conforme estas características vão variando, o pedido deve ser submetido a aprovação por níveis hierárquicos cada vez mais altos (supervisor, gerente, diretor, presidente).

O diagrama abaixo mostra as classes participantes da solução para esse cenário utlizando a Cadeia de Responsabilidade.
Diagrama UML para Chain Of Responsability
A nomenclatura das classes participantes da solução é descrita no quadro abaixo:
Handler é a classe que descreve a interface que é disponibilizada para aqueles que pretendem submeter uma requisição. Ou seja, é uma classe abstrata que apenas oferece os pontos de entrada para as requisições que poderão ser feitas; as transações que forem utilizá-la não saberão de antemão quais nem quantos são os níveis hierárquicos existentes, tampouco as regras aplicadas. No diagrama acima, este é o papel da classe TWAprovacao.
É preciso que outras classes sejam criadas para implementar a interface publicada pelo Handler. Essas classes são chamadas de Concrete Handlers, sendo exemplos as classes TWAprovacaoBasica e TWAprovacaoGerente no diagrama. Repare que apenas observando o diagrama não é possível determinar a hierarquia da cadeia de responsabilidades. Isso terá que ser montado através de algum dos padrões criacionais, num ponto em que inevitavelmente teremos que introduzir regras de negócio.
Por fim, a classe que submete uma requisição à Cadeia de Responsabilidades é chamada de Client. A classe TWbusinessObj tem essa função no nosso exemplo.
A classe TWDocumento representa um documento genérico do meu sistema, podendo ser um Pedido de Compra ou de Venda, uma Nota Fiscal, etc.. Ela é o ponto de contato entre a transação representada pelo objeto de negócio TWbusinessObj e a hierarquia de classes da Cadeia de Responsabilidade. Portanto, neste exemplo o TWDocumento é o dado que trafega pelos elos da cadeia permitindo que cada classe tenha subsídios para determinar se a mensagem é de sua responsabilidade ou se ela deve ser retransmitida.

No próximo post eu apresento uma forma de codificar essa solução usando Delphi.

Mais Informações
Posts sobre Design Patterns

18 de fevereiro de 2010

Novo SDK para o IPhone também contempla o IPad

Em janeiro eu postei aqui uma notícia a respeito do lançamento do SDK para o Kindle realizado pela Amazon na mesma semana do lançamento do novo gadget da Apple - o IPad. Agora é a vez da Apple liberar uma versão do SDK do IPhone que também permite a construção de aplicações para o IPad. Conforme a própria Apple anunciou, os programas feitos para o IPhone e IPod Touch deverão rodar sem problemas no novo aparelho.

No entanto, há recursos e características inerentes a cada um dos aparelhos que deverão ser levados em conta na hora do desenvolvimento pois, apesar do código binário (o programa em si) poder ser o mesmo, os recursos que não existem num dispositvo obviamente não operarão. Dependendo de como a interação da aplicação com o usuário foi pensada, ela pode se tornar inútil. O site Gizmodo compilou as principais novidades da interface do IPad e a matéria resultante foi publicada no endereço http://gizmodo.com/5459873/the-ipads-interface-and-gestures-whats-actually-new-video.

A Infoworld.com publicou uma matéria a respeito da diferença de programação entre o IPad e o IPhone, a qual reproduzo abaixo:
Desenvolvedores de aplicações para o iPhone deram uma olhada nas nuances de se construir programas para o tablet IPad e descobriram que é muito similar à construção de programas para o iPhone, embora com a adição do benefício da tela ser maior.

"Acho que estou surpreso por não haver surpresas," diz o programador Christopher Allen, autor de "iPhone in Action" e fundador de uma comunidade de desenvolvedores para o IPhone - o iPhoneWebDev. "Se você sabe programar para o iPhone, então o desenvolvimento para iPad é muito fácil," diz Allen. "Eles são muito similares."

A Apple lançou o SDK iPhone 3.2, que inclui um emulador do iPad e extensões específicas desse aparelho -- cobrindo, por exemplo, reconhecimento de novos gestos aceitos pela tela touch, tal como "girar" -- e o liberou em 28 de Janeiro (dia seguinte ao anúncio do iPad) para desenvolvedores registrados do iPhone.

"É muito similar ao SDK do iPhone exceto que ele permite aos programadores criarem aplicações para o iPad utilizando seus novos recursos -- a tela com tamanho maior, por exemplo. Além disso, não há nada mirabolante nem novas funcionalidades no SDK," diz Brandan Greenwood, diretor de soluções móveis da consultoria Amadeus Consulting que desenvolve para iPhone.

O desenvolvedor da aplicação de gerenciamento na nuvem Rackspace, Michael Mayo, descreve o SDK como "somente uma atualização do SDK para o iPhone."

Os programadores consultados pela InfoWorld estão todos focados no uso da tela mais larga em suas aplicações iPad. "Há muitas coisas que o espaço extra da tela te dá," diz Allen.

"A Rackspace espera reescrever suas aplicações para iPad mesmo isso não sendo necessário", diz Mayo. "Estou reescrevendo porque quero acrescentar muitas novas funcionalidades, tais como mostrar o status do sistema como um todo na nuvem", afirma. "O iPhone não deixa muito espaço para isto mas o iPad sim", explica.

"A parte mais difícil é redesenhar a aplicação, onde o desenvolvedor deve determinar o que o produto deverá fazer e como ele deve parecer ao usuário no novo contexto".

A maioria das aplicações para iPhone já desenvolvidas pode ser executada sem alterações no iPad, oferecendo aos usuários do iPad uma vasta biblioteca já no lançamento do produto. O iPad inclui um botão virtual 2X para permitir que uma aplicação feita para o iPhone possa rodar com um tamanho 200% maior que seu tamanho original de modo a se ajustar à tela maior do iPad; caso contrário, elas executarão com o mesmo tamanho com que aparecem no iPhone. Diversos desenvolvedores confirmaram que suas aplicações executaram sem alterações no emulador de iPad distribuido junto com o SDK.

"De saida o iPad já possui mais de 140.000 aplicações disponíveis na loja online da Apple. Quando o iPhone surgiu não haviam tantas aplicações e os usuários demoraram a adotá-las. Agora está mais fácil para as pessoas se empolgarem para possuir um iPad," disse Greenwood.

Ao promover o SDK revisado do iPhone, o site da Apple nota que os desenvolvedores podem criar applicações tanto para iPads quanto para iPhones usando um único binário. Isto permite aos desenvolvedores usar o mesmo código para acessar recursos que tenham diferenças no iPhone e no iPad, como aquelas relativas à interface gráfica, sem ter que separar o código para cada dispositivo - é possível testar para saber qual o tipo do dispositivo que está em uso para determinar qual trecho de código é apropriado.

"Esta é uma tecnologia antiga para a Apple," observa Greenwood. "Eles vêm fazendo isso a tempos, originalmente para habilitar aplicações Motorola 680x0 e PowerPC, depois com aplicações PowerPC e Intel 32-bit e agora com aplicações Intel 32-bit e Intel 64-bit."

Allen antecipa que haverá um upgrade para versão 4.0 do sistema operacional do iPad e do iPhone ainda este ano, provavelmente em Junho na conferência Apple World Wide Developers, quando a Apple historicamente anuncia novos iPhones. "Há algumas pistas. A Apple normalmente faz uma grande revisão todo ano," diz ele.

O texto original em inglês pode ser encontrado neste endereço.

12 de fevereiro de 2010

Interagindo com Forms HTML em programas Delphi e C++ Builder - parte III

No último post, apresentei um exemplo em que uma página HTML é exibida num programa Delphi, programa este que era capaz de interceptar uma navegação realizada no HTML e controlar os efeitos da navegação. No exemplo, apenas um campo era passado do HTML para o Delphi através de scripts embutidos na página. Agora, pretendo mostrar como capturar a navegação produzida num Form HTML e utilizar todos os campos preenchidos nesse form.

A ideia basica não é muito diferente da apresentada no outro post, já que acionar o botão de submit no Form HTML prepara a URL da mesma maneira que nós preparamos manualmente lá. Tome como exemplo o seguinte Form montado numa página HTML.
<form action="http://event-omega" method="get">
Nome Completo:<input type="text" name="edNome" />
EMail:<input type="text" name="edEmail" />
Detalhes:<textarea name="edDet" />
<input type="image" src="BV_Enviar.jpg" />
</form>

Acionar o botão "submit" nesse Form direciona o navegador à URL informada no parâmetro action da tag FORM. A esta URL são acrescentados os nomes e valores de todos campos existentes no Form - mesmo os escondidos, se houver - , produzindo ao final uma URL com parâmetros similiar à utilizada no post anterior:
http://event-omega/?edNome=nome_informado&edEmail=endere_email&edDet=texto_informado
Mais uma vez, o endereço http://event-omega não existe de fato e serve apenas para marcar posição de maneira que a navegação pode ser detectada corretamente pelo nosso programa. Portanto, o código será bastante parecido com o do post anterior, exceto pelo fato de que agora teremos que extrair mais de um parâmetro da URL - o que pode ser conseguido procurando pelo separador & (E-comercial), conforme pode ser observado na URL no quadro anterior.
if AnsiStartsStr ('http://event-omega/', URL) then
begin
lHtmlVars := TStringList.Create;
try
{ Captura apenas os parâmetros, isto é, o que vem após o sinal de interrogação }
lPos := AnsiPos ('?', URL);
lUrl := AnsiRightStr (URL, Length (URL) - lPos);
ExtractParams(URL, lHtmlVars);
finally
trataForm (lHtmlVars);
FreeAndNil (lHtmlVars);
end;

{ Vou navegar para outra página, cancela esta }
Cancel := true;

{ Navega para a nova página, que informará ao usuário sobre o sucesso de sua operação }
URL := 'file://' + ExtractFilePath (Application.ExeName) + 'bemvindo/BV_Form2.html';
FBrowserForm.Navigate(url);

Aqui eu simplesmente recuperei os parâmetros através de uma função minha (ExtractParams e os exibo num MessageBox. O código da função não é mostrado no quadro mas pode ser encontrado nos fontes para download) do projeto com o exemplo. Eu poderia ter feito um tratamento mais complexo com os parâmetros, incluindo a validação das informações fornecidas pelo usuário e cancelando a navegação se algo não estivesse de acordo com o esperado.

Note que no fim do evento eu redireciono a navegação para uma outra página, que mostrará ao usuário que a operação comandada por ele foi executada com sucesso. Como a URL para a qual estou redirecionando não tem o marcador 'http://event-omega/', eu não preciso verificar no início na operação qual é a página que está sendo tratada - haverá apenas uma que se encaixa no meu marcador. No entanto, em um sistema mais completo envolvendo múltiplas navegações, a verificação de nomes de páginas específicas pode ser obrigatório para evitar comportamentos estranhos.

Um inconveniente nessa abordagem é que os valores dos campos são incluídos na URL com algumas marcações próprias para evitar que a URL se torne inválida. Assim, caracteres em branco são substituídos por símbolos + (mais) e caracteres com acentuação são convertidos para um formato de escape que se inicia com uma % (porcentagem) e contem mais dois caracteres com o código da letra. Há que se tratar manualmente esses valores para obter o texto real entrado pelo usuário.

E se um dos campos do FORM for uma senha que deva ser escondida por razões óbvias ?
Se a navegação estivesse ocorrendo num navegador que exponha a URL numa barra de endereços - como o Internet Explorer, o Firefox e outros navegadores de mercado - isso seria mesmo um problema pois a senha apareceria completamente desprotegida na barra. Mas, no nosso programa a navegação é feita internamente, num modo em que o usuário não sabe tão diretamente qual é a URL onde ele está. Portanto, não haveria um risco de segurança como nos navegadores reais.

Ainda assim, o usuário pode clicar com o botão direito na página e obter esse endereço e, por conseguinte, os nomes e valores de todos os campos. Podemos evitar isso substituindo o método de envio do form HTML de GET para POST. O programa Delphi também terá que ser modificado pois com esse método as informações terão que ser recuperadas no parâmetro PostData do evento OnBeforeNavigate2. Para um exemplo de como obter os dados por este método, veja o site http://delphi.about.com/od/delphitips2007/qt/view_http_post.htm.

O programa de exemplo deste post e o do post anterior foram montados com Delphi 2005 e podem ser baixados aqui.

Mais Informações
Interação com HTML em Delphi - parte I e parte II, HTML FORMs, Download do Exemplo.

8 de fevereiro de 2010

Interagindo com HTML em programas Delphi e C++ Builder - parte II

Neste post, vou dar sequência ao anterior e mostrar como interagir com uma página HTML dentro de um programa desktop em Delphi - esquema que vale igualmente para o C++ Builder com a única ressalva de que nesse ambiente o nome do componente a ser usado é TCppWebBrowser ao invés de WebBrowser.

Na ABC71, exibimos uma página HTML para recepcionar os novos usuários de nosso ERP, apresentando-lhe uma espécie de tutorial da ferramenta. Uma caixa de seleção (checkbox) é apresentada no canto inferior esquerdo da página e permite ao usuário optar por remover a recepção, de modo que ela não é mais exibida quando ele se logar no sistema. Este efeito é obtido através da interceptação do evento de navegação na página HTML.

A caixa de seleção da página HTML que eu citei tem um evento de "clique" que força a navegação para uma URL controlada por mim. Através de um código javascript na página, eu adiciono a essa URL o status atual da caixa de seleção, isto é, se ela está marcada ou não. O trecho abaixo mostra o ponto do javascript em que a URL é redirecionada:
function ExibirClick () {
Exibir = ! Exibir;
if ( Exibir ) {
document.getElementById("img_exibir").src = "Checked.jpg";
location.href = "http://event-omega/?exibir=true";
}
else {
document.getElementById("img_exibir").src = "Unchecked.jpg";
location.href = "http://event-omega/?exibir=false";
}
}
Repare no endereço HTTP incluído na URL: http://event-omega/. Esse é um endereço que não existe de verdade. Ele está aí apenas para marcar posição já que minha aplicação Delphi pesquisará por esse nome para detectar que se trata da navegação que eu quero interceptar. Com isso, se tentar usar essa página sem o programa que intercepta a navegação, uma página de erro será exibida avisando que o endereço acessado não existe.

Repare ainda que é acrescentada à URL um texto iniciado por um sinal de interrogação (?). Este texto emula o padrão usado pela tag FORM de um HTML. Quando se configura a ação de uma tag Form para usar o método GET, o "clique" para submeter o conteúdo prepara a URL acrescentando-lhe a interrogação e uma sequência de pares de valores (que correspondem a parâmetros da aplicação e o respectivo valor de cada um) separados por um símbolo & (E Comercial). O navegador, então, usa a URL resultante para realizar a próxima navegação. No exemplo, apenas um parâmetro é acrescentado manualmente - exibir - que está assumindo o valor "false" ou "true", dependendo se a caixa está marcada ou não no HTML.

No programa Delphi que utilizará este HTML, devemos começar exibindo-o, através da função Navigate do TWebBrowser:
var lURL: String;
begin
lURL := 'file://' + ExtractFilePath(Application.ExeName) + 'bemvindo/BV_menu.html/';
FBrowser.Navigate(lURL);
end;
Agora, podemos responder ao evento de navegação OnBeforeNavigate2. Esse evento deve detectar que se trata da nossa URL, extrair os valores passados como parâmetros e utilizá-los conforme a necessidade.
if AnsiStartsStr ('http://event-omega/', URL) then
begin
lHtmlVars := TStringList.Create;
try
{ Captura apenas os parâmetros, isto é, o que vem após o sinal de interrogação }
lPos := AnsiPos ('?', URL);
lUrl := AnsiRightStr (URL, Length (URL) - lPos);
lHtmlVars.Add (lUrl);
finally
trataEvento (lHtmlVars);
FreeAndNil (lHtmlVars);
end;

{ cancela a navegação pois o tratamento já foi feito }
Cancel := true;
A presença de um StringList neste código é um atalho para facilitar o tratamento de pares de valores separados pelo sinal de igual (=). Valores com essas características adicionados a um StringList são acessados mais facilmente. Por exemplo, dentro da minha função trataEvento eu posso recuperar diretamente o valor do parâmetro 'exibir' enviado pelo HTML:
var lStrExibir: String;
begin
lStrExibir := AHtmlVars.Values ['exibir'];
Com o valor recuperado, posso armazenar a opção do usuário num banco de dados ou no Registry do Windows e não exibir mais a página quando ele se loga.

O programa de exemplo montado com Delphi 2005 pode ser baixado aqui.

4 de fevereiro de 2010

Interagindo com HTML em programas Delphi e C++ Builder - parte I

A linguagem HTML é amplamente utilizada na criação de sites devido as suas características, que permitem criar facilmente textos formatados e com diagramação complexa que incluam também imagens, animações e outros recursos.

No entanto, ela tem sérias restrições quando se tenta usá-la para criar aplicações ricas. Por "aplicação rica" eu quero dizer aquelas que são capazes de prover interações com o usuário de uma forma mais completa e responsiva. Elas contem recursos como organização em guias, apresentação de informações em árvores ou listas que podem responder a cliques do usuário, etc. As aplicações Windows tradicionais - (ditas Desktop) como o Excel ou um ERP como o Omega da ABC71 são exemplos de tais aplicações.

Por outro lado, em aplicações desktop é difícil de se tratar a exibição de textos formatados em que ocorram variações de tipo de fonte ou que apresentem hiperlinks e exibam imagens e animações - exatamente os recursos encontrados em um HTML.

Então, ter a capacidade de exibir páginas HTML numa aplicação desktop pode ser de grande utilidade. O Delphi e o C++ Builder incluem em suas paletas um componente chamado WebBrowser (ou CppWebBrowser) que permite exibir facilmente páginas HTML em aplicações desktop. Na verdade, este componente é um encapsulamento do navegador Internet Explorer de modo que ele tem os mesmos recursos que o navegador, podendo exibir páginas de forma idêntica. A grande diferença é que, como o componente está embutido em seu programa, é possível interceptar os eventos gerados pelo navegador e executar ações em cima desse eventos. Os eventos incluem seguir um link (permitindo redirecionar para uma outra página, por exemplo), o clique de botões (permitindo tratar o envio das informações contidas num formulário HTML), acompanhamento de downloads, entre outros.

O principal uso desse componente é exibir páginas HTML, o que pode ser obtido através da função Navigate:
FBrowser.Navigate('http://balaiotecnologico.blogspot.com/');

O parâmetro passado para esta função é a URL onde está o documento a ser exibido. A URL pode também apontar um endereço local, permitindo distribuir os documentos HTML necessários juntos com o seu programa:
var lURL: String;
begin
{ A URL deve ter o caminho completo, por isso extraio o caminho onde o executável está para prepará-la adequadamente, concatenando a esse caminho o protocolo FILE:// e acrescentando o nome do HTML }
lURL := 'file://' + ExtractFilePath(Application.ExeName) + 'bemvindo/bemvindo.html/';
FBrowser.Navigate(lURL);
end;

O evento OnDocumentComplete notifica o programa quando a carga de uma página terminou, o que pode ser usado para habilitar partes da interface gráfica no programa Delphi - por exemplo, habilitar um botão ou uma caixa de edição na tela. Caso a página indicada na URL seja constituida de Frames, este evento é executado uma vez para cada Frame, sempre que a carga de cada um é completada. Tenha em mente ainda que o OnDocumentComplete é disparado mesmo que a carga da página solicitada tenha dado erro. Neste caso, é possível detectar que uma situação de erro aconteceu respondendo ao evento OnNavigateError.

Outro evento importante é o OnBeforeNavigate2. Ele ocorre sempre que o usuário segue algum link na página ou pressiona um botão para submeter os dados de um formulário. O evento também é acionado quando se faz uma chamada à função Navigate dentro de seu programa. Todas as informações relativas à navegação são repassadas para este evento - a nova URL, os dados do formulário (se houver), o nome do frame aonde a página deve ser apresentada, etc. - de modo que é possível consistir tais informações e modificá-las apropriadamente se for necessário. Por exemplo, se o usuário tentar seguir um link ao qual ele não deve ter acesso, é possível redirecionar para uma página de erro ou até mesmo cancelar a navegação.

Esse componente também tem recursos para se transformar num editor de HTML no estilo WYSWYG, permitindo dar ao usuário final de seu programa um meio visual para modificar o conteúdo de páginas HTML mesmo sem conhecer essa linguagem. Falo desse recurso em outro post.

No próximo post eu mostro como fazer com que as interações do usuário com um HTML exibido no TWebBrowser sejam tratadas por um programa Delphi.