gerenciando a abertura de views, - atual sistemas …culos do desenvolvedor –orientação...
TRANSCRIPT
Manual do Desenvolvedor
Gerenciando a Abertura de Views,
ModalPanels e DataFile’s Para Melhor
Desempenho
Esse documento é uma obra intelectual de uso restrito da Atual Sistemas. Qualquer ato de leitura, alteração, cópia ou distribuição,
completa ou parcial deve ser feita somente sob autorização expressa. A posse ou uso não autorizado desse documento, ou do seu
conteúdo completo ou parcial, constitui-se um uma violação direta dos direitos de intelectualidade e será julgada conforme o rigor da lei.
Fascículo I
Junho/2011
ii Fascículos do Desenvolvedor
Conteúdos
Trabalhando Com Janelas Visuais Deferred 1
Abrindo Tabelas No Momento Certo 9
Usando Comando “Open As”, Slots e Apoio dos Recursos Globais 12
Recomendações Para um Bom Fonte 15
Ativação e Desativação de Janelas – O Método Sugerido 20
Capítulo 1
Trabalhando Com Janelas Visuais Deferred
Ao desenvolver aplicações gráficas, para que haja uma melhor interação entre Usuário-Software, se faz um extensivo uso de
Janelas. Janelas diferem entre si não somente em aparência e conteúdo, mas principalmente em comportamento. Por isso,
muitas vezes um sistema requer um grande número de janelas. Em cada uma dessas janelas, há componentes e objetos que
compõe o seu conteúdo e as tornam necessárias. Quando temos que lidar com um grande número de janelas, cada uma
com os seus componentes, isso pode significar a necessidade de um grande volume de recursos do sistema, o que requer
um bom gerenciamento.
Esse capítulo aborda que recursos o Visual DataFlex nos dispões, bem como que recursos foram desenvolvidos pela própria
Atual Sistemas, para que não venhamos a estar desperdiçando recursos necessários ao inserir novas janelas no sistema.
Como o Visual DataFlex Classifica Janelas
Seguindo o modelo utilizado no Visual DataFlex, nós podemos classificar janelas em duas categorias (há mais tipos de
janelas):
Modal Janelas modal são caracterizadas por não permitirem que o usuário selecione janelas que já foram abertas
anteriores a ela na mesma aplicação. Em Visual DataFlex nós usamos as classes ModalPanel e ReportPanel para
criar janelas modal, sendo a ultima classe usada apenas para relatórios.
Modeless Janelas modeless são usadas em casos quando não desejamos impedir que usuário faça outras tarefas
na aplicação enquanto essa janela está aberta. Em Visual DataFlex nós usamos as classes View e ReportView para
criar janelas modaless, sendo a ultima classe usada apenas para relatórios.
Dica Consulte o apêndice sob título Ativação e Desativação de Janelas – O Método
Sugerido.
Ativação Atrasada de Janelas
Todos os objetos visuais que pertencem ao escopo (que estão dentro) do objeto Client_Area devem ser criados no regime
Deferred (efeito de criação atrasada). Isso significa que o conteúdo dessas janelas só será criado quando elas forem ativadas
pela primeira vez. Isso evita o consumo desnecessário de memória e reduz o tempo necessário para iniciar a aplicação
Para tornar uma View ou ReportView deferred, troque o valor da propriedade Deferred Object para True, ou faça a
substituição do código A pelo código B seguindo as características do seu objeto.
//Código A
Activate_View Activate_MeuRelatorio for MeuRelatorio
Object MeuRelatorio is a ReportView
// Restante do código
End_Object
Troque o código acima para
//Código B
Deferred_View Activate_MeuRelatorio for ;
Object MeuRelaorio is a ReportView
// Restante do código
Cd_End_Object
2 Fascículos do Desenvolvedor
Para objetos ModalPanel ou ReportPanel, apenas troque a propriedade CD Popup para true.
Uma vez que você determina que um objeto visual tenha a inicialização atrasada, nenhum de seus objetos filhos será criado
até que você chame o método de disparo, no caso Activate para View’s e ReportView’s (Modeless) e Popup_Modal para
ModalPanel’s e ReportPanel’s (Modal). Isso significa que até que algumas dessas funções sejam chamadas, nenhuma
informação referente ao objeto e aos seus filhos poderá ser manipulada.
Para desativar objetos visuais Deferred, se você estiver lidando com um objeto modeless, use o procedimento Deactivate.
Para diálogos modais envie Close_Panel para o componente segundo o nome dele.
Atenção: Ao passar um componente para deferred, não referencie objetos filhos de componentes modeless (ex:
ReportPanel, ModalPanel ...) usando o nome do componente no endereço. Por exemplo, caso você tenha um
componente modeless chamado Consulta_SL que possui um objeto filho chamado Exata, não referencie esse objeto
com a seguinte síntese:
Send AlgumaCoisa to (Exata(Consulta_SL(Self)))
Nesses casos, não use o nome do componente visual. Ao invés disso, use apenas o self. Caso você tenha que usar
realmente o nome do componente, acrescente _CD no final do nome do componente. Isso é porque um objeto
Consulta_SL_CD será criado em tempo de execução de modo transparente e é esse objeto quem abriga os
controles filhos.
// Exemplo SEM o nome do componente
Send AlgumaCoisa to (Exata(Self))
// Exemplo COM o nome do componente
Set Label of (Consulta_SL_CD(Self)) to "Consulta Geral"
Send Close_Panel to (Consulta_SL_CD(Self))
Como Enviar Dados Necessários Após a Ativação da Janela
Já que os dados de um objeto só ficam disponíveis após a sua criação, foram desenvolvidos e disponibilizados sistemas para
compartilhamento de dados que se comportem de maneira segura e adequada entre os módulos visuais. Esses são o
Sistema de Parâmetros Globais, Sistema de Registros Globais e o Gerenciador de Dados Múltiplos. Seguindo a ordem em que
foram alistados tente dar prioridade a eles. Caso o primeiro não seja suficiente, tente o segundo, se esse também não for
suficiente tente o terceiro. Portanto segue abaixo como resolver algumas das situações, o que requererá criatividade do
programador na escolha apropriada.
SPG – Quando é necessário passar um parâmetro Talvez uma informação que era definida antes da ativação de módulo possa ser transformada em um parâmetro no sistema
de recursos globais. Por exemplo: O ForBax tinha de abrir o ForExc em dois modos que diferiam no botão que o usuário
clicava para abrir o ForExc. Um dos botões era para consulta e, portanto tudo que estivesse relacionado com alterações teria
que ser desabilitado a fim de que toda a tela se comportasse em modo somente leitura. O outro deveria permitir que o
usuário fizesse alterações. Essa informação deveria estar disponível para o diálogo durante a sua criação. Como resolver a
situação?
Foi inserido um parâmetro no Sistema de Parâmetros Globais. Ao ser aberto, o ForExc procura pelo parâmetro. Caso ele seja
encontrado, se entende que a exibição deve ser em modo somente leitura. Durante a consulta se realiza a exclusão do
parâmetro já que ele será consultado apenas uma vez durante a ativação do objeto visual.
O código abaixo demonstra a criação do parâmetro antes da ativação do diálogo.
// ForBax.vw – cabeçalho
Use vdfclasses.pkg
Use ForExc.vw
// Dentro do ForBax...
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
3 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
//
Procedure OnClick
If (FORMOV.RECIBO > 0) Begin
Integer iResult
Get InserirParametro of (oRecursosGlobais(Self)) FOREXC_SOMENTE_LEITURA to iResult
Send Activate_ForExc
Send Deactivate_Group to (ForBax(Self))
End
End_Procedure
Note que a constante que FOREXC_SOMENTE_LEITURA não está definida no arquivo FORBAX.VW, mas no FOREXC.VW já
que é lá que está o fonte a quem o parâmetro se refere.
// ForExc.vw – Cabeçalho.
Use vdfclasses.pkg
Define FOREXC_SOMENTE_LEITURA for |CS"FOREXC_SOMENTE_LEITURA"
// Dentro do ForExc...
//
Procedure Activate
If ((DeletarParametro(oRecursosGlobais(Self), FOREXC_SOMENTE_LEITURA)) = OK_) Begin
Set Label of (ForExc(Self)) to "Consulta de Recibo - Fornecedor"
Set Bitmap of (BtnExcluir(Self)) to ""
Set Bitmap of (BtnLimpar(Self)) to ""
Set Enabled_State of (BtnExcluir(Self)) to False
Set Enabled_State of (BtnLimpar(Self)) to False
End
Else Begin
Set Label of (ForExc(Self)) to "Exclusão de Recibo - Fornecedor"
Send SetaJPG "Excluir.jpg" (BtnExcluir(Self))
Send SetaJPG "Limpar.jpg" (BtnLimpar(Self))
Set Enabled_State of (BtnExcluir(Self)) to True
Set Enabled_State of (BtnLimpar(Self)) to True
End
Forward Send Activate
End_Procedure
Conforme você pode notar, você deverá usar essas funções a partir do objeto oRecursosGlobais. Esse objeto deve ser
sempre usado com a finalidade do compartilhamento de dados globais entre views. Quanto a função DeletarParametro ela
funciona de maneira idêntica a função ConsultarParametro, com a única diferença que ela apaga o parâmetro quando o
encontra. Caso o parâmetro seja encontrado o retorno de ambas as funções será um OK_, caso contrário será um
INVALID_HANDLE_VALUE.
Um parâmetro é uma string. Como você deve ter notado, foi criada uma constante que em tempo de compilação deixa de
existir e é substituída a referência da constante pelo seu valor. Sempre recorra ao uso de constantes para evitar erros de
programação difíceis de detectar.
Use sempre como prefixo o nome do objeto que precisa do parâmetro (no exemplo acima FORBAX) usando um underscore
(_) onde ficaria o espaço e em seguida descrevendo a finalidade do parâmetro separada por underscore onde houver
necessidade de espaço. Tudo em maiúsculo.
Nomeie a constante que armazenar o valor de modo idêntico ao valor e lembre-se que você não pode ter dois parâmetros
iguais no SPG. Então a string deve ser única. Você pode descobrir se houve uma tentativa de criar um parâmetro que já
existe por verificar se o retorno de InserirParametro é INVALID_HANDLE_VALUE.
Lembre. O valor string de um parâmetro deve atender as seguintes expectativas:
Estar armazenado em uma constante de compilação e ser referido por essa constante sempre.
A string deve ser maiúscula e deve-se usar underscore (_) no lugar de espaço.
Deve-se colocar o nome do módulo que tornou o parâmetro necessário seguido por underscore.
A constante deve ser nomeada segundo o seu valor.
O valor deve ser único em todo o sistema.
Nota: Explicações mais detalhadas e específicas podem ser encontradas nos comentários do arquivo classes.pkg.
4 Fascículos do Desenvolvedor
SRG – Quando é Necessário Passar um Registro Agora que você está um pouco familiarizado com o sistema de troca de dados entre objetos visuais, imagine que você tenha
que passar uma informação que não tem um valor fixo determinado. Por exemplo, o diálogo Acrescimo (Acrescimo.dg) não
pode ser Deferred porque um de seu controles recebe um valor antes de ser exibido. E o valor de um dos controles é
recuperado como resultado após ser fechado. Para resolver esse problema, basta se recorrer ao Sistema de Registros
Globais.
Um registro é constituído de identificador e um valor. Um identificador, ou chave, deve seguir as seguintes regras:
Estar armazenado em uma constante de compilação e ser referido por essa constante sempre.
A string deve ser maiúscula e deve usar underscore (_) no lugar de espaço.
Deve-se colocar o nome do módulo que tornou o parâmetro necessário seguido por underscore (FORBAX_).
A constante deve ser nomeada segundo o seu valor, porém usando um prefixo REG_ (Ex.
REG_FORBAX_MEU_REGISTRO).
O identificador deve ser único em todo o sistema.
O valor do registro pode ser de qualquer tipo, sem exceções.
No caso do problema acima, segue abaixo o modelo de como se resolveria a questão da atribuição e retorno do valor
usando-se um registro.
// ESTMOV.VW – cabeçalho
Use vdfclasses.pkg
Use Acrecimo.dg
// Dentro do EstMov...
//
Procedure OnClick
Integer iResult
Number nVrAcrecimo
If (ESTMOV.PRE_VENDA = 'S' and CADOPE.VENDA = 'S' and ESTMOV.VR_BRUTO > 0 and ;
ESTMOV.DATA_EXC = 0) Begin
Get CriarRegistro of (oRecursosGlobais(Self)) REG_ESTMOV_ACRESCIMO ESTMOV.VRBRUTO To iResult
Send Popup_Modal to (Acrescimo(Self))
Get LerRegistro of (oRecursosGlobais(Self)) REG_ESTMOV_ACRESCIMO (&nVrAcrescimo) To iResult
Get RemoverRegistro of (oRecursosGlobais(Self)) REG_ESTMOV_ACRESCIMO to iResult
If (nVrAcrecimo > 0) Begin
// Faz aqui todos os demais processos.
End
End
End_Procedure
A seguir note o que será necessário fazer no arquivo Acrecimo.dg.
// ACRESCIMO.DG – cabeçalho
Use vdfclasses.pkg
Define REG_ESTMOV_ACRESCIMO for |CS"ESTMOV_ACRESCIMO"
// Dentro de Acrescimo...
//
Procedure Activating Returns Integer
Integer iResult
Number nVrAcrecimo
Get LerRegistro of (oRecursosGlobais(Self)) REG_ESTMOV_ACRESCIMO (&nVrAcrescimo) To iResult
If (iResult = OK_) Set Value of (oBruto(Dados(Self))) to nVrAcrecimo
Forward Send Msg_Activating to iResult
Procedure_Return iResult
End_Procedure
Procedure Deactivating Returns Integer
Integer iResult
Number nVrAcrecimo
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
5 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Get Value of (oValor(Dados(Self))) to nVrAcrecimo
Get AlterarRegistro of (oRecursosGlobais(Self)) REG_ESTMOV_ACRESCIMO nVrAcrescimo To iResult
Forward Send Msg_Deactivating to iResult
Procedure_Return iResult
End_Procedure
Os exemplos acima mostram como usar o sistema de registros. É claro que como você o usará não se limitará somente a um
modelo como esse.
Para o uso de registro você lidará com as funções CriarRegistro, AlterarRegistro, LerRegistro e RemoverRegistro. Todas
essas funções quando funcionam com êxito retornam OK_ e quando falham retornam INVALID_HANDLE_VALUE. Sempre
verifique esse valor de retorno, pois caso qualquer uma das funções falhem, nenhum efeito ocorrerá no sistema de registros.
Por exemplo, suponhamos que por descuido você crie uma chave e não a remova em determinada situação. Ao tentar criá-la
novamente, se identificará que uma chave já existe e a função CriarRegistro irá falhar. O que significa que o valor que você
passou não será armazenado porque uma chave idêntica foi encontrada. Caso você não verifique o valor de CriarRegistro,
nenhum problema aparente será notado. Quando você tentar lê a chave, você irá encontrá-la com o mesmo valor que ela
estava antes da tentativa frustrada de criar uma nova chave. O que poderá trazer resultados inesperados para a sua lógica.
Nota: Explicações mais detalhadas e específicas podem ser encontradas nos comentários do arquivo classes.pkg.
GDM – Quando é Necessário Passar Dados Múltiplos Em alguns casos talvez o fonte fique extremamente complexo e trabalhoso se você tiver que lidar com registros. Isso se dá
principalmente em casos onde diversos valores devem ser informados em conjunto. Nesse caso talvez seja melhor usar um
contêiner.
Para entender em que situações deve se usar um contêiner tenha sempre em mente os seguintes passos. Se os pontos
delineados abaixo forem atendidos, então você deve usar um contêiner para a sua tarefa.
Não é possível ou não é benéfico transmitir a informação com um ou mais parâmetros.
Não é possível ou não é benéfico transmitir a informação com um ou mais registros.
O uso de contêiner tornará o fonte mais fácil de entender.
A quantidade de dados em questão varia conforme as circunstâncias e geralmente é desconhecido (por ex. Às vezes
eu tenho A e B e as vezes eu tenho A, B, C e D).
Precisa ser listado em uma ordem linear.
Caso pelo menos quatro pontos sejam verdadeiros no seu caso, então talvez seja apropriado usar um contêiner.
A definição de contêiner e o uso são bem simples. Um contêiner é um Array com um identificador único, similar ao utilizado
pelo sistema de registros globais. O identificador deve ser passado para se usar o contêiner. Esse identificador é uma string e
a seguinte convenção deve ser usada para se nomear um identificador.
Estar armazenado em uma constante de compilação e ser referido sempre por essa constante.
A string deve ser em maiúsculo e deve usar underscore (_) no lugar do espaço.
Deve-se colocar o nome do módulo que tornou o parâmetro necessário seguido por underscore (FORBAX_).
A constante deve ser nomeada segundo o seu valor, porém usando um prefixo CON_ (Ex.
CON_FORBAX_MEU_REGISTRO).
O identificador deve ser único em todo o sistema.
Por exemplo, o procedimento mGravaLogOperacao que é responsável por exibir o diálogo LogOperacao costumava fornecer
dados para a grid que havia no diálogo. Esses dados eram fornecidos diretamente se usando um handle para ter acesso a
grid. Assim eram acrescidas informações ao conteúdo da grid e verificada a quantidade que fora inserida. O exemplo parcial
abaixo demonstra isso.
// PREVENT.PKG
6 Fascículos do Desenvolvedor
//...
Procedure mGravaLogOperacao String sTabela String sAcao
//...
//...
Move (Eval(oGridOperacoes(LogOperacao(Self)))) to hGrid
Send Delete_Data to hGrid
move 0 to iCount
//...
Move (Trim(Lowercase(sFile))) to sFile
If_ (Left(sFile,3) = 'log' and right(sFile,4) = '.log') Begin
move '' to dVar
//...
Direct_Input channel 1 sFile
Repeat
Readln channel 1 sVar
move (Seqeof) to bAcabouLinhas
//...
If_ (Trim(sVar) <> '') begin
//...
Send Add_Item to hGrid msg_none (left(sVar,39))
Send Add_Item to hGrid msg_none (mid(sVar,200,45))
Set Item_Shadow_State of hGrid item iCount to True
Set Item_Shadow_State of hGrid item (iCount+1) to True
Add 2 to iCount
end
Until (bAcabouLinhas)
Close_Input channel 1 sFile
//...
If_ (Item_Count(hGrid) > 0) Begin
//...
Set Label of (txtFormulario(oMensagem(LogOperacao(Self)))) to sFormulario
Set Visible_State of (txtAbortar(LogOperacao(Self))) to (Right(sAcao,1) = 'X')
Send Popup to (LogOperacao(Self))
//...
End
//...
End
//...
End_Procedure
Esse modelo funciona com componentes que não são iniciados atrasados (em modo deferred). Uma vez que o LogOperacao
passou a ter a inicialização atrasado, se tornou necessário um método para o transporte dessas informações que eram
reunidas antes da abertura do diálogo.
Como você pode notar também, além ser necessário preencher a grid, também era necessário informar o valor do Label de
um TextBox e o Visible_State de outro. Porém esse dois problemas podem ser facilmente resolvidos com um registro e um
parâmetro para cada um respectivamente.
No caso da grid apenas um contêiner de dados resolveria o problema, e isso é ilustrado na solução abaixo.
// PREVENT.PKG
//...
Procedure mGravaLogOperacao String sTabela String sAcao
//...
//...
Get CriarConteiner of (oRecursosGlobais(Self)) CON_LOGOPERACAO_GRADE to iResult
Send Delete_Data to (Conteiner(oRecursosGlobais(Self), CON_LOGOPERACAO_GRADE))
move 0 to iCount
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
7 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
//...
Move (Trim(Lowercase(sFile))) to sFile
If_ (Left(sFile,3) = 'log' and right(sFile,4) = '.log') Begin
move '' to dVar
//...
Direct_Input channel 1 sFile
Repeat
Readln channel 1 sVar
move (Seqeof) to bAcabouLinhas
//...
If_ (Trim(sVar) <> '') begin
//...
Set ValorConteiner of (oRecursosGlobais(Self)) CON_LOGOPERACAO_GRADE item ;
(Item_Count(Conteiner(oRecursosGlobais(Self), CON_LOGOPERACAO_GRADE))) to ;
(Left(sVar,39))
Set ValorConteiner of (oRecursosGlobais(Self)) CON_LOGOPERACAO_GRADE item ;
(Item_Count(Conteiner(oRecursosGlobais(Self), CON_LOGOPERACAO_GRADE))) ;
to (Mid(sVar,200,45))
end
Until (bAcabouLinhas)
Close_Input channel 1 sFile
//...
If_ ((Item_Count(Conteiner(oRecursosGlobais(Self), CON_LOGOPERACAO_GRADE))) > 0) Begin
//...
Get CriarRegistro of (oRecursosGlobais(Self)) REG_LOGOPERACAO_FORMULARIO sFormulario ;
to iResult
If (Right(sAcao,1) = 'X') Get InserirParametro of (oRecursosGlobais(Self)) ;
LOGOPERACAO_ABORTAR_VISIVEL to iResult
Send Popup to (LogOperacao(Self))
//...
End
//...
End
Get RemoverConteiner of (oRecursosGlobais(Self)) CON_LOGOPERACAO_GRADE to iResult
//...
End_Procedure
A função CriarConteiner cria um contêiner com o identificador único que foi passado para ela. Caso nenhum contêiner com
o mesmo identificador tenha sido criado e tudo corra bem, é retornado OK_; caso contrário retorna
INVALID_HANDLE_VALUE.
Um contêiner é simplesmente um objeto cSetTabela, uma classe derivada da classe Set (um implemento de Array). A
função Conteiner retorna um handle para que você tenha acesso a esse objeto. Porém, evite o uso dessa função e sempre
copie o valor retornado a partir dela para um handle, caso você tenha que recorrer a função Conteiner constantemente. Isso
porque toda vez que a função Contener é chamada, uma busca é realizada para achar o contêiner segundo o identificador
informado. Caso nenhum contêiner seja encontrado, INVALID_HANDLE_VALUE é retornado.
O método propriedade ValorConteiner é usado para se alterar/consultar os elementos do contêiner informado (usando
Get/Set respectivamente). Caso deseje alterar os elementos do contêiner por um handle, terá o mesmo efeito que o uso
desse método.
Por fim, o método RemoverConteiner é usado para remover o contêiner do Gerenciador de Dados Múltiplos. Lembre-se
sempre de usar esse método e deixe que o gerenciador se encarregue de destruir o contêiner com seus elementos. Nunca
envie Destroy para o handle de algum contêiner, porque a referência permanecerá no gerenciador.
Segue abaixo as alterações que seriam necessárias no Diálogo para que o fonte acima desse certo.
// LOGOPERACAO.DG
//...
Define CON_LOGOPERACAO_GRADE for |CS"LOGOPERACAO_GRADE"
Define REG_LOGOPERACAO_FORMULARIO for |CS"LOGOPERACAO_GRADE"
8 Fascículos do Desenvolvedor
Define LOGOPERACAO_ABORTAR_VISIVEL for |CS"LOGOPERECAO_ABORTAR_VISIVEL"
Cd_Popup_Object LogOperacao is a ModalPanel
//...
//... Faz declaração de objetos.
//...
Procedure Activating Returns Integer
Integer iResult iIndice iLimite
Handle hConteiner
String sFormulario
Set Dynamic_Update_State of (oGridOperacoes(Self)) to False
Get Conteiner of (oRecursosGlobais(Self)) CON_LOGOPERACAO_GRADE to hConteiner
If (hConteiner <> INVALID_HANDLE_VALUE) Begin
Move ((Item_Count(hConteiner)) - 1) to iLimite
For iIndice from 0 to iLimite
Send Add_Item to (oGridOperacoes(Self)) msg_none ;
(ValorConteiner(oRecursosGlobais(Self), CON_LOGOPERACAO_GRADE, iIndice))
Set Item_Shadow_State of (oGridOperacoes(Self)) ;
item ((Item_Count(oGridOperacoes(Self))) -1) to True
Loop
End
Set Dynamic_Update_State of (oGridOperacoes(Self)) to True
Get DeletarParametro of (oRecursosGlobais(Self)) LOGOPERACAO_ABORTAR_VISIVEL to iResult
Set Visible_State of (txtAbortar(Self)) to (iResult = OK_)
Get LerRegistro of (oRecursosGlobais(Self)) ;
REG_LOGOPERACAO_FORMULARIO (&sFormulario) to iResult
If (iResult = OK_) Set Label of (txtFormulario(oMensagem(Self))) to sFormulario
Forward Get Msg_Activating to iResult
Procedure_Return iResult
End_Procedure
Procedure Deactivating Returns Integer
Integer iResult
Get RemoverRegistro of (oRecursosGlobais(Self)) REG_LOGOPERACAO_FORMULARIO to iResult
Forward Get Msg_Deactivating to iResult
Procedure_Return iResult
End_Procedure
Cd_End_Object
Conforme demonstrado nessa situação ilustrativa, cSlotTabelaDinamica se encarrega de todos os processos envolvidos,
eliminando a necessidade de você ter de se preocupar com o slot em si e com outros dados como a numeração dos campos
da tabela. O procedimento CriarTabelaDinamica é responsável por reservar o slot e reunir os meta-dados referente ao
nome das colunas. A propriedade phTabela contém o manipulador do slot reservado. A função Campo retorna o número
do campo conforme a string passada. Por fim, a função CampoValorCorrente retorna o valor do campo informado.
Após ser utilizado, o objeto deve ser destruído o quanto antes. Durante a destruição, a classe se encarrega de liberar o slot e
fechar a tabela.
Nota
A classe cRecursosGlobais possui uma série de usos não alistados aqui. Explicações
mais detalhadas e específicas podem ser encontradas nos comentários do arquivo
classes.pkg.
Capítulo 2
Abrindo Tabelas No Momento Certo
Um bom desenvolvedor zela pelo bom uso de recursos de sistemas, e quando se fala de recursos e otimizar, a base de
dados sempre terá o seu lugar nessa preocupação. Principalmente em nosso caso, o bom gerenciamento de recursos da
base é extremamente necessário já que a base de dados embutida do Visual DataFlex é uma base não relacional. Assim, cada
tabela possui um arquivo dedicado que é acessado usando recursos comuns do computador, da rede local ou do domínio.
Esse capítulo aborda como fazer o bom uso de comandos para abertura de tabelas de maneira que não desperdicemos
recursos reservando tabelas que no final não usaremos.
Bom uso do comando Open
Antes que uma tabela (ou arquivo de dados) possa ser referenciada no código, junto com os respectivos campos, para que
então seus dados sejam lidos ou alterados, é necessário abrir essa tabela. Para isso, o Visual DataFlex usa o Comando Open.
Esse comando diz para o compilador que ele conhece essa tabela e seus campos, e durante a execução do programa ele
reserva a tabela para uso.
O comando open deve ser usado com critério e as tabelas não devem ser abertas até que elas tenham realmente de ser
utilizadas. Por isso, devem ser removidos os comandos open que estão no escopo global dos arquivos.
Atualmente muitos cabeçalhos estão em uma situação semelhante a do nosso exemplo abaixo.
// ACAXPRE.RV
Use vdfclasses.pkg
Open CAXPRE
Open FCAXPRE
Deferred_View Activate_ACaxPre For ;
Object ACaxPre is a ReportView
// Fonte continua...
Cd_End_Object
Isso faz com que, nesse exemplo, os arquivos CAXPRE e FCAXPRE sejam abertos durante a criação do objeto Client_Area. O
que derruba a desempenho do sistema desde a inicialização quando não há necessidade de que esses arquivos estejam
abertos. Na verdade eles deveriam estar dentro de um objeto deferred, como no exemplo abaixo.
// ACAXPRE.RV
Use vdfclasses.pkg
Deferred_View Activate_ACaxPre For ;
Object ACaxPre is a ReportView
Open CAXPRE
Open FCAXPRE
// Fonte continua...
Cd_End_Object
Sempre deixe comandos de open dentro de algum escopo fechado. No caso de objetos, o escopo só será fechado se o
objeto for deferred.
10 Fascículos do Desenvolvedor
Sempre que possível, use Declare_DataFile
Em algumas situações, não é possível compilar a aplicação porque fora feita referência a alguma tabela que ainda não fora
aberta. Para solucionar esse problema, o programador geralmente recorre ao comando Open. Embora essa tenha sido a
solução para o problema em versões antigas do VDF, as versões mais recentes do Visual DataFlex dispões de outro recurso
para resolver esse problema fazendo bom uso dos recursos do sistema ao mesmo tempo.
O Comando Declare_DataFile tem o mesmo efeito em tempo de compilação que o comando Open, com a única diferença
que ele não reservará a tabela para uso em tempo de execução. Isso se torna extremamente útil em aplicações que fazem
extenso uso de DDO’s, já que os dicionários de dados se responsabilizarão em abrir a tabela no momento certo.
Assim, em pacotes (.PKG) que precisamos usar tabelas do banco para prover funcionalidades para Janelas que possuem
DDO’s, mas não se é possível compilar esses pacotes por falta do comando Open, basta usar o comando Declare_DataFile
neste pacotes. Veja um exemplo caso no código abaixo.
// FUNCOES.PKG
Open CADPRO
Open ESTSAL
Function fValida_Desconto Global Date dData Returns Number
If (CADPRO.DESCONTO_OK = "S") Begin
// Fonte continua...
End
End_Function
Nesse exemplo, o comando Open precisa estar presente no fonte para que seja possível compilar a linha que faz referência
ao CADPRO.DESCONTO_OK. Porém o fato de o comando Open estar no escopo global desse PKG faz com que esse
comando open seja executado durante a inicialização em algum ponto onde esse arquivo é incluso no projeto. Se nós
estamos usando o Open apenas para garantir a compilação, o comando Declare_DataFile seria suficiente nesse caso. Veja o
exemplo abaixo.
// FUNCOES.PKG
Declare_Datafile CADPRO
Declare_Datafile ESTSAL
Function fValida_Desconto Global Date dData Returns Number
If (CADPRO.DESCONTO_OK = "S") Begin
// Fonte continua...
End
End_Function
Nesse exemplo, a tabela não será aberta quando o PKG for incluso no projeto durante a inicialização do programa e nós
conseguiremos compilar esse arquivo sem nenhum problema com a referência ao campo CADPRO.DESCONTO_OK.
Contudo, nesse caso, quando fValida_Desconto fosse chamada, alguma outra parte da aplicação deveria se responsabilizar
em já ter essas tabelas abertas. Se não é possível garantir que a função fValida_Desconto será chamada apenas com CADPRO
e ESTSAL abertos, então é mais seguro usar o comando Open mesmo, mas nesse caso deve se usar da seguinte forma.
// FUNCOES.PKG
Function fValida_Desconto Global Date dData Returns Number
Open CADPRO
Open ESTSAL
If (CADPRO.DESCONTO_OK = "S") Begin
// Fonte continua...
End
End_Function
Nesse ultimo caso, o open abrirá a tabela e, por ele não estar no escopo global, isso só ocorrerá quando a função
fValida_Desconto for chamada. Essa deve ser a ultima alternativa para abrir a tabela. Geralmente, funções que acessam dados
de tabelas são chamadas a partir de Janelas que já abriram essas tabelas, o que torna o uso de Declare_DataFile mais
desejável para um fonte mais compreensível e seguro. Isso, porque se você usar o Open dentro da função, você terá de usá-
lo em cada função que precise fazer uso do arquivo em questão (no nosso exemplo, o ESTSAL).
Resumindo: Use o comando Open somente quando Declare_DataFile não puder realmente resolver a situação.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
11 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Os arquivos DD’s já estão fazendo uso desse tipo de recurso, por declararem em tempo de compilação que usam uma tabela
(ou arquivo de dados), mas só tentam abrir tais tabelas no momento em que uma instancia da classe DD é criada.
Veja o exemplo.
// cCadMsaDataDictionary.dd
Use DataDict.pkg
Declare_Datafile CADSEC
Declare_Datafile CADMSA
Declare_Datafile CADFUN
Class cCadMsaDataDictionary is a DataDictionary
Procedure Construct_Object
Open CADSEC
Open CADMSA
Open CADFUN
Forward Send Construct_Object
// Fonte continua...
End_Procedure
// Fonte continua...
End_Class
Uma classe de Dicionário de Dados como a do exemplo dado acima só abrirá o arquivo no momento em que a primeira
instancia dela for criada, o que pode significar que esse arquivo nunca será aberto se a janela que faz uso dele não for usada
pelo usuário durante a execução. Dessa forma, nenhum recurso do sistema é usado a menos que seja de fato necessário.
Capítulo 3
Usando Comando “Open As”, Slots e Apoio dos Recursos Globais
A base de dados do Visual DataFlex utiliza um método para organizar suas tabelas, ou arquivos de dados, de modo que cada
uma delas receba um número em uma lista que é chamado de FileList. Assim cada tabela no sistema possui um número, e
cada número desse é tecnicamente chamado de slot.
Esse capítulo abordará algumas técnicas providas pelo Visual DataFlex e por bibliotecas desenvolvidas pela Atual Sistemas
para fazermos bom uso desses slots para determinados tipos de consultas.
Abrindo Uma Cópia de Um Arquivo de Dados
Em alguns casos nós precisamos realizar uma consulta em uma tabela, mas isso não é possível porque nós temos dados no
buffer daquela tabela que não podem ser afetados. Ou então, pode acontecer de precisamos de comparar dois registros da
mesma tabela. Como podemos alcançar um registro específico de uma tabela sem afetar o registro corrente do buffer?
O comando Open As serve justamente para esse propósito. Com esse comando, nós podemos abrir uma cópia de uma
tabela em um slot vazio no banco de dados e realizar consultas nesse novo slot sem afetar o buffer da tabela original. Todas
as operações comuns de consulta podem ser realizadas usando-se comandos de acesso a dados de um arquivo de dados
que usam números como referências ao invés do nome simbólico dos campos (ex. Set_Field_Value, Get_Field_Value,
VFind). Ao final da tarefa, depois de termos realizado a consulta, nós nunca deixamos de chamar o comando Close para
liberar a cópia.
Embora essa técnica não seja nova há alguns reajuste cuja implementação é necessária. Isso porque é preciso uma
segurança maior na hora de decidir qual slot vazio deve ser utilizado. Caso seja aberta uma tabela sobre um slot que está
sendo usado temporariamente por outra cópia, isso resultará em problemas que inicialmente podem passar despercebidos,
mas que são completamente comprometedores.
Atenção: Não use reread ou operações de DDO que fazem alterações em arquivos de dados quando houver alguma cópia de
um arquivo de dados aberta.
Por exemplo, era comum o uso da função fNewFile ou um número específico (geralmente 1500) para determinar o slot que
a cópia do arquivo seria aberta. Esses métodos não são seguros e devem ser corrigidos. Segue abaixo uma situação típica.
// CADIMP.RV – Conteúdo
Integer iCampoID iCampoCaminho iCampoDescricao iCampoEstacao
Integer iID iIDFile iFile
String sCaminho sDescricao sCaminhoFile sDescricaoFile sEstacao
Get Value of (ID(Self)) to iID
Get Value of (Caminho(Self)) to sCaminho
Get Value of (Descricao(Self)) to sDescricao
Get_FieldNumber CADIMP.ID to iCampoID
Get_FieldNumber CADIMP.CAMINHO to iCampoCaminho
Get_FieldNumber CADIMP.DESCRICAO to iCampoDescricao
Get_FieldNumber CADIMP.ESTACAO to iCampoEstacao
Move (fNewFile()) to iFile // Função insegura para chamadas concorrentes...
Open "CADIMP" as iFile
Clear iFile
Set_Field_Value iFile iCampoID to iID
Set_Field_Value iFile iCampoCaminho to sCaminho
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
13 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Set_Field_Value iFile iCampoDescricao to sDescricao
VFind iFile 2 EQ
Get_Field_Value iFile iCampoID to iIDFile
Get_Field_Value iFile iCampoCaminho to sCaminhoFile
Get_Field_Value iFile iCampoDescricao to sDescricaoFile
If ((Found) and iID = iIDFile and sCaminho = sCaminhoFile and sDescricao = sDescricaoFile) Begin
Get_Field_Value iFile iCampoEstacao to sEstacao
End
Close iFile
Function_Return sEstacao
// Fonte continua...
Como proceder em um caso como esse? Nos casos onde é necessário o uso de "Open as" deverá se optar entre dois
métodos de uso. O primeiro é a reserva de Slots através do gerenciador de slots de oRecursosGlobais. E o outro é por meio
do uso de objetos da classe cSlotTabelaDinamica.
Usando oRecursosGlobais Com Gerenciador de Slots Entre as muitas funções de oRecursosGlobais está o gerenciamento dos slots vagos do FileList que estão sendo usados em
tempo de execução pela aplicação. Esse gerenciamento centralizado impede que haja conflitos no uso dos slots livres.
Seguindo a necessidade do exemplo dado anteriormente, mostraremos a solução por meio do gerenciador de slots de
oRecursosGlobais.
// CADIMP.RV – Conteúdo
Integer iCampoID iCampoCaminho iCampoDescricao iCampoEstacao
Integer iID iIDFile
String sCaminho sDescricao sCaminhoFile sDescricaoFile sEstacao
Handle hArquivo
Get Value of (ID(Self)) to iID
Get Value of (Caminho(Self)) to sCaminho
Get Value of (Descricao(Self)) to sDescricao
Get_FieldNumber CADIMP.ID to iCampoID
Get_FieldNumber CADIMP.CAMINHO to iCampoCaminho
Get_FieldNumber CADIMP.DESCRICAO to iCampoDescricao
Get_FieldNumber CADIMP.ESTACAO to iCampoEstacao
Get ReservarSlotLivre of (oRecursosGlobais(Self)) to hArquivo
Open "CADIMP" as hArquivo
Clear hArquivo
Set_Field_Value hArquivo iCampoID to iID
Set_Field_Value hArquivo iCampoCaminho to sCaminho
Set_Field_Value hArquivo iCampoDescricao to sDescricao
VFind hArquivo 2 EQ
Get_Field_Value hArquivo iCampoID to iIDFile
Get_Field_Value hArquivo iCampoCaminho to sCaminhoFile
Get_Field_Value hArquivo iCampoDescricao to sDescricaoFile
If ((Found) and iID = iIDFile and sCaminho = sCaminhoFile and sDescricao = sDescricaoFile) Begin
Get_Field_Value hArquivo iCampoEstacao to sEstacao
End
Close hArquivo
Send LiberarSlot of (oRecursosGlobais(Self)) hArquivo
Function_Return sEstacao
// Fonte continua...
Como você pode notar a função ReservaSlotLivre de oRecursosGlobais tem como retorno um handle para um slot livre
que a aplicação pode utilizar. Esse handle deve ser utilizado como manipulador da cópia que você criou e deve ser liberado
14 Fascículos do Desenvolvedor
no final da tarefa. Para liberar esse manipulador no gerenciador nós usamos LiberarSlot de oRecursosGlobais. Só chame
LiberarSlot após a chamada do comando Close.
Usando cSlotTabelaDinamica Para Simplificar a Consulta Como você pode notar no exemplo anterior, o uso do gerenciador de slots serve apenas para assegurar a segurança, mas
não simplificam o processo em si. Para que houvesse uma simplificação do processo, foi desenvolvido a classe
cSlotTabelaDinamica.
O uso de objetos cSlotTabelaDinamica é feito para se simplificar todos os processos envolvidos, desde o uso do slot até ao
acesso aos dados. O exemplo abaixo demonstra o uso de cSlotTabelaDinamica. Compare e veja o número de variáveis e
complicações que se reduz no uso dele.
// CADIMP.RV – Conteúdo
Integer iID
String sCaminho sDescricao sEstacao
Handle hcstdCadImp
Get Value of (ID(Self)) to iID
Get Value of (Caminho(Self)) to sCaminho
Get Value of (Descricao(Self)) to sDescricao
Get Create U_cSlotTabelaDinamica to hcstdCadImp
Send CriaTabelaTemporaria to hcstdCadImp "CADIMP"
Clear (phTabela(hcstdCadImp))
Set_Field_Value (phTabela(hcstdCadImp)) (Campo(hcstdCadImp, "ID")) to iID
Set_Field_Value (phTabela(hcstdCadImp)) (Campo(hcstdCadImp, "CAMINHO")) to sCaminho
Set_Field_Value (phTabela(hcstdCadImp)) (Campo(hcstdCadImp, "DESCRICAO")) to sDescricao
VFind (phTabela(hcstdCadImp)) 2 EQ
If ((Found) and (iID = (CampoValorCorrente(hcstdCadImp, "ID")) ;
and (sCaminho = (CampoValorCorrente(hcstdCadImp, "CAMINHO")) ;
and (sDescricao = (CampoValorCorrente(hcstdCadImp, "DESCRICAO")))))) Begin
Get CampoValorCorrente of hcstdCadImp "Estacao" to sEstacao
End
Send Destroy to hcstdCadImp
Function_Return sEstacao
// Fonte continua...
Conforme demonstrado nessa situação ilustrativa, cSlotTabelaDinamica se encarrega de todos os processos envolvidos,
eliminando a necessidade de você ter de se preocupar com o slot em si e com outros dados como a numeração dos campos
da tabela. O procedimento CriarTabelaDinamica é responsável por reservar o slot e reunir os meta-dados referente ao
nome das colunas. A propriedade phTabela contém o manipulador do slot reservado. A função Campo retorna o número
do campo conforme a string passada. Por fim, a função CampoValorCorrente retorna o valor do campo informado.
Após ser utilizado, o objeto deve ser destruído o quanto antes. Durante a destruição, a classe se encarrega de liberar o slot e
fechar a tabela.
Atenção: Não chame o comando Close para fechar um slot aberto por cSlotTabelaDinamica jamais.
Nota
A classe cSlotTabelaDinamica possui uma série de usos não alistados aqui.
Explicações mais detalhadas e específicas podem ser encontradas nos comentários do
arquivo classes.pkg.
Apêndice I
Recomendações Para um Bom Fonte
Segue abaixo sete recomendações que se forem seguidas com afinco combinadas com criatividade o farão não somente um
programador, mas sim um Desenvolvedor.
1. Não escreva longos procedimentos. Um procedimento não deve ter mais de dez ou doze linhas, deve ser escrito
para ser reutilizável para mais de uma situação e deve evitar afetar valores globais.
2. Todo procedimento deve ter uma finalidade clara. A finalidade de um procedimento não pode ser mesclada com a
finalidade dos procedimentos que vem antes ou depois dele. Um bom programa é uma série de procedimentos
limpos que não se sobrepõe um ao outro.
3. Não use características muito complexas da linguagem. Se o seu trabalho não é feito com simples declarações de
variáveis, chamadas de procedimentos, classes, declarações de controle de fluxo e operadores aritméticos, alguma
coisa está errada. Quando você usa os recursos básicos da linguagem isso o faz pensar no que você está
escrevendo. Não complique o que pode ser simples.
4. Nunca use recursos da linguagem que você não tem certeza como se comportarão. Se você costuma escrever
código que você não tem certeza como se comporta, é melhor reavaliar em que área você quer trabalhar.
5. Evite ao máximo usar Copiar & Colar, principalmente se você é um principiante. Assim você usará o menor número
de arquivos possíveis.
6. Evite usar referências a valores abstratos cuja origem pode ser desconhecida e o valor incerto (buffer, variáveis
globais).
7. De tempos em tempos tente entender como as bibliotecas que você usa funcionam. Procure ver porque
determinados processos são de um modo específico.
Em suma, essas recomendações podem parecer genéricas, mas tente vê-las de um modo prático ao cumprir com seu dever.
Siga um padrão claro
Há muitos erros escandalosos em Visual DataFlex que podem ser encontrados em fontes de Desenvolvedores conceituados,
mas isso não significa que o erro deles deve ser o nosso. Vamos considerar alguns erros básicos de programação que devem
ser evitados.
O Desenvolvedor deve sempre levar em mente ao escrever cada linha de código.
Essa linha deve ser clara e inteligível.
O fonte final deve estar asseado aparentando ordem e esmero, devidamente posicionados para melhor
compreensão.
Será que essa linha é realmente necessária?
Existe alguma forma de simplificar o que eu estou escrevendo agora?
Nunca poupe linhas que significarão cem linhas a menos no futuro e eliminarão possíveis bugs, mas nunca escreva mais
linhas de código para obter trinta minutos a menos de trabalho. Lembre-se que cada linha que você escreve no seu fonte é
mais uma chance de você estar inserindo um novo bug no seu projeto.
Dadas as recomendações teóricas, segue abaixo algumas recomendações práticas em VDF.
Sempre use notação matemáticas em declarações de controle de fluxo e condicionais ao invés de expressões verbosas
antigas do VDF. Apenas em útimo caso use essas expressões verbosas (ex, constraint), já que elas dificultam a compreensão
do fonte.
16 Fascículos do Desenvolvedor – versão 1.6.1
Assim, evite sempre que possível o uso de EQ, GE, GT, LE, LT e NE. Ao invés disso use sempre que possível <, <=, =, not,
<>, >= e >. (Ao usar esses operadores você será obrigado a usar parênteses, o que torna o fonte ainda mais claro.)
Verifique sempre se não é possível resumir a uma linha uma condição if ... else de duas linhas. Em muitos casos isso é
plenamente possível com a chamada expressão ternária. Por exemplo, veja o fonte abaixo.
If (CADPRO.DESCONTO_OK = "S") Move iDesconto to ESTMOV.DESCONTO
Else Move iValor to ESTMOV.DESCONTO
Embora o VDF não possua um operado ternário nativo, é possível criar uma expressão ternária usando a função If.
Move (If((CADPRO.DESCONTO_OK = "S"), iDesconto, iValor)) to ESTMOV.DESCONTO
Como você pode ver, a expressão abaixo é compacta e bem clara para entender, poupando uma linha para a visão de um
programador que procura entender o fonte.
Outro caso é quando estamos decidindo se atribuímos verdadeiro ou falso a algum elemento. Nesses casos, não há
necessidade de se usar IF ... Else. Por exemplo, note o código abaixo.
If (CADPRO.DESCONTO_OK = "S") Set Visible_State of oDesconto to True
Else Set Visible_State of oDesconto to False
Em um caso como esse é desnecessário usarmos duas linhas. Nós poderiamos declarar isso simplesmente assim.
Set Visible_State of oDesconto to (CADPRO.DESCONTO_OK = "S")
Evite operadores desnecessários. Como nós sabemos, o VDF é uma linguagem em si verbosa que possui certos operadores
como os parênteses, Begin e End que são desnecessários em certas circunstâncias. A menos que eles realmente contribuam
para um melhor entendimento do fonte, evite-os. Veja um caso de uso desnecessário abaixo.
If (CADPRO.DESCONTO_OK = "S") Begin
Move iDesconto to ESTMOV.DESCONTO
End
O código abaixo deveria ser simplesmente assim:
If (CADPRO.DESCONTO_OK = "S") Move iDesconto to ESTMOV.DESCONTO
Tenha uma linha de raciocínio clara sobre o seu propósito. Em alguns casos, programadores simplesmente complicam
demasiadamente o código com linhas em excesso onde algumas nem serão executadas. Obviamente, para evitarmos que
isso aconteça, precisamos primeiro escrever essas linhas por nós mesmo, nunca copiar e colar. Depois de escrever, tente ver
se não tem como dizer a sua lógica de um modo mais direto. Veja o exemplo abaixo.
Integer iRet
// No Final da Função ...
Get YesNoCancelBox "Tem certeza mesmo?" "Atenção" 1 To iRet
If (iRet = MBRYes) Begin
// Faz alguma coisa ...
Function_Return True
End
Else if (iRet = MBRNo) Function_Return False
Else if (iRet = MBRCancel) Function_Return False
Function_Return
Nesse exemplo, há três erros presentes no fonte. Primeiro, o programador usou um YesNoCancelBox para uma situação em
que um YesNoBox seria o suficiente, já que ele trata o resultado do Cancelar da mesma forma que ele trata o Não. Segundo,
todos esses condicionais poderiam ser resumidos em uma única linha. Terceiro, não é necessário um Else para chamar um
Function_Return ou um Procedure_Return quando a sentença condicional é a ultima sentença do método em questão.
Corrigindo esse código, o formato mais simples e direto seria o seguinte.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
17 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
// No Final da Função ...
If ((YesNoCancelBox("Tem certeza mesmo?", "Atenção")) = MBRYes) Begin
// Faz alguma coisa ...
Function_Return True
End
Function_Return False
Function_Return
Não crie validações em campos de entrada de dados que impeça os usuários de sair deles até digitar o dado correto.
Em tais procedimentos de validações ao sair do campo, caso seja retornado o valor 1, o usuário é impedido de sair do
campo. Isso é um erro de layout crasso. Não cometa esse erro.
Caso você precise validar um dado quando o usuário sair de um campo, faça de um modo que não afete o fluxo de uso da
aplicação. Por exemplo, limpe o valor inválido do campo, altere algum label que sirva como status, coloque a cor de fundo
do campo para vermelho, porém nunca exiba uma caixa de diálogo como resposta e muito menos impeça o usuário de
abandonar o campo.
Por ultimo, como um bom Desenvolvedor VDF, entenda como o buffer do arquivo de dados funciona em conjunto com o
DDO e use-os de acordo. Esse é um problema latente entre programadores VDF, mas uma breve explicação será fornecida
aqui.
Todo arquivo de dados em VDF pode ser acessado por um canal chamado de buffer do arquivo. Quando nós acessamos os
dados de uma tabela usando apenas o nome da tabela e o nome da coluna (ex,Stop_Box CADPRO.MATRICULA), nós
estamos na realidade lendo o buffer desse arquivo de dados diretamente. Nas primeiras versões do VDF, essa técnica era
satisfatória, entretanto quando é necessário ler e alterar vários registros da mesma tabela em várias telas simultaneamente, o
uso de um buffer que só pode direcionar um registro por vez para várias tarefas simultâneas pode ser desafiador. Isso
porque o buffer só direciona um registro por vez e ele é um só para toda a aplicação.
A fim de suprir essa deficiência, foi provido o DD ou Dicionário de Dados, que nada mais é do que uma classe de objetos
que usam o buffer para armazenar os dados de um registro específico. Assim, com o uso de objetos DD ou DDOs nós
podemos ter um DDO apontando para o registro 20 enquanto o buffer está apontando para o registro 10. Em um caso
desses, se eu usar os comandos do buffer para acessar os dados da tabela eu vou ter os valores do registro 10, enquanto se
eu usar as funções do DDO para ler os dados da tabela, eu vou ter os valores do registro 20. Com tal forma de organização
eu posso ter vários DDOs apontando para vários registros da mesma tabela sem precisarem se importa para onde o buffer
está direcionado, se é que este está apontando para algum registro.
O DDO consegue cumprir esse papel por copiar todos os valores do registro para dentro de sua estrutura. À medida que o
usuário altera esses valores, o DDO mantém um registro das alterações. Ao receber a ordem de salvar o registro, o DDO
localiza o registro novamente no buffer e copia todos os dados alterados para o buffer e os salva no arquivo usando
comandos para gravação de dados via buffer.
Os problemas relacionados ao uso de buffer e DDOs começam a partir do momento que alguns programadores não
entendem esse fato, e também não compreendem que embora o buffer não afete a posição de um DDO, o DDO afeta a
posição de um buffer porque, no final das contas, o DDO usa o buffer para ler, criar, alterar e deletar registros. A seguinte
ilustração mostra quatro DDOs apontando para quatro registros diferentes da tabela X e a relação que eles têm com o
buffer dessa tabela.
18 Fascículos do Desenvolvedor – versão 1.6.1
Em lugares onde se usam DDOs, mesmo que seja apenas um DDO, deve-se sempre evitar usar o buffer ou valores nele, a
menos que realmente a intenção seja buscar valores que estão na tabela sem afetar os valores que estão no DDO. Também,
no caso de telas guiadas por DDOs, ou os chamados objetos visuais DEO, os controles que exibem dados da tabela
geralmente usam DDOs e tem os seus valores alterados por eles. Assim, nunca se usa a propriedade Value de um controle
DEO para alterar os valores do registro corrente apontado pelo DDO. Ao invés disso, use recursos do próprio DDO para esse
fim (ex Get_Field_Current_Value, Set_Field_Changed_Value, etc).
Por exemplo, nunca faça como no seguinte código.
Object CadPro_DD is a CadPro_DataDictionary
End_Object
Object CadPro_Desconto is a dbForm
Entry_Item CADPRO.DESCONTO
End_Object
Procedure AtualizarDesconto Number nDesconto
If (Value(CadPro_Desconto(Self)) = 0) Set Value of (CadPro_Desconto(Self)) to nDesconto
End_Procedure
Embora o controle CadPro_Desconto represente o campo CADPRO.DESCONTO para o DDO CadPro_DD nesse exemplo,
atribuir o valor ou buscar o valor a partir da propriedade Value desse controle significa passar por uma rota muito longa até
chegar ao lugar original da informação. Isso diminui a perspectiva de segurança da aplicação, já que podem ser inseridos
processo inesperado nesse caminho.
Seria também um grande engano usarmos algo semelhante ao exemplo seguinte.
Procedure AtualizarDesconto Number nDesconto
Move (CADPRO.VALOR – nDesconto) to nDesconto
// fonte continua ...
End_Procedure
Nesse exemplo, o erro está sendo o acesso direto ao buffer, que pode estar direcionado para outro registro, ou mesmo que
esteja no mesmo registro, pode não estar refletindo o valor correto devido alguma atualização no valor que ainda não foi
salva no buffer. Este é um erro mais grosseiro ao código que pode significar bugs sérios já que nem sempre algum problema
é prontamente observado.
O próximo exemplo demonstra qual é o acesso correto aos dados de um registro mantido em DDO.
Object CadPro_DD is a CadPro_DataDictionary
End_Object
Object CadPro_Desconto is a dbForm
Entry_Item CADPRO.DESCONTO
End_Object
Procedure AtualizarDesconto Number nDesconto
Buffer X - 10
DDO A - 20
DDO C - 12
DDO D - 30
DDO B - 100
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
19 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Number nValue
Get Field_Current_Value of (CadPro_DD(Self)) Field CADPRO.DESCONTO to nValue
If (nValue = 0) Set Field_Changed_Value of (CadPro_DD(Self)) to nDesconto
End_Procedure
Como você pode ver, o acesso e a alteração são feitos diretamente no DDO. Não se preocupe que o DDO se encarregará de
atualizar o valor do controle.
Em alguns treinamentos é dito que nunca se deve usar o buffer. Como você pode ver essa é uma afirmação enganosa. Mais
informação será preparada sobre esse tema, mas procure entender corretamente essas práticas seguras para que você possa
manipular tanto buffer quantos DDOs como uma ferramenta ao seu favor, não um pesadelo.
Desfrute Do Seu Trabalho
Esforce-se em seguir essas sugestões. Qualquer esforço nesse respeito beneficiará primariamente você como desenvolvedor.
Lembre-se que seu trabalho será verificado e visto por outros várias vezes. O legado que você deixa em uma base de código
pode ser duradouro. Esforce-se em ser lembrado como alguém inteligente, prático e organizado. Isso lhe poupará dores de
cabeça em ter que corrigir seu próprio código outras vezes, ou lhe poupará ainda a vergonha de não consegui explicar para
outro Desenvolvedor como o seu próprio código funciona.
Esteja determinado em transformar o seu trabalho em uma fonte de alegria.
Apêndice II
Ativação e Desativação de Janelas – O Método Sugerido
O mecanismo de Ativação e Desativação tem um longo histórico. Ele foi construído como parte da original Interface de
usuário orientada a objetos e foi submetido a muitas mudanças. Teve de ser estendido para suporta objetos modal e
modeless. Após isso foi expandido para trabalha com objetos orientados por dados (DEOs). Mais tarde, ele foi alterado para
suportar controles do Windows. Não surpreende que o resultado final seja uma variedade de formas de fazer as coisas
funcionarem e mais outros muitos modos de fazer as coisas não funcionarem. Mais cedo ou mais tarde, todo mundo acaba
esbarrando em alguma questão sobre ativar e desativar diálogos.
O propósito desse artigo e prover uma série de orientações para o processo de ativação e desativação. Você verá os meios
mais simples e consistentes para controlar esse processo. Você saberá quais mensagens você deve enviar e quais você deve
complementar. Você também verá quais você deve deixar sozinhas e isso pode te surpreender. Não serão abordados todos
os métodos de ativação e desativação e talvez você conheça outro procedimento. Porém, será explicado bastante a respeito
do processo a fim que você possa trabalhar mais eficientemente independente do método que você escolha.
Vamos dar a definição de alguns termos.
Um diálogo é um diálogo do Windows. Uma view é um diálogo modeless como um dbView ou um View. Um diálogo modal
é um diálogo modal como um dbModalPanel ou um ModalPanel. A classe determina se o diálogo é modal ou modeless.
Você não pode transformar uma view em modal e você não pode transformar um diálogo modal para modeless.
Um objeto DEO é um objeto orientado a dados que trabalha com base na estrutura de Dicionário de Dados / Objeto de
Entrada de Dados (DD/DEO). Um objeto DEO pode ser um container ou um controle. Exemplos de containers DEO são
dbView, dbModalPanel e dbContainer3D. Exemples de controles DEO são dbForm e cdbTextEdit.
Um objeto non-DEO é um objeto que não compreende a estrutura DD/DEO. Exemplos de containers non-DEO são View,
ModalPanel e Container3D. Exemplos de controles non-DEO são Button, Form e cTextEdit.
Um diálogo DEO é um diálogo que compreende a estrutura DD/DEO. Um diálogo DEO pode conter uma mistura de objetos
DEO e non-DEO. Um diálogo non-DEO pode conter apenas objetos non-DEO.
Ativação se refere um diálogo que não está que ainda não foi desenhado. Um diálogo está inativo quando não está sendo
exibido e ativo quando passa a ser exibido.
O objeto Foco é o objeto que possui o foco de entrada.
Mudança de Foco se refere a troca de foco entre objetos ativos. Isso pode ocorrer dentro de um diálogo ou através de
diálogo. Freqüentemente o termo ativar é usado para referir-se a ativação ou a mudança de foco. Nesse artigo, ativar refere-
se somente a ativação.
Desativação significa fechar o diálogo e torná-lo inativo.
Ativação
Do ponto de vista do usuário a ativação torna o diálogo visível e disponível para uso. De uma perspectiva técnica, a ativação
cria e exibe o objeto diálogo do Windows e todos os objetos filhos dentro do diálogo, coloca-os na árvore de foco do
DataFlex, e da o foco a um desses objetos.
A propriedade Activate_State determina se um objeto já está ativado, indicando se ele já esta na árvore de foco. A
propriedade Windows_Handle determina se um controle do Windows foi criado. Normalmente estas duas propriedades
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
21 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
devem mudar em conjunto. Se um objeto está na árvore de foco, Activate_State deverá ser true e Windows_Handle diferente
de zero. A propriedade Focus determina qual objeto tem o foco.
O processo de ativação é um pouco diferente entre views e diálogos modais. Enquanto muito do comportamento é similar
há algumas diferenças sérias. Começaremos com as views.
Ativação de Views A ativação da view pode ser representada com esse pseudocódigo. Esse código assume que a que a view ainda não está
ativa (essa é uma ativação e não uma mudança de foco).
Activate_View
Activate
Add_Focus
Page_Object (objeto adicionado a árvore de foco)
Activating
Page (Controle do Windows criado)
Broadcast Add_Focus (Para todos os filhos)
(Dá o foco para o primeiro objeto que possa receber foco)
Activate_View é a mensagem que você deve enviar para ativar a view. Na verdade, é a mensagem Activate que executa a
ativação. Ela é uma mensagem útil e importante e será discutida em breve. Add_Focus cria o controle do Windows e o
adiciona a árvore de foco. Ele faz isso por enviar Page_Object, o qual coloca o objeto na árvore de foco, chama o evento
Activating e chama Page, o qual cria o controle do Windows. Então, Add_Focus envia Add_Focus para todos os controles
filhos. Finalmente Activate moverá o foco para o primeiro objeto filho que o receba. Isso é feita enviando Activate para o
objeto (nesse caso activate é usado para mudança de foco).
Do ponto de vista de customizações de view, nós só estamos interessados em duas mensagens: Activate e Activating.
A Mensagem Activate Você nunca precisará enviar a mensagem Activate a fim de realizar a ativação. Porém, essa é uma mensagem muito útil para
complementos.
Se você deseja cancelar a ativação, complemente e não encaminhe (forward) a mensagem.
Se você deseja faze alguma mudança no diálogo antes da ativação, você pode fazê-la antes de encaminhar a mensagem por
que na verdade o processo não foi iniciado ainda.
Lembre-se que o método Activate é usado para ativação e mudança de foco. Se você desejar que suas alterações sejam
usadas pelas tarefas corretas, use Activate_State para determinar isso. Segue um exemple do como isso pode ser feito.
Procedure Activate Returns Integer
Boolean bActivate bCanActivate
Integer iRet
Get Activate_State to bActivate
// Se for usado para ativação
IF (not(bActivate)) Begin
// chama um método local para determinar se a ativação é permitida.
Get CanActivate to bCanActivate
IF (not(bCanActivate)) Begin
Procedure_Return 1
End
// Chama um método local para efetuar outras operações locais antes
// de começar
Send PreActivateDialog
Forward Get Msg_Activate to iRet
End
Else Begin // Se for usado para mudança de foco
Forward Get Msg_Activate to iRet
End
Procedure_Return iRet
End_Procedure
22 Fascículos do Desenvolvedor – versão 1.6.1
A função CanActivate e PreActivateDialog são exemplos e não existem – você teria de criá-las.
Agora nós temos um ponto muito importante. Uma vez que você encaminha Activate para o sistema de classes, você
ultrapassou uma linha sem volta e a janela será ativada. Se a ativação é interrompida a partir desse ponto, isso é considerado
um erro de programação e as coisas não vão ficar boas.
Você pode complementar após ter encaminhado a mensagem Activate, mas você só poderá fazer isso com views e não com
diálogos modais (será explicado em breve). Nesse ponto a view está ativada e um objeto dentro da view tem o foco. Você
pode alterar outros objetos por mudar propriedades ou até mesmo o foco enviando Activate para outro objeto dentro do
diálogo. Note que embora você nunca deva enviar Activate para realizar ativação, você pode enviá-lo para mudança de foco.
O Evento Activating Activating é chamado por cada objeto no diálogo em uma ordem do topo para baixo. Ele é chamado antes que o objeto do
Windows seja criado. Também é chamado antes que os controles filhos sejam criados (ele é chamado antes que Add_Focus
seja enviado para os objetos filhos). Portanto, nesse ponto você pode mudar propriedades, mudar os estilos do Windows e
fazer até mesmo alterações mais agressivas aos objetos filhos. Você não deve tentar fazer qualquer coisa que mude o foco,
desative objetos ou altera a arvore de foco existente. Você não pode usar essa mensagem para cancelar a ativação – ela não
foi criada para isso. Portanto, não retorno um valor de dentro desse evento.
Ativação de Diálogo Modal A ativação do diálogo modal é quase a mesma que a da view, mas há algumas diferenças chaves. Você ativa um diálogo
modal por enviar a mensagem Popup. Ela faz o seguinte.
Popup
Activate
Create_Dialog (Diálogo do Windows criado!)
---Um novo nível da interface do usuário é criado---
Add_Focus
Page_Object (objeto adicionado a árvore de foco)
Activating
Page (Controle do Windows criado)
Broadcast Add_Focus (Para todos os filhos)
(Dá o foco para o primeiro objeto que possa receber foco)
A principal diferença aqui é que Create_Dialog é chamada entre Activate e Add_Focus. Isso força o comportamento modal
por desabilitar os objetos origens e iniciar um novo nível de IU. Iniciar um novo nível de IU significa que o procedimento
Create_Dialog não está completo até que o diálogo modal esteja fechado. Isso muda completamente o comportamento em
relação ao complemento posterior na mensagem Activate. O que foi posto posteriormente não é executado até que o
diálogo feche. Sendo assim, não use complementos posteriores na mensagem Activate. Entretanto, você ainda pode usa
Activate para os mesmo pré-complementos usados na ativação de uma view.
Há outra diferença técnica. Por razões internas, o controle Windows do diálogo modal é criado em Create_Dialog antes que
Add_Focus seja chamado. Isso significa que o controle Windows já existe quando Activating é chamada e, portanto Page não
precisa criar esse controle. Normalmente isso não muda nada, mas se você precisar fazer alguma coisa com o controle
diálogo exterior antes de ele ser criado, você deve fazer isso no Activate. Somente o controle diálogo é criado
prematuramente – todos os filhos do diálogo modal são criados segundo o modelo normal.
Talvez você notou que nós não usamos Popup_Modal para ativação. Essa mensagem funciona mas não é necessária.
Popup_Modal é uma mensagem herdada que chama Popup.
Mudança de Foco Você nunca deve precisar usar Activate para ativar um diálogo. Você pode enviar Activate para mudar o foco. Isso é feito
com freqüência dentro de uma view ou de um diálogo modal quando se muda o foco de um objeto para outro. Ao mudar o
foco entre views, é recomendável que você trate isso como uma mudança de view. Primeiro ative a view e, se necessário,
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
23 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
envie Activate para um objeto dentro da view. Lembre-se que a mudança de foco ocorre quando o Activate_State da view
está apontado para true.
Desativação
Desativação é o processo de fechar um diálogo ativo. Da perspectiva do usuário a view está fechada quando ela não está
mais visível e utilizável. Internamente, o diálogo e todos os seus filhos são removidos da árvore de foco do DataFlex, todos
os objetos do Windows no diálogo são destruídos e o foco é movido para alguma outra parte da aplicação.
Diferente do processo de ativação onde views e diálogos modais se comportam de maneiras diferentes uns dos outros, o
processo de desativação para esses dois tipos de diálogo são iguais. Contudo, há algumas diferenças significantes entre os
diálogos DEO (dbView, dbModalPanel) e os non-DEO (View, ModalPanel).
Desativação e Diálogos DEO Começaremos com os diálogos DEO porque esses são usados mais freqüentemente.
Close_Panel
Exit_Function
Request_Cancel
Verify_Exit
Executa a mensagem Verify_Exit. Se retornar zero, continua.
Deactivate
Send Activate para outro diálogo (mudança de foco)
Release_Focus (Libera o foco)
Broadcast Release_Focus (Para todos os filhos)
Remove_Object (Remove o objeto da árvore de foco)
Deactivating
Page_Delete (Destrói os controles do Windows)
Como você pode ver, esse processo é um pouco complicado e na verdade ele é bem mais complicado. Portanto, nós
começaremos com a parte simples. Eis o que você precisa saber para controlar a desativação.
1. Envie Close_Panel para fechar um diálogo. Você pode enviá-lo para o diálogo ou qualquer controle dentro dele.
2. Crie a sua própria função “pode-fechar” que retorna diferente de zero para cancelar a desativação. Coloque o id de
sua mensagem na propriedade Verify_Exit_Msg do objeto diálogo.
3. Use o evento Deactivating para controlar qualquer lógica “está fechando”. Deactivating é enviado para cada objeto
no diálogo.
4. Não complemente ou envie qualquer uma das outras mensagens.
Se você seguir esses passos, você não terá de se preocupar com as complicações que serão descritas. A principal
complicação é que os objetos DEO tenta fazer coisas demais. Você talvez ache que todas essas mensagens (Close_Panel,
Exit_Function, Request_Cancel, etc) são todas definidas e manipuladas pelo objeto diálogo via delegação. Com objetos DEO
filhos não é assim. Cada classe DEO entende e manipula essas mensagens diretamente. Para piorar as coisas, esses objetos
podem enviar diferentes mensagens para fechar o diálogo (Close_Panel, Exit_Function ou Request_Cancel). Para criar mais
uma complicação, objetos non-DEO (como botões) não entendem essas mensagens e, portanto as delegam. Isso torna difícil
saber qual mensagem deve ser complementada. Dependendo de onde o foco está mensagens para fechar deferentes
estarão sendo enviadas para diferentes objetos. A boa noticia é que se você não complementar nenhuma dessas mensagens,
tudo isso irá funcionar. Enquanto você usar sua função pode-fechar no Verify_Exit_Msg do diálogo, você terá um
comportamento consistente. A mensagem Verify_Exit passa pelos DEOs de origem procurando por um Verify_Exit_Msg
diferente de zero. Se você colocar Verify_Exit_Msg no diálogo externo, todos os objetos o encontrarão e usarão a mesma
função pode-fechar.
Como exemplo, adicionar o seguinte código a uma dbView ou a um dbModalPanel apenas lhe permitiria fechar o diálogo
quando a data corrente tivesse com o segundo impar (uma técnica massa que deixa qualquer um louco).
Object oDbAwareModalDialog is dbModalPanel
Set Label to “Label...”
Set Size to 89 211
24 Fascículos do Desenvolvedor – versão 1.6.1
Set Border_Style to Border_Thick
Function CancelClose Returns Boolean
DateTime dDT
Integer iSec
Boolean bStopIt
// teste inútil que retorna true para parar a desativação
// se o valor corrente dos segundos for par.
Move (CurrentDateTime()) to DDT
Move (DateGetSecond(dDT)) to iSec
Move (Integer(iSec/2) = (iSec/2.0)) to bStopIt
Function_Return bStopIt
End_Function
Set Verify_Exit_Msg to (RefFunc(CancelClose))
Desativação e diálogos non-DEO A desativação de um diálogo não DEO é muito mais simples. Uma vez que não é permitido aninhar objetos DEO dentro de
containers non-DEO, nós sabemos que nenhum desses objetos são DEOs. Deactivation se parecerá com o seguinte.
Close_Panel
Deactivate
Send Activate para outro diálogo (mudança de foco)
Release_Focus (libera o foco)
Broadcast Release_Focus (Para todos os filhos)
Remove_Object (Remove o objeto da árvore de foco)
Deactivating
Page_Delete (Destrói os controles do Windows)
Assim como em diálogos DEO, você envia Close_Panel para desativar o objeto. Neste caso, enviar Close_Panel para qualquer
objeto no diálogo resultará que a mensagem seja delegada ao objeto diálogo. Se você deseja cancelar a desativação,
complemente Close_Panel e não encaminhe a mensagem.
Eis um exemplo de como cancelar a desativação de um diálogo non-DEO.
Object oDbAwareModalDialog is ModalPanel
Set Label to “Label...”
Set Size to 89 211
Set Border_Style to Border_Thick
Function CancelClose Returns Boolean
DateTime dDT
Integer iSec
Boolean bStopIt
// teste inútil que retorna true para parar a deseativaçao
// se o valor corrente dos segundos for par.
Move (CurrentDateTime()) to DDT
Move (DateGetSecond(dDT)) to iSec
Move (Integer(iSec/2) = (iSec/2.0)) to bStopIt
Function_Return bStopIt
End_Function
Procedure Close_Panel
Booleand bStop
Get CancelClose to bStop
If not bStop Begin
Forward Send Close_Panel
End
End_Procedure
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
25 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
O processo de desativação Uma vez que a desativação foi iniciada (que a mensagem Deactivate foi enviada) o processo é o mesmo para todos os
diálogos. Se a desativação já foi iniciada ela não pode ser interrompida e se for, é um erro de programação e a aplicação não
será estável.
A mensagem Deactivate funciona de dois modos. Ela é usada para encontrar o objeto da “área” externa para desativá-lo e
então ela é usada para desativar um objeto. Você tem de saber como enviar essa mensagem com os parâmetros corretos e
você tem que saber como complementá-la da maneira correta. Então esqueça! Não a envie nem a complemente.
Quando você fecha um diálogo você deseja ter certeza que há algum outro lugar para receber o foco. Com views,
dificilmente isso é um problema. Com diálogos modais, o sistema tentará dar o foco ao objeto que tinha o foco quando o
diálogo modal foi chamado. Se por alguma razão esse objeto não puder retomar o foco, isto é um erro de programação e o
programa não será estável.
Objetos filhos são desativados antes de o objeto origem ser desativado.
O evento Deactivating é enviado para cada objeto ativo no diálogo em uma ordem debaixo para cima. Você não pode usar
esse evento para cancelar a desativação. Quando a desativação é chamada, o objeto já foi removido da árvore de foco
(active_state é igual a zero) mas o controle do Windows ainda existe (Window_Handle é diferente de zero). Alem disso, todos
os objetos filhos já estão desativados (removidos da árvore de foco e os controle do Windows destruídos). O método
Page_Delete na verdade destrói os controles do Windows.
Se você complementar o código de Deactivating, será mais provável que você faça isso somente no objeto diálogo.
Trabalhando com Diálogo Modal Diálogos modais são modais por uma razão. Você deseja chamá-los em um momento em particular, e suspender o resto da
aplicação até que o processo deste diálogo modal o forneça uma informação que você precisa para continuar. Enquanto
você poderia usar algumas das mensagens que nós abordamos aqui tais como Popup e Deactivating para manipular pré- e
pós-processamento, isso geralmente não é a melhor estratégia. Veja o artigo Comunicação entre Views e Diálogos
(http://support.dataaccess.com/Forums/blog.php?b=30, em inglês) para uma discussão cabal deste tópico e um método
sugerido.
Como as coisas dão errado
Várias vezes foi mencionado que “coisas ruins” acontecem quando a ativação ou a desativação não são corretamente
concluídas. Há três coisas que devem ser corretamente sincronizadas para que tudo funcione. Essas são a árvore de foco, os
objetos do Windows e o nível de IU.
A árvore de foco é uma estrutura do DataFlex que controla como a navegação para o objeto seguinte e anterior se dará.
Objetos são adicionados e removidos dessa árvore de foco durante ativação e desativação. Objetos do Windows são objetos
que são controles que são criados durante ativação e destruídos durante a desativação. Quando um objeto é adicionado a
arvore de foco, um objeto do Windows deve ser criado e vice-versa. Quando a ativação ou a desativação são derrubadas
impropriamente, você pode acabar em uma condição onde um objeto está na árvore de foco, mas não existe nenhum
controle do Windows, ou o objeto não está na árvore de foco mas o controle do Windows existe. Você pode testar isso por
olhar Active_State, que reflete o status da árvore de foco, e Window_Handle que indica se o controle do Windows existe.
O nível da IU só é importante com diálogos modais. O nível da IU é o nível onde o loop de processamento da interface do
usuário DataFlex é executado. Normalmente há apenas um nível que é usado por todas as views. Quando um diálogo modal
é ativado, o método Create_Dialog cria um novo nível de IU e suspende o nível anterior. O nível de IU permanece suspenso
até que o novo nível seja finalizado, o que ocorre durante a desativação. Se alguma coisa der errado durante a desativação, o
nível de IU pode não finalizar. Quando isso acontece o diálogo pode até parecer estar fechado, mas você estará processando
eventos de IU no nível de IU errado. Você pode ver isso com o depurador. Pause o seu programa. Se o diálogo parece
fechado mas você ainda vê Create_Dialog no Call-Stack, você tem um problema para resolver. Nesse ponto há uma grande
chance que Active_State e Window_Handle não estejam mais sincronizados.
26 Fascículos do Desenvolvedor – versão 1.6.1
Em todos os casos citados, uma vez que não há sincronia entre Active_State e Window_Handle, não haverá um modo fácil de
recuperar a execução da aplicação. Mesmo que as coisas pareçam funcionar, nada está funcionando corretamente.
Normalmente você não precisa se preocupar com nada disso porque o processo de ativação e desativação do DataFlex se
encarrega disso. Se você está tendo algum problema, você está fazendo alguma coisa errada. Corrija e tudo ficará perfeito.
© 2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material você está automaticamente concordando com o termo de licença na página 3.
27 Fascículos do Desenvolvedor – Orientação Essencial em Práticas e Metodologias em Visual Dataflex
Escreva seu próprio futuro
Nós Queremos Te Ouvir
Nós apreciaremos qualquer feedback a respeito desse material. Sua opinião e suas sugestões são muito relevantes e pode
nos levar a construir conteúdos melhores. Sua participação será uma ajuda.
Envie seu feedback para [email protected].
Equipe Editora
Desenvolvido e Escrito Por Claudio M. Souza Junior
Revisado e Editado Por Wanderson Lúcio Bastos
Todo Desenvolvedor pode escolher entre cumprir tarefas ou ter
paixão pelo que faz, entre fazer o possível ou fazer o impossível, entre
escrever um código ou escrever seu próprio futuro.
Atualize suas habilidades como desenvolvedor por conhecer e aplicar
Recursos e Idéias providos pelo Desenvolvimento da Atual Sistemas.