gerenciando a abertura de views, - atual sistemas …culos do desenvolvedor –orientação...

29
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

Upload: vuongtuyen

Post on 09-Nov-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 2: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 3: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 4: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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...

Page 5: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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.

Page 6: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 7: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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

Page 8: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 9: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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"

Page 10: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 11: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 12: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 13: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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.

Page 14: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 15: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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

Page 16: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 17: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 18: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 19: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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.

Page 20: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 21: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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.

Page 22: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 23: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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

Page 24: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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,

Page 25: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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

Page 26: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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

Page 27: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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.

Page 28: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

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.

Page 29: Gerenciando a Abertura de Views, - Atual Sistemas …culos do Desenvolvedor –Orientação Essencial em Práticas e Metodologias em Visual Dataflex 3 // Procedure OnClick If (FORMOV.RECIBO

© 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.