Leitores, hoje é dia de assunto inédito no blog!
Você sabiam que é possÃvel programar ações personalizadas na IDE do Delphi? Com algumas Interfaces especiais, chamadas de Open Tools API, podemos adicionar novos menus na IDE para executar ações especÃficas, codificadas por nós mesmos.
Para exemplificar este recurso, desenvolveremos um visualizador de DataSets em runtime que ficará disponÃvel no menu Help do Delphi.
Introdução
Você provavelmente utiliza – ou já utilizou – add-ins no Delphi para aprimorar a produtividade, como o GExperts, MMX, CnPack ou o DDevExtensions, certo? Essas extensões são tecnicamente conhecidas como “wizards” no RAD Studio e geralmente adicionam um novo menu na IDE do Delphi com várias opções para auxiliar nas atividades de programação. Porém, alguma vez você já se perguntou como estes wizards foram criados?
A resposta é simples. A Embarcadero disponibiliza um conjunto de Interfaces para trabalhar com as propriedades internas da IDE, chamadas de Open Tools API. Através dessas Interfaces, podemos, por exemplo, acessar o menu da IDE, o Project Manager, o editor de códigos ou a paleta de componentes. Com essa liberdade, é possÃvel “estender” a IDE, adicionando ações personalizadas conforme desejamos. O uso dessas Interfaces é extremamente útil, por exemplo, para desenvolver add-ins que aprimorem a produtividade dos desenvolvedores de uma equipe.
Para este artigo, faremos um wizard bem simples, mas muito útil, para visualizar os dados de um DataSet em tempo de execução (runtime). Essa funcionalidade pode nos ajudar bastante no rastreamento de erros e na análise da execução de rotinas. Imagine, por exemplo, que uma exceção esteja ocorrendo na iteração dos registros de um DataSet em uma classe não-visual. A princÃpio, não conseguimos verificar os dados que estão carregados naquele momento de maneira fácil. Seria interessante, portanto, se houvesse uma forma de exibir esses dados em uma Grid, não acham? 🙂
Bom, vamos partir para o hands-on!
1) Criar um pacote para adicionar o wizard
Existem basicamente dois meios de distribuir um wizard: por pacote ou por DLL. Neste artigo, abordaremos a primeira modalidade por ser mais didático.
Acesse o menu File > New > Other e selecione Package na janela New Items. O nome e diretório do pacote é de sua preferência.
2) Adicionar a referência ao “designide.dcp” na seção Requires
Essa inclusão nos permitirá ter acesso à s bibliotecas do Open Tools API em design-time. O arquivo “designide.dcp” está localizado na pasta ..lib\win32\release\
 do RAD Studio. Para adicioná-lo, clique com o botão direito na seção Requires e acione a opção Add Reference, informando-o no campo Package Name.
3) Adicionar uma nova unit no pacote
Só precisaremos de uma única unit para criar o nosso wizard. Clique com o botão direito no pacote e acesse o menu Add New > Unit, nomeando-a com o nome que desejar. A nova unit será anexada à seção Contains do pacote.
4) Escrever a classe
Antes de iniciar, adicione a unit ToolsAPI
na uses da unit para utilizarmos as Interfaces.
Pois bem, para que o Delphi reconheça o nosso pacote como um wizard, a classe deverá implementar a Interface IOTAWizard
, na qual exige a implementação dos oito métodos descritos abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
uses ToolsAPI; type TVisualizadorDataSets = class(TInterfacedObject, IOTAWizard) private { Assinaturas do IOTAWizard } function GetState: TWizardState; function GetIDString: string; function GetName: string; procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Execute; procedure Modified; end; |
No nosso exemplo, implementaremos somente os quatro métodos a seguir:
GetState
: para indicar se o wizard está habilitado;GetIDString
: para informar o identificador do wizard;GetName
: para informar o nome do wizard;Execute
: ação que o wizard irá executar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
uses  Vcl.Dialogs; function TVisualizadorDataSets.GetIDString: string; begin // Texto de identificação do wizard result := 'Visualizador de DataSets'; end; function TVisualizadorDataSets.GetName: string; begin // Nome do wizard, exigido pela IDE result := 'Visualizador de DataSets'; end; function TVisualizadorDataSets.GetState: TWizardState; begin // Indica que o wizard está habilitado na IDE result := [wsEnabled]; end; procedure TVisualizadorDataSets.Execute; begin // Executa a ação do wizard ShowMessage('Visualizador de DataSets acionado!'); end; |
Os outros métodos podem ficar vazios.
Uma vez que a nossa classe implementa a Interface IOTAWizard
, podemos registrá-la com o método RegisterPackageWizard
, informando uma instância da classe como parâmetro. A chamada deste método deve ser declarada dentro do procedimento padrão de registro de componentes do Delphi, ou seja, com o método Register
. Na verdade, essa etapa é semelhante ao registro de componentes do Delphi, quando usamos o RegisterComponents
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
type TVisualizadorDataSets = class(TInterfacedObject, IOTAWizard) private { Assinaturas do IOTAWizard } function GetState: TWizardState; function GetIDString: string; function GetName: string; procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Execute; procedure Modified; end; procedure Register; implementation procedure Register; begin // Registra o Wizard RegisterPackageWizard(TVisualizadorDataSets.Create); end; |
Neste momento, ao instalar o pacote (botão direito > Install), o wizard já será automaticamente registrado na IDE. Porém, se você navegar pelos menus do Delphi, não irá encontrá-lo. O motivo é que ainda não programamos o código para que ele fique visÃvel. Avançaremos, então, para o próximo passo.
5) Implementar a Interface IOTAMenuWizard
Já sabemos que, no Delphi, uma classe pode implementar várias Interfaces, correto? Bom, este será o nosso caso. Além de IOTAWizard
, a classe também implementará a Interface IOTAMenuWizard
, que exige apenas a implementação do método GetMenuText
. Este método, conforme já podemos presumir, é o texto do menu que aparecerá na IDE do Delphi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
type TVisualizadorDataSets = class(TInterfacedObject, IOTAWizard, IOTAMenuWizard) private { Assinaturas da Interface IOTAWizard } function GetState: TWizardState; function GetIDString: string; function GetName: string; procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Execute; procedure Modified; { Assinatura da Interface IOTAMenuWizard } function GetMenuText: string; end; implementation { ... } function TVisualizadorDataSets.GetMenuText: string; begin // Texto que aparecerá no menu Help > Help Wizards result := 'Visualizar DataSet'; end; |
Opa, agora, sim!
Quando instalarmos novamente o nosso pacote, um novo submenu será adicionado no menu Help > Help Wizards do Delphi, conforme ilustrado na imagem abaixo.
Por fim, ao clicarmos no menu, a mensagem que codificamos no método Execute
será exibida:
Interessante, não? 🙂
Nota: caso você receba uma mensagem informando que não foi possÃvel instalar o pacote, desinstale-o primeiro (botão direito > Uninstall) antes de instalá-lo novamente. Na verdade, ao realizar as alterações na unit, não é necessário reinstalar o pacote. A operação de Build, por si só, já atualiza o binário gerado, refletindo as alterações no wizard.
Na próxima parte do artigo, trabalharemos exclusivamente no método Execute
, que ficará responsável por salvar os dados do DataSet.
Até amanhã, pessoal!
[Delphi] Visualizador de DataSets com Open Tools API – Pacote
[Delphi] Visualizador de DataSets com Open Tools API – Evaluate
[Delphi] Visualizador de DataSets com Open Tools API – Formulário
[Delphi] Visualizador de DataSets com Open Tools API – Notificador
Bom dia!
Vou acompanhar essa série!
Excelente trabalho meu amigo(acho que já posso chamar assim né kkkkkk)
Abraço
Olá, amigo Elton! 🙂
Legal, acompanhe, sim! A segunda parte já foi publicada.
Abraço!
Olá André, tudo bem?
Sei que minha questão não está relacionada com o post, portanto, desde já me perdoe.
Então, recentemente, migrei meu projeto do Delphi XE6 para o Berlin, até aà nada demais no entanto. O problema começou a surgir sempre que eu dava um click em um componente (do próprio Delphi) como o Combobox, ou um Edit por exemplo. A aplicação simplesmente trava e o Windows dá mensagem que o programa parou de funcionar, então a aplicação é fechada. Como eu não consegui resolver este problema, testei em uma nova aplicação feita do zero no Delphi Berlim 10.1 Update 2. O problema continuou. Então fiz outro teste para tentar compreender melhor onde poderia estar contido o erro: Criei uma nova aplicação, com apenas o formulário principal, sem código algum e coloquei nesse formulário um combobox e um edit, mais nada. Somente um formulário, um combobox e um edit, sem código algum, e a aplicação continua parando de funcionar ao clicar no combobox ou no edit.
Há explicação aparente para isso? É um bug no Delphi Berlin?
Agradeço desde já André.
Muito obrigado.
Adriano.
Olá, Adriano.
Sinto muito por estar experienciando estes problemas com o RAD Studio Berlin. Acredito que não seja um problema da versão, já que a utilizo desde o primeiro Update e não tive complicações. De qualquer forma, vou entrar em contato para tentar ajudá-lo.
Abraço!
mto bom seu artigo
e a funcionalidade q vc desenvolveu ao longo dessa serie é brilhante! 😀
n tenho comentado mto, mas sempre acompanho seus posts! 😛
MMX eu nao conhecia, me parece ser pago, estou correto?
DDevExtensions eu tb n conhecia
sobre esses 2, saberia dizer de alguma funcionalidade q eu deveria conhecer?
estou instalando pra testar, mas se tiver algo para me direcionar…
atualmente uso CnPack, o fato dele sugerir prefixos nos nomes e colorir BEGINs e ENDs é o principal motivo de eu o usar, GExperts me pareceu um pouco redundante com CnPack, nao teve nenhuma opção q me chamou a atenção a ponto de ter ele instalado.
Abs
Fala, Conde, quanto tempo! 🙂
Isso mesmo, o MMX é pago, mas é bem produtivo.
O DDevExtensions incrementa algumas funcionalidades principalmente relacionadas à compilação como, por exemplo, mostrar o tempo dispendido.
O CnPack e o GExperts realmente possuem muitas funcionalidades semelhantes. Geralmente os desenvolvedores optam por apenas um deles. Acredito que o CnPack é mais utilizado, mas o GExperts traz algumas particularidades, como o Code Librarian (biblioteca de códigos), Code Proofreader (corretor de códigos) e o Grep Search (pesquisador de arquivos).
Nada impede que o desenvolvedor tenha 2 ou 3 desses add-ins instalados ao mesmo tempo, mas deve ter consciência de que eles deixam a IDE um pouco mais carregada.
Abraço!
Bom dia, parabéns pelo artigo.
Ao declarar a uses ToolsAPI, meu Delphi não a localiza: File not found: ‘ToolsAPI.dcu’
Adicionei no Library Path do Delphi: $(Delphi)\Source\ToolsAPI
Mas agora esta unit não está encontrando o arquivo: ‘DockForm.dcu’
Qual erro será que estou cometendo?
Olá, Paulo, tudo bem?
Peço desculpas pela demora. Por algum motivo, o seu comentário foi motivo para a caixa de spams.
Vou entrar em contato por e-mail. Abraço!
Boa tarde, André. Tudo bem com você ?
Seu artigo sobre MVC me ajudou em 2016 no desenvolvimento do meu projeto de TCC. Agora vou tentar abusar mais um pouco de você (no bom sentido, rsrsrsr). Não tenho muita experiência com criação de classes no Delphi. Estou desenvolvendo uma pequena aplicação freeware para uma escola pública da cidade de Limeira, para controle da biblioteca. A aplicação tem muitas pesquisas nos cadastros, e eu acabei achando um componente desenvolvido pelo Luciano Pimenta no site dele ( http://www.lucianopimenta.com/post.aspx?id=151 ) que, teoricamente, facilitaria o meu trabalho. Porém, eu quis dar uma “atualizada” e alterá-lo para as minhas necessidades (o componente é de 2010). Nesse componente ele utiliza ClientDataset e eu quis alterar para TFDQuery (FireDAC). O componente preenche um DGBrid com todos os registros da tabela e faz o filtro nele de acordo com o texto digitado em um Edit. Eu preferi já trazer os registros filtrados no DBGrid, etc. Inclusive acrescentei a possibilidade de ajustar algumas coisas em runtime, como os tÃtulos das colunas, seus tamanhos e alinhamentos através de propriedades utilizando vetores.
Agora é que vem a “zica”… Está dando um “Access Violation” no método que retorna a chave primária para a tela chamadora do componente. Instalei o EurekaLog trial e ele me mostrou um “Memory Leak” exatamente no método de retorno da chave. Mesmo com essa informação, não consegui localizar o erro. Seria muita ousadia da minha parte se eu lhe enviasse o componente para você dar uma olhada ? Já estou arrancando os cabelos…
Muito obrigado e desculpe se fui inconveniente.
Grande abraço.
Olá, Adalberto, boa noite!
Peço desculpas pela demora em respondê-lo. Por algum motivo o seu comentário caiu na caixa de spams, mas consegui recuperá-lo.
Adalberto, infelizmente não conheço este componente do Luciano Pimenta. Mesmo assim, como é relativamente antigo, talvez está acessando algum objeto ou propriedade que deixou de existir nas versões mais recentes do Delphi.
Fique à vontade para enviá-lo no e-mail [email protected]. Se você puder enviar um projeto de exemplo no qual você utiliza este componente, será ainda mais fácil identificar o erro.
Grande abraço!
OI André ! Obrigado pelo retorno. Antes de lhe encher a paciência, quero lhe dizer que entrei em contato com o Luciano pedindo uma ajuda, já que o componente é dele e fiz umas alterações e informei isso a ele. Eu estou desconfiado que é o seguinte: o componente cria o formulário de pesquisa em tempo de execução e libera esse formulário somente com o “.free”. Que eu me lembre o correto é FreeAndNil, ou “.free” seguido da atribuição de “nil” à variável que referencia o formulário, correto ? E pelo que me lembro de ter lido em algum site, o “.free” é usado para “esperar” a execução de alguma coisa que não me lembro o que agora. Quando eu atribuo “nil” à variável já na linha seguinte ao “.free” ou uso FreeAndNil(form) parece que não dá tempo do método de retorno buscar o valor da chave primária, ocorrendo a “Access Violation” (o método não consegue acessar aquela posição da memória, pois o formulário já está destruido). Mas agora tudo está funcionando, porém com esse “memory leak” que incomoda. Afinal, é um erro de programação. Se ele não responder eu preparo uma aplicação rapidinho documentando as linhas e lhe envio.
Muito obrigado novamente e grande abraço.
Olá, Adalberto!
O problema realmente pode estar relacionado com o
Free
, mas acho pouco provável, pois o erro aparentemente não ocorria na época em que o Luciano escreveu o componente. Eu apostaria em algum objeto que não foi inicializado ou foi precocemente destruÃdo. A propósito, pode ser o formulário mesmo.Se ele não responder, ficarei grato em ajudar!
Abração!