Fascculo I
Junho/2011
Esse documento uma obra intelectual de uso restrito da Atual Sistemas. Qualquer ato de leitura, alterao, cpia ou distribuio,
completa ou parcial deve ser feita somente sob autorizao expressa. A posse ou uso no autorizado desse documento, ou do seu
contedo completo ou parcial, constitui-se um uma violao direta dos direitos de intelectualidade e ser julgada conforme o rigor da lei.
ii
Fascculos do Desenvolvedor
Contedos
Trabalhando Com Janelas Visuais Deferred
12
15
20
Captulo 1
Modal Janelas modal so caracterizadas por no permitirem que o usurio selecione janelas que j foram abertas
anteriores a ela na mesma aplicao. Em Visual DataFlex ns usamos as classes ModalPanel e ReportPanel para
criar janelas modal, sendo a ultima classe usada apenas para relatrios.
Modeless Janelas modeless so usadas em casos quando no desejamos impedir que usurio faa outras tarefas
na aplicao enquanto essa janela est aberta. Em Visual DataFlex ns usamos as classes View e ReportView para
criar janelas modaless, sendo a ultima classe usada apenas para relatrios.
Dica
Fascculos 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 inicializao atrasada, nenhum de seus objetos filhos ser criado
at que voc chame o mtodo de disparo, no caso Activate para Views e ReportViews (Modeless) e Popup_Modal para
ModalPanels e ReportPanels (Modal). Isso significa que at que algumas dessas funes sejam chamadas, nenhuma
informao 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 dilogos modais envie Close_Panel para o componente segundo o nome dele.
Ateno: Ao passar um componente para deferred, no referencie objetos filhos de componentes modeless (ex:
ReportPanel, ModalPanel ...) usando o nome do componente no endereo. Por exemplo, caso voc tenha um
componente modeless chamado Consulta_SL que possui um objeto filho chamado Exata, no referencie esse objeto
com a seguinte sntese:
Send AlgumaCoisa to (Exata(Consulta_SL(Self)))
Nesses casos, no use o nome do componente visual. Ao invs 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 execuo 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))
Note que a constante que FOREXC_SOMENTE_LEITURA no est definida no arquivo FORBAX.VW, mas no FOREXC.VW j
que l que est o fonte a quem o parmetro se refere.
// ForExc.vw Cabealho.
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 "Excluso 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 funes a partir do objeto oRecursosGlobais. Esse objeto deve ser
sempre usado com a finalidade do compartilhamento de dados globais entre views. Quanto a funo DeletarParametro ela
funciona de maneira idntica a funo ConsultarParametro, com a nica diferena que ela apaga o parmetro quando o
encontra. Caso o parmetro seja encontrado o retorno de ambas as funes ser um OK_, caso contrrio ser um
INVALID_HANDLE_VALUE.
Um parmetro uma string. Como voc deve ter notado, foi criada uma constante que em tempo de compilao deixa de
existir e substituda a referncia da constante pelo seu valor. Sempre recorra ao uso de constantes para evitar erros de
programao difceis de detectar.
Use sempre como prefixo o nome do objeto que precisa do parmetro (no exemplo acima FORBAX) usando um underscore
(_) onde ficaria o espao e em seguida descrevendo a finalidade do parmetro separada por underscore onde houver
necessidade de espao. Tudo em maisculo.
Nomeie a constante que armazenar o valor de modo idntico ao valor e lembre-se que voc no pode ter dois parmetros
iguais no SPG. Ento a string deve ser nica. Voc pode descobrir se houve uma tentativa de criar um parmetro que j
existe por verificar se o retorno de InserirParametro INVALID_HANDLE_VALUE.
Lembre. O valor string de um parmetro deve atender as seguintes expectativas:
Estar armazenado em uma constante de compilao e ser referido por essa constante sempre.
A string deve ser maiscula e deve-se usar underscore (_) no lugar de espao.
Deve-se colocar o nome do mdulo que tornou o parmetro necessrio seguido por underscore.
Nota: Explicaes mais detalhadas e especficas podem ser encontradas nos comentrios do arquivo classes.pkg.
Fascculos do Desenvolvedor
constante
deve
ser
nomeada
segundo
seu
valor,
porm
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 excees.
No caso do problema acima, segue abaixo o modelo de como se resolveria a questo da atribuio e retorno do valor
usando-se um registro.
// ESTMOV.VW cabealho
Use vdfclasses.pkg
Use Acrecimo.dg
// Dentro
//
Procedure
Integer
Number
do EstMov...
OnClick
iResult
nVrAcrecimo
de Acrescimo...
Activating Returns Integer
iResult
nVrAcrecimo
Os exemplos acima mostram como usar o sistema de registros. claro que como voc o usar no se limitar somente a um
modelo como esse.
Para o uso de registro voc lidar com as funes CriarRegistro, AlterarRegistro, LerRegistro e RemoverRegistro. Todas
essas funes quando funcionam com xito retornam OK_ e quando falham retornam INVALID_HANDLE_VALUE. Sempre
verifique esse valor de retorno, pois caso qualquer uma das funes falhem, nenhum efeito ocorrer no sistema de registros.
Por exemplo, suponhamos que por descuido voc crie uma chave e no a remova em determinada situao. Ao tentar cri-la
novamente, se identificar que uma chave j existe e a funo CriarRegistro ir falhar. O que significa que o valor que voc
passou no ser armazenado porque uma chave idntica foi encontrada. Caso voc no 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 lgica.
Nota: Explicaes mais detalhadas e especficas podem ser encontradas nos comentrios do arquivo classes.pkg.
constante
deve
ser
nomeada
segundo
seu
valor,
porm
usando
um
prefixo
CON_
(Ex.
CON_FORBAX_MEU_REGISTRO).
Por exemplo, o procedimento mGravaLogOperacao que responsvel por exibir o dilogo LogOperacao costumava fornecer
dados para a grid que havia no dilogo. Esses dados eram fornecidos diretamente se usando um handle para ter acesso a
grid. Assim eram acrescidas informaes ao contedo da grid e verificada a quantidade que fora inserida. O exemplo parcial
abaixo demonstra isso.
// PREVENT.PKG
Fascculos 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 no so iniciados atrasados (em modo deferred). Uma vez que o LogOperacao
passou a ter a inicializao atrasado, se tornou necessrio um mtodo para o transporte dessas informaes que eram
reunidas antes da abertura do dilogo.
Como voc pode notar tambm, alm ser necessrio preencher a grid, tambm era necessrio informar o valor do Label de
um TextBox e o Visible_State de outro. Porm esse dois problemas podem ser facilmente resolvidos com um registro e um
parmetro para cada um respectivamente.
No caso da grid apenas um continer de dados resolveria o problema, e isso ilustrado na soluo 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
A funo CriarConteiner cria um continer com o identificador nico que foi passado para ela. Caso nenhum continer com
o
mesmo
identificador
tenha
sido
criado
tudo
corra
bem,
retornado
OK_;
caso
contrrio
retorna
INVALID_HANDLE_VALUE.
Um continer simplesmente um objeto cSetTabela, uma classe derivada da classe Set (um implemento de Array). A
funo Conteiner retorna um handle para que voc tenha acesso a esse objeto. Porm, evite o uso dessa funo e sempre
copie o valor retornado a partir dela para um handle, caso voc tenha que recorrer a funo Conteiner constantemente. Isso
porque toda vez que a funo Contener chamada, uma busca realizada para achar o continer segundo o identificador
informado. Caso nenhum continer seja encontrado, INVALID_HANDLE_VALUE retornado.
O mtodo propriedade ValorConteiner usado para se alterar/consultar os elementos do continer informado (usando
Get/Set respectivamente). Caso deseje alterar os elementos do continer por um handle, ter o mesmo efeito que o uso
desse mtodo.
Por fim, o mtodo RemoverConteiner usado para remover o continer do Gerenciador de Dados Mltiplos. Lembre-se
sempre de usar esse mtodo e deixe que o gerenciador se encarregue de destruir o continer com seus elementos. Nunca
envie Destroy para o handle de algum continer, porque a referncia permanecer no gerenciador.
Segue abaixo as alteraes que seriam necessrias no Dilogo 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"
Fascculos do Desenvolvedor
Define LOGOPERACAO_ABORTAR_VISIVEL for |CS"LOGOPERECAO_ABORTAR_VISIVEL"
Cd_Popup_Object LogOperacao is a ModalPanel
//...
//... Faz declarao 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 situao 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 numerao dos campos
da tabela. O procedimento CriarTabelaDinamica responsvel por reservar o slot e reunir os meta-dados referente ao
nome das colunas. A propriedade phTabela contm o manipulador do slot reservado. A funo Campo retorna o nmero
do campo conforme a string passada. Por fim, a funo CampoValorCorrente retorna o valor do campo informado.
Aps ser utilizado, o objeto deve ser destrudo o quanto antes. Durante a destruio, a classe se encarrega de liberar o slot e
fechar a tabela.
Nota
Captulo 2
Isso faz com que, nesse exemplo, os arquivos CAXPRE e FCAXPRE sejam abertos durante a criao do objeto Client_Area. O
que derruba a desempenho do sistema desde a inicializao quando no 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
Fascculos do Desenvolvedor
Nesse exemplo, o comando Open precisa estar presente no fonte para que seja possvel compilar a linha que faz referncia
ao CADPRO.DESCONTO_OK. Porm o fato de o comando Open estar no escopo global desse PKG faz com que esse
comando open seja executado durante a inicializao em algum ponto onde esse arquivo incluso no projeto. Se ns
estamos usando o Open apenas para garantir a compilao, 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 no ser aberta quando o PKG for incluso no projeto durante a inicializao do programa e ns
conseguiremos compilar esse arquivo sem nenhum problema com a referncia ao campo CADPRO.DESCONTO_OK.
Contudo, nesse caso, quando fValida_Desconto fosse chamada, alguma outra parte da aplicao deveria se responsabilizar
em j ter essas tabelas abertas. Se no possvel garantir que a funo fValida_Desconto ser chamada apenas com CADPRO
e ESTSAL abertos, ento 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 no estar no escopo global, isso s ocorrer quando a funo
fValida_Desconto for chamada. Essa deve ser a ultima alternativa para abrir a tabela. Geralmente, funes que acessam dados
de tabelas so chamadas a partir de Janelas que j abriram essas tabelas, o que torna o uso de Declare_DataFile mais
desejvel para um fonte mais compreensvel e seguro. Isso, porque se voc usar o Open dentro da funo, voc ter de uslo em cada funo que precise fazer uso do arquivo em questo (no nosso exemplo, o ESTSAL).
Resumindo: Use o comando Open somente quando Declare_DataFile no puder realmente resolver a situao.
Uma classe de Dicionrio 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 no for usada
pelo usurio durante a execuo. Dessa forma, nenhum recurso do sistema usado a menos que seja de fato necessrio.
11
Captulo 3
CADIMP.ID
CADIMP.CAMINHO
CADIMP.DESCRICAO
CADIMP.ESTACAO
to
to
to
to
iCampoID
iCampoCaminho
iCampoDescricao
iCampoEstacao
Como proceder em um caso como esse? Nos casos onde necessrio o uso de "Open as" dever se optar entre dois
mtodos de uso. O primeiro a reserva de Slots atravs do gerenciador de slots de oRecursosGlobais. E o outro por meio
do uso de objetos da classe cSlotTabelaDinamica.
CADIMP.ID
CADIMP.CAMINHO
CADIMP.DESCRICAO
CADIMP.ESTACAO
to
to
to
to
iCampoID
iCampoCaminho
iCampoDescricao
iCampoEstacao
Como voc pode notar a funo ReservaSlotLivre de oRecursosGlobais tem como retorno um handle para um slot livre
que a aplicao pode utilizar. Esse handle deve ser utilizado como manipulador da cpia que voc criou e deve ser liberado
13
14
Fascculos do Desenvolvedor
no final da tarefa. Para liberar esse manipulador no gerenciador ns usamos LiberarSlot de oRecursosGlobais. S chame
LiberarSlot aps a chamada do comando Close.
Conforme demonstrado nessa situao 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 numerao dos campos
da tabela. O procedimento CriarTabelaDinamica responsvel por reservar o slot e reunir os meta-dados referente ao
nome das colunas. A propriedade phTabela contm o manipulador do slot reservado. A funo Campo retorna o nmero
do campo conforme a string passada. Por fim, a funo CampoValorCorrente retorna o valor do campo informado.
Aps ser utilizado, o objeto deve ser destrudo o quanto antes. Durante a destruio, a classe se encarrega de liberar o slot e
fechar a tabela.
Ateno: No chame o comando Close para fechar um slot aberto por cSlotTabelaDinamica jamais.
Nota
Apndice I
No escreva longos procedimentos. Um procedimento no deve ter mais de dez ou doze linhas, deve ser escrito
para ser reutilizvel para mais de uma situao e deve evitar afetar valores globais.
2.
Todo procedimento deve ter uma finalidade clara. A finalidade de um procedimento no pode ser mesclada com a
finalidade dos procedimentos que vem antes ou depois dele. Um bom programa uma srie de procedimentos
limpos que no se sobrepe um ao outro.
3.
No use caractersticas muito complexas da linguagem. Se o seu trabalho no feito com simples declaraes de
variveis, chamadas de procedimentos, classes, declaraes de controle de fluxo e operadores aritmticos, alguma
coisa est errada. Quando voc usa os recursos bsicos da linguagem isso o faz pensar no que voc est
escrevendo. No complique o que pode ser simples.
4.
Nunca use recursos da linguagem que voc no tem certeza como se comportaro. Se voc costuma escrever
cdigo que voc no tem certeza como se comporta, melhor reavaliar em que rea voc quer trabalhar.
5.
Evite ao mximo usar Copiar & Colar, principalmente se voc um principiante. Assim voc usar o menor nmero
de arquivos possveis.
6.
Evite usar referncias a valores abstratos cuja origem pode ser desconhecida e o valor incerto (buffer, variveis
globais).
7.
De tempos em tempos tente entender como as bibliotecas que voc usa funcionam. Procure ver porque
determinados processos so de um modo especfico.
Em suma, essas recomendaes podem parecer genricas, mas tente v-las de um modo prtico ao cumprir com seu dever.
O fonte final deve estar asseado aparentando ordem e esmero, devidamente posicionados para melhor
compreenso.
Nunca poupe linhas que significaro cem linhas a menos no futuro e eliminaro possveis bugs, mas nunca escreva mais
linhas de cdigo 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 recomendaes tericas, segue abaixo algumas recomendaes prticas em VDF.
Sempre use notao matemticas em declaraes de controle de fluxo e condicionais ao invs de expresses verbosas
antigas do VDF. Apenas em timo caso use essas expresses verbosas (ex, constraint), j que elas dificultam a compreenso
do fonte.
16
Embora o VDF no possua um operado ternrio nativo, possvel criar uma expresso ternria usando a funo If.
Move (If((CADPRO.DESCONTO_OK = "S"), iDesconto, iValor)) to ESTMOV.DESCONTO
Como voc pode ver, a expresso abaixo compacta e bem clara para entender, poupando uma linha para a viso de um
programador que procura entender o fonte.
Outro caso quando estamos decidindo se atribumos verdadeiro ou falso a algum elemento. Nesses casos, no h
necessidade de se usar IF ... Else. Por exemplo, note o cdigo 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 desnecessrio usarmos duas linhas. Ns poderiamos declarar isso simplesmente assim.
Set Visible_State of oDesconto to (CADPRO.DESCONTO_OK = "S")
Evite operadores desnecessrios. Como ns sabemos, o VDF uma linguagem em si verbosa que possui certos operadores
como os parnteses, Begin e End que so desnecessrios em certas circunstncias. A menos que eles realmente contribuam
para um melhor entendimento do fonte, evite-os. Veja um caso de uso desnecessrio abaixo.
If (CADPRO.DESCONTO_OK = "S") Begin
Move iDesconto to ESTMOV.DESCONTO
End
Tenha uma linha de raciocnio clara sobre o seu propsito. Em alguns casos, programadores simplesmente complicam
demasiadamente o cdigo com linhas em excesso onde algumas nem sero executadas. Obviamente, para evitarmos que
isso acontea, precisamos primeiro escrever essas linhas por ns mesmo, nunca copiar e colar. Depois de escrever, tente ver
se no tem como dizer a sua lgica de um modo mais direto. Veja o exemplo abaixo.
Integer iRet
// No Final da Funo ...
Get YesNoCancelBox "Tem certeza mesmo?" "Ateno" 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 trs erros presentes no fonte. Primeiro, o programador usou um YesNoCancelBox para uma situao em
que um YesNoBox seria o suficiente, j que ele trata o resultado do Cancelar da mesma forma que ele trata o No. Segundo,
todos esses condicionais poderiam ser resumidos em uma nica linha. Terceiro, no necessrio um Else para chamar um
Function_Return ou um Procedure_Return quando a sentena condicional a ultima sentena do mtodo em questo.
Corrigindo esse cdigo, o formato mais simples e direto seria o seguinte.
= MBRYes) Begin
No crie validaes em campos de entrada de dados que impea os usurios de sair deles at digitar o dado correto.
Em tais procedimentos de validaes ao sair do campo, caso seja retornado o valor 1, o usurio impedido de sair do
campo. Isso um erro de layout crasso. No cometa esse erro.
Caso voc precise validar um dado quando o usurio sair de um campo, faa de um modo que no afete o fluxo de uso da
aplicao. Por exemplo, limpe o valor invlido do campo, altere algum label que sirva como status, coloque a cor de fundo
do campo para vermelho, porm nunca exiba uma caixa de dilogo como resposta e muito menos impea o usurio 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 explicao ser fornecida
aqui.
Todo arquivo de dados em VDF pode ser acessado por um canal chamado de buffer do arquivo. Quando ns acessamos os
dados de uma tabela usando apenas o nome da tabela e o nome da coluna (ex,Stop_Box CADPRO.MATRICULA), ns
estamos na realidade lendo o buffer desse arquivo de dados diretamente. Nas primeiras verses do VDF, essa tcnica era
satisfatria, entretanto quando necessrio ler e alterar vrios registros da mesma tabela em vrias telas simultaneamente, o
uso de um buffer que s pode direcionar um registro por vez para vrias tarefas simultneas pode ser desafiador. Isso
porque o buffer s direciona um registro por vez e ele um s para toda a aplicao.
A fim de suprir essa deficincia, foi provido o DD ou Dicionrio de Dados, que nada mais do que uma classe de objetos
que usam o buffer para armazenar os dados de um registro especfico. Assim, com o uso de objetos DD ou DDOs ns
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 funes do DDO para ler os dados da tabela, eu vou ter os valores do registro 20. Com tal forma de organizao
eu posso ter vrios DDOs apontando para vrios 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
usurio altera esses valores, o DDO mantm um registro das alteraes. 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 gravao de dados via buffer.
Os problemas relacionados ao uso de buffer e DDOs comeam a partir do momento que alguns programadores no
entendem esse fato, e tambm no compreendem que embora o buffer no afete a posio de um DDO, o DDO afeta a
posio de um buffer porque, no final das contas, o DDO usa o buffer para ler, criar, alterar e deletar registros. A seguinte
ilustrao mostra quatro DDOs apontando para quatro registros diferentes da tabela X e a relao que eles tm com o
buffer dessa tabela.
17
18
DDO A
- 20
DDO B
- 100
Buffer
X - 10
DDO C
- 12
DDO
D - 30
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 inteno seja buscar valores que esto na tabela sem afetar os valores que esto no DDO. Tambm,
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 invs disso, use recursos do prprio DDO para esse
fim (ex Get_Field_Current_Value, Set_Field_Changed_Value, etc).
Por exemplo, nunca faa como no seguinte cdigo.
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 informao. Isso diminui a perspectiva de segurana da aplicao, j que podem ser inseridos
processo inesperado nesse caminho.
Seria tambm 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 no estar refletindo o valor correto devido alguma atualizao no valor que ainda no foi
salva no buffer. Este um erro mais grosseiro ao cdigo que pode significar bugs srios j que nem sempre algum problema
prontamente observado.
O prximo 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
Como voc pode ver, o acesso e a alterao so feitos diretamente no DDO. No 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 afirmao enganosa. Mais
informao ser preparada sobre esse tema, mas procure entender corretamente essas prticas seguras para que voc possa
manipular tanto buffer quantos DDOs como uma ferramenta ao seu favor, no um pesadelo.
19
Apndice II
Ativao
Do ponto de vista do usurio a ativao torna o dilogo visvel e disponvel para uso. De uma perspectiva tcnica, a ativao
cria e exibe o objeto dilogo do Windows e todos os objetos filhos dentro do dilogo, 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
Ativao de Views
A ativao da view pode ser representada com esse pseudocdigo. Esse cdigo assume que a que a view ainda no est
ativa (essa uma ativao e no uma mudana 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
ativao. 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. Ento, 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 mudana de foco).
Do ponto de vista de customizaes de view, ns s estamos interessados em duas mensagens: Activate e Activating.
A Mensagem Activate
Voc nunca precisar enviar a mensagem Activate a fim de realizar a ativao. Porm, essa uma mensagem muito til para
complementos.
Se voc deseja cancelar a ativao, complemente e no encaminhe (forward) a mensagem.
Se voc deseja faze alguma mudana no dilogo antes da ativao, voc pode faz-la antes de encaminhar a mensagem por
que na verdade o processo no foi iniciado ainda.
Lembre-se que o mtodo Activate usado para ativao e mudana de foco. Se voc desejar que suas alteraes 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 ativao
IF (not(bActivate)) Begin
// chama um mtodo local para determinar se a ativao permitida.
Get CanActivate to bCanActivate
IF (not(bCanActivate)) Begin
Procedure_Return 1
End
// Chama um mtodo local para efetuar outras operaes locais antes
// de comear
Send PreActivateDialog
Forward Get Msg_Activate to iRet
End
Else Begin // Se for usado para mudana de foco
Forward Get Msg_Activate to iRet
End
Procedure_Return iRet
End_Procedure
2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material voc est automaticamente concordando com o termo de licena na pgina 3.
21
22
O Evento Activating
Activating chamado por cada objeto no dilogo em uma ordem do topo para baixo. Ele chamado antes que o objeto do
Windows seja criado. Tambm 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 alteraes mais agressivas aos objetos filhos. Voc no deve tentar fazer qualquer coisa que mude o foco,
desative objetos ou altera a arvore de foco existente. Voc no pode usar essa mensagem para cancelar a ativao ela no
foi criada para isso. Portanto, no retorno um valor de dentro desse evento.
A principal diferena aqui que Create_Dialog chamada entre Activate e Add_Focus. Isso fora o comportamento modal
por desabilitar os objetos origens e iniciar um novo nvel de IU. Iniciar um novo nvel de IU significa que o procedimento
Create_Dialog no est completo at que o dilogo modal esteja fechado. Isso muda completamente o comportamento em
relao ao complemento posterior na mensagem Activate. O que foi posto posteriormente no executado at que o
dilogo feche. Sendo assim, no use complementos posteriores na mensagem Activate. Entretanto, voc ainda pode usa
Activate para os mesmo pr-complementos usados na ativao de uma view.
H outra diferena tcnica. Por razes internas, o controle Windows do dilogo 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 no
precisa criar esse controle. Normalmente isso no muda nada, mas se voc precisar fazer alguma coisa com o controle
dilogo exterior antes de ele ser criado, voc deve fazer isso no Activate. Somente o controle dilogo criado
prematuramente todos os filhos do dilogo modal so criados segundo o modelo normal.
Talvez voc notou que ns no usamos Popup_Modal para ativao. Essa mensagem funciona mas no necessria.
Popup_Modal uma mensagem herdada que chama Popup.
Mudana de Foco
Voc nunca deve precisar usar Activate para ativar um dilogo. Voc pode enviar Activate para mudar o foco. Isso feito
com freqncia dentro de uma view ou de um dilogo modal quando se muda o foco de um objeto para outro. Ao mudar o
foco entre views, recomendvel que voc trate isso como uma mudana de view. Primeiro ative a view e, se necessrio,
Desativao
Desativao o processo de fechar um dilogo ativo. Da perspectiva do usurio a view est fechada quando ela no est
mais visvel e utilizvel. Internamente, o dilogo e todos os seus filhos so removidos da rvore de foco do DataFlex, todos
os objetos do Windows no dilogo so destrudos e o foco movido para alguma outra parte da aplicao.
Diferente do processo de ativao onde views e dilogos modais se comportam de maneiras diferentes uns dos outros, o
processo de desativao para esses dois tipos de dilogo so iguais. Contudo, h algumas diferenas significantes entre os
dilogos DEO (dbView, dbModalPanel) e os non-DEO (View, ModalPanel).
Como voc pode ver, esse processo um pouco complicado e na verdade ele bem mais complicado. Portanto, ns
comearemos com a parte simples. Eis o que voc precisa saber para controlar a desativao.
1.
Envie Close_Panel para fechar um dilogo. Voc pode envi-lo para o dilogo ou qualquer controle dentro dele.
2.
Crie a sua prpria funo pode-fechar que retorna diferente de zero para cancelar a desativao. Coloque o id de
sua mensagem na propriedade Verify_Exit_Msg do objeto dilogo.
3.
Use o evento Deactivating para controlar qualquer lgica est fechando. Deactivating enviado para cada objeto
no dilogo.
4.
Se voc seguir esses passos, voc no ter de se preocupar com as complicaes que sero descritas. A principal
complicao que os objetos DEO tenta fazer coisas demais. Voc talvez ache que todas essas mensagens (Close_Panel,
Exit_Function, Request_Cancel, etc) so todas definidas e manipuladas pelo objeto dilogo via delegao. Com objetos DEO
filhos no assim. Cada classe DEO entende e manipula essas mensagens diretamente. Para piorar as coisas, esses objetos
podem enviar diferentes mensagens para fechar o dilogo (Close_Panel, Exit_Function ou Request_Cancel). Para criar mais
uma complicao, objetos non-DEO (como botes) no entendem essas mensagens e, portanto as delegam. Isso torna difcil
saber qual mensagem deve ser complementada. Dependendo de onde o foco est mensagens para fechar deferentes
estaro sendo enviadas para diferentes objetos. A boa noticia que se voc no complementar nenhuma dessas mensagens,
tudo isso ir funcionar. Enquanto voc usar sua funo pode-fechar no Verify_Exit_Msg do dilogo, 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 dilogo externo, todos os objetos o encontraro e usaro a mesma
funo pode-fechar.
Como exemplo, adicionar o seguinte cdigo a uma dbView ou a um dbModalPanel apenas lhe permitiria fechar o dilogo
quando a data corrente tivesse com o segundo impar (uma tcnica massa que deixa qualquer um louco).
Object oDbAwareModalDialog is dbModalPanel
Set Label to Label...
Set Size to 89 211
2011 Atual Sistemas. Todos os direitos Reservados.
Ao fazer uso desse material voc est automaticamente concordando com o termo de licena na pgina 3.
23
24
Assim como em dilogos DEO, voc envia Close_Panel para desativar o objeto. Neste caso, enviar Close_Panel para qualquer
objeto no dilogo resultar que a mensagem seja delegada ao objeto dilogo. Se voc deseja cancelar a desativao,
complemente Close_Panel e no encaminhe a mensagem.
Eis um exemplo de como cancelar a desativao de um dilogo 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 intil que retorna true para parar a deseativaao
// 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
O processo de desativao
Uma vez que a desativao foi iniciada (que a mensagem Deactivate foi enviada) o processo o mesmo para todos os
dilogos. Se a desativao j foi iniciada ela no pode ser interrompida e se for, um erro de programao e a aplicao no
ser estvel.
A mensagem Deactivate funciona de dois modos. Ela usada para encontrar o objeto da rea externa para desativ-lo e
ento ela usada para desativar um objeto. Voc tem de saber como enviar essa mensagem com os parmetros corretos e
voc tem que saber como complement-la da maneira correta. Ento esquea! No a envie nem a complemente.
Quando voc fecha um dilogo voc deseja ter certeza que h algum outro lugar para receber o foco. Com views,
dificilmente isso um problema. Com dilogos modais, o sistema tentar dar o foco ao objeto que tinha o foco quando o
dilogo modal foi chamado. Se por alguma razo esse objeto no puder retomar o foco, isto um erro de programao e o
programa no ser estvel.
Objetos filhos so desativados antes de o objeto origem ser desativado.
O evento Deactivating enviado para cada objeto ativo no dilogo em uma ordem debaixo para cima. Voc no pode usar
esse evento para cancelar a desativao. Quando a desativao 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 esto desativados (removidos da rvore de foco e os controle do Windows destrudos). O mtodo
Page_Delete na verdade destri os controles do Windows.
Se voc complementar o cdigo de Deactivating, ser mais provvel que voc faa isso somente no objeto dilogo.
25
26
Ns Queremos Te Ouvir
Ns apreciaremos qualquer feedback a respeito desse material. Sua opinio e suas sugestes so muito relevantes e pode
nos levar a construir contedos melhores. Sua participao ser uma ajuda.
Envie seu feedback para forumdev@atualsistemas.net.
Equipe Editora
Desenvolvido e Escrito Por Claudio M. Souza Junior
Revisado e Editado Por Wanderson Lcio Bastos
27