Campo em Árvore
Este objeto encontra-se disponível nas gamas Advanced e Enterprise.
Com o objeto "Campo em Árvore" torna-se possível visualizar a informação organizada como se trata-se de ramos de uma árvore (treelist). Cada inicio de um ramo chama-se nó, e cada nó poderá ter diversos itens (linhas de informação) e/ou mais outros nós que passaram a ser sub-nós.
Pode-se fechar e abrir os nós que desejarmos para visualizar a informação organizada de diversas maneiras. Este objeto também permite ao se efetuar duplo clique num nó ou item, navegar para um determinado ecrã do software que esteja relacionado com a informação do nó ou item respetivo.
Este objeto á semelhança do objeto Grelha e Gráfico, também disponibiliza em Advanced a propriedade Expressão XBASE que poderá ser um programa mesmo em Advanced, para que se consiga de uma maneira mais personalizada definir o conteúdo (os seus nós e itens) que o objeto irá ter.
Acesso
Esta propriedade permite definir acessos de consulta, alteração e introdução para os utilizadores, relativamente ao objeto em questão.
Os acessos disponíveis são:
- Sem controlo de acessos
- Acesso privado para um utilizador
- Acesso X à tabela Y
- Acesso por tipo de perfil
Expressão TSQL
Nesta propriedade podemos escrever uma expressão em Tsql que irá retornar um cursor do servidor de dados, e será com base na informação desse cursor (campos e registos) que se irá construir a lista de nós e itens do objeto.
Por exemplo, podemos criar uma expressão que nos retorne todos os documentos de faturação ordenados por tipo de documento, dentro do tipo por cliente e dentro do cliente por número de documento.
Com esta informação podemos criar uma árvore com três nós: tipo de documento, cliente e nº de documento. No terceiro nó poderíamos depois disponibilizar como itens as linhas de cada documento de faturação.
Esta árvore com três nós, terá itens no terceiro nó, por isso vamos dizer que a árvore tem 4 NÍVEIS, 1º nível é o tipo de documento, 2º nível é o cliente, 3º nível é o numero do documento e por fim o 4º nível é as linhas do documento.
Podemos criar a seguinte Expressão TSQL:
Select ft.nmdoc,ft.no,ft.fno,fi.ref from ft (nolock) inner join fi (nolock) on ft.ftstamp=fi.ftstamp order by ft.nmdoc,ft.no,ft.fno
Devemos dar um nome a este cursor, por exemplo podemos chamar ao nosso cursor de "MyTree".
A partir daqui basta configurar a propriedade Nós & Itens (Campos) para que o objeto possa funcionar corretamente.
Esta expressão vai ser modificada à medida que vamos acrescentado novas funcionalidades ao objeto. Será feita referência a esta propriedade e expressão à medida que formos explicando novas funcionalidades nas outras propriedades.
Expressão XBASE
No nosso exemplo vamos utilizar esta propriedade porque a propriedade "Expressão TSQL" não aceita variáveis por isso não se pode filtrar segundo um critério que muda, no nosso caso, sempre que consultamos um cliente diferente. Então o que temos que fazer é criarmos o cursor para a construção do objeto segundo os nossos critérios, que será algo como o seguinte exemplo:
Local cTsql
m.cTsql = "select ft.ftstamp,ft.nmdoc,ft.fno,ft.etotal,fi.ref,fi.design,fi.qtt,fi.epv,fi.etiliquido,isnull(st.ststamp,'') as ststamp from ft (nolock) inner join fi (nolock) on ft.ftstamp=fi.ftstamp left join st (nolock) on fi.ref=st.ref where ft.no="+transform(cl.no)+" and ft.estab="+transform(cl.estab)+" and ft.ftano="+transform(year(date()))+" order by ft.nmdoc,ft.fno,fi.lordem"
u_sqlexec(m.ctsql,"MyTree")
Vamos analisar um pouco esta expressão, primeiro notamos que o campo do número de cliente "ft.no" já não está incluído e assim como a ligação á tabela CL "inner join cl (nolock) on ft.no=cl.no and ft.estab=cl.estab", isto porque agora nós vamos estar no ecrã de clientes e não é necessário incluir a informação do cliente na construção do objeto (anteriormente o nível 2) porque o cliente será aquele que estivermos a consultar nesse mesmo ecrã, temos sim que filtrar a informação pedida ao servidor para que retorne só os documentos de faturação daquele cliente que estamos a consultar, e já agora, os documentos apenas de um determinado ano, isso é feito incluindo variáveis na definição da expressão tsql, como seja:
"... ft.no=" + transform(cl.no) + " and ft.estab=" + transform(cl.estab) + " and ft.ftano=" + transform(year(date())) + "... "
Outra coisa diferente nesta expressão é a ligação á tabela ST em vez de "inner join st (nolock)" utilizamos agora "left join st (nolock)" isto é para que as linhas dos documentos de faturação que não tenham referência preenchida também sejam utilizadas na construção do nosso objeto, e como tal o campo "st.ststamp" anteriormente utilizado agora pode retornar o valor NULL (sem valor) nessa situação, por isso temos que precaver esta situação e escrever a expressão agora desse campo como "isnull(st.ststamp,'') as ststamp".
Outra coisa é a ultima ordenação "fi.lordem", aqui estamos a indicar que o último nível que será o nível dos itens, irá ser ordenado pela ordem atual da linha do documento, ou seja, pela ordem com que elas foram inseridas ou ordenadas no documento.
Também podemos reparar que temos mais campos, ft.etotal que é o total do documento, fi.design,fi.qtt,fi.epv e fi.etiliquido que são respetivamente a designação da referência, a quantidade da linha, o preço unitário e o total da linha, isto porque agora vamos querer a seguinte informação em cada nível:
- Nível 1 - Tipo de documento
- Nível 2 - Numero do documento + Total do documento
- Nível 3 - Referência + Designação + Quantidade + Preço unitário + Total da Linha
Para construir o objeto com esta informação, e assumindo que continuamos a dar o valor "MyTree" á propriedade "Nome do Cursor", deve-se definir uma expressão na propriedade "Nós & Itens (Campos)".
Nota: Esta funcionalidade está disponível nas três gamas: PHC Corporate CS, PHC Advanced CS e PHC Enterprise CS, caso o número de instalação já exista.
Se for uma instalação nova, a partir da versão 17, esta funcionalidade existe nas gamas PHC Advanced CS, apenas com o PHC On ativo, e na gama PHC Enterprise CS, com ou sem PHC On ativo.
Nome do Cursor
Nesta propriedade definimos o nome do cursor que controla este objeto.
Nós & Itens (Campos)
Uma vez definida a expressão responsável pela criação do cursor com os dados para preencher o nosso objeto, temos agora que definir como vamos criar a estrutura dos nós e itens no objeto, ou seja, definir que campos do nosso cursor iram ser usados para preencher cada um nos NÍVEIS respetivos. Tendo em conta o exemplo definido na expressão TSQL (cursor MyTree), vamos definir a seguinte expressão:
MyTree.nmdoc;MyTree.no;MyTree.fno;MyTree.ref
Cada um dos níveis é separado por ponto e virgula, assim definimos que o campo MyTree.nmdoc do nosso cursor será utilizado para construir o nível 1 (primeiro nó), o campo MyTree.no será utilizado para o nível 2 (segundo nó), o campo MyTree.fno para o nível 3 (terceiro nó) e finalmente o campo MyTree.ref será utilizado para o 4 e último nível (itens)
Se tivermos em conta a expressão Xbase, o valor desta propriedade deverá ser alterado para:
MyTree.NMDOC;Transform(MyTree.FNO)+" - "+Alltrim(Transform(MyTree.ETOTAL,m.m_eurpic));Alltrim(MyTree.REF)+" - "+Alltrim(MyTree.DESIGN)+" - "+Alltrim(Transform(MyTree.QTT,m.m_qttpic))+" - "+Alltrim(transform(MyTree.EPV,m.m_eurpic))+" - "+Alltrim(Transform(MyTree.ETILIQUIDO,m.m_eurpic))
Como podemos ver pela expressão acima, a propriedade "Nós & Itens (Campos)" aceita expressões (não programas) de XBASE, o que nos é muito útil pois nos dá mais flexibilidade para definir a informação que irá aparecer nos nós e itens do objeto.
Recalcula Valores
Esta propriedade indica se queremos que além da primeira vez em que o objeto é construído, se em determinadas situações como por exemplo nos painéis de informação ao se clicar nos botões "Atualizar" ou "Só esta página" ou então num ecrã da aplicação que tenha esta personalização e seja efetuado o refresh desse ecrã por exemplo ao navegar de registo em registo, se este objeto volta a recalcular o seu conteúdo, ou seja, se volta a reconstruir os seus nós e itens.
Esta propriedade é necessário quando queremos que o calculo do cursor para a construção do objeto inclua variáveis, caso contrário, deve-se colocar esta propriedade a "Não" para melhorar a performance da aplicação. Neste exemplo estamos a calcular um valor geral, ou seja, todos os documentos de todos os anos e de todos os clientes, sem utilizar nenhum tipo de filtro, tal informação será útil por exemplo num painel de informação em que queiramos disponibilizar informação global. Mas se desejarmos colocar este objeto numa personalização de ecrã, por exemplo no ecrã de clientes e que o objeto passe a mostrar os documentos de faturação apenas do cliente que estivermos a consultar no momento, então vamos ter que filtrar os valores que vamos retornar do servidor de dados, para isso deve-se utilizar a propriedade expressão xbase.
Tabela de Busca
Esta propriedade permite que se possa fazer navegação no duplo clique do objeto. Tendo em conta o exemplo definido na expressão TSQL (cursor MyTree), vamos supor que pretendemos que o utilizador ao executar um duplo clique num registo possa navegar para o ecrã de documentos de faturação já posicionado no documento correspondente ao registo. Para isso devemos ter em conta que a funcionalidade de navegação deste objeto é sempre feita via ligação das tabelas pelo seu campo único do tipo STAMP, ou seja como a tabela para onde desejamos ir é a tabela FT então temos que incluir no nosso cursor o campo FTSTAMP para que o nosso objeto consiga efetuar a navegação para o ecrã de documentos de faturação no duplo clique. Assim temos que alterar a propriedade "Expressão TSQL" para incluir o campo necessário:
Select ft.ftstamp,ft.nmdoc,ft.no,ft.fno,fi.ref from ft (nolock) inner join fi (nolock) on ft.ftstamp=fi.ftstamp order by ft.nmdoc,ft.no,ft.fno
Nesta propriedade temos que especificar que é para esta tabela de documentos de faturação (FT) que desejamos navegar, para isso basta escrever "FT" e com isto o objeto passa a fazer navegação por duplo clique.
Esta propriedade suporta várias tabelas, ou seja, o objeto pode navegar para diversos ecrãs consoante o nível onde o utilizador efetuou o duplo clique. No nível 1 do nosso exemplo temos o tipo de documento, nesse nível como não se define um documento em particular não nos interessa navegar, no 2º nível temos o cliente, aqui iremos querer navegar para o ecrã de clientes, no 3º nível temos o números de documentos, aqui como já temos um documento especifico vamos navegar para esse documento no ecrã de documentos de faturação e por fim no 4º nível temos os itens que são as referências introduzidas nas linhas do documento, vamos querer navegar para o ecrã de stocks. Assim na propriedade "Tabela de Busca" vamos especificar o seguinte:
;CL;FT;ST
Reparar que o primeiro nível ficou vazio, assim terá o comportamento desejado que é não efetuar nenhum tipo de navegação quando o utilizador efetuar duplo clique nesse nível que é o do tipo de documento. Mas ainda nos falta incluir no nosso cursor os campos de STAMP correspondentes á tabela CL e á tabela ST que serão respetivamente CLSTAMP e STSTAMP, assim vamos outra vez alterar a nossa "Expressão SQL" para:
Select ft.ftstamp,ft.nmdoc,ft.no,ft.fno,fi.ref,cl.clstamp,st.ststamp from ft (nolock) inner join fi (nolock) on ft.ftstamp=fi.ftstamp inner join cl (nolock) on ft.no=cl.no and ft.estab=cl.estab inner join st (nolock) on fi.ref=st.ref order by ft.nmdoc,ft.no,ft.fno
Reparar que a ordem dos campos da expressão tsql não influência a maneira como os níveis do objeto são construídos, pois essa definição cabe á propriedade "Nós & Itens (Campos)". E assim ficamos com a navegação deste objeto para 3 tipos de ecrãs diferentes consoante o nível onde o utilizador efetuar o duplo clique.
As semelhanças deste objeto na gama Advanced e Enterprise acabam aqui, a seguir serão descritos os eventos e as propriedades que estão apenas disponíveis em Enterprise.
Estão disponíveis os seguintes eventos neste objeto:
- Evento Após Atualizar - é executado quando o utilizador seleciona um determinado nó ou item do objeto.
- Evento Abrir - é executado quando o utilizador abre um nó do objeto.
- Evento Fechar - é executado quando o utilizador fecha um nó do objeto.
- Evento Clique Direito - é executado ao se fazer clique direito e se retornar .F. não mostra o menu do objeto que permite imprimir e abrir/fechar todos os nós.
- Evento Duplo Clique - é executado ao se fazer duplo clique, se retornar .F. não efetua a navegação que possa estar definido na propriedade "Tabela de Busca". Este evento permite construir navegações mais especificas que apenas utilizando a referida propriedade, pois aqui podemos verificar determinada condição e efetuar uma navegação personalizada, como seja por exemplo navegar para em ecrã em modo de introdução.
- Evento Criar - este evento só deve ser utilizado quando queremos mais flexibilidade na construção do nosso objeto, caso seja preenchido o objeto ignora as propriedade "Nós & Itens (Campos)", "Nós & Itens (Dados extra - caracter)", "Nós & Itens (Dados extra - numérico)", "Nós & Itens (Tipo)", "Nó de Raiz" e "Nós com campo lógico", ou seja, passa a ser completamente manual a construção dos nós e itens do objeto, será o código de utilizador desta propriedade que irá construir o objeto.
É claro que para se utilizar este evento é necessário uma compreensão do objeto que estamos a utilizar, explicação essa que sai fora do âmbito desta descrição, este objeto é um ActiveX de nome "ctTree" e é disponibilizado pela empresa DBI Technologies Inc cujo site é http://www.dbi-tech.com
Caso se utilize-se este evento e se queira que o objeto faça uma navegação automática baseada na propriedade "Tabela de Busca" temos que ir preenchendo um array interno que guarda o valor do STAMP respetivo de cada nível/item, para tal ao se adicionar um nó ou item a este objeto devemos também chamar a função do objeto "AddNodeVal1" enviando o valor do STAMP.
Além desta função existe outras que podem ser úteis neste trabalho de guardar os valores de STAMP, são elas:
- AddNodeVal1(m.xValor,m.nIndex), permite adicionar um valor ao array interno, o parâmetro m.nIndex é opcional e indica a posição do array onde queremos inserir o valor, se não for especificado o valor é adicionado á última posição.
- DelNodeVal1(m.nIndex), permite apagar um determinado valor de uma posição do array, o parâmetro m.nIndex é opcional e indica a posição do array onde queremos remover o valor, se não for especificado é apagado a última posição.
- ClearNodeVal1(), apaga todas as posições do array.
Alguns deste eventos disponibilizam propriedades que indicam o estado do objeto, por exemplo, no evento Duplo Clique temos as seguintes propriedades:
m.ObjRecebido.NodeNivel, indica o Nível do nó ou item
m.ObjRecebido.NodeIndex, indica o Número do nó ou item
m.ObjRecebido.NodeCargo, indica o valor dos Dados extra do nó ou item (Caracter)
m.ObjRecebido.NodeData, indica o valor dos Dados extra do nó ou item (Numéricos)
É claro, mais uma vez, para quem tiver conhecimento da estrutura interna do objeto "ctTree" disponibilizado pela DBI Technologies Inc, pode consultar ou modificar qualquer uma das suas propriedades através do apontador também aqui disponibilizado:
m.ObjRecebido.Objecto
A propriedade "Nós & Itens (Dados extra - caracter)" e a propriedade "Nós & Itens (Dados extra - numérico)" servem para guardar informação adicional que desejarmos, e que pode depois ser consultada num dos eventos acima descritos, as diferenças entre as duas propriedades é o tipo de dados, uma apresenta os dados sempre em formato caracter, e a outra em formato numérico, mesmo que os campos especificados nestas propriedades não o sejam do tipo requerido. Por exemplo na propriedade "Nós & Itens (Dados extra - caracter)" embora ela seja do tipo caracter podemos especificar um campo numérico como seja MyTree.FNO que a aplicação converte automaticamente o tipo de dados, depois mais tarde quando formos consultar o valor da propriedade "m.ObjRecebido.NodeCargo" temos que ter em mente que embora tenhamos especificado um campo numérico o valor retornado por esta propriedade será sempre do tipo caracter.
Caso não seja nada especificado na propriedade "Nós & Itens (Dados extra - caracter)" ela irá assumir o valor que cada nível mostrar no objeto, caso seja especificado apenas um campo, ela irá assumir o valor desse campo em todos os níveis, ou então podemos especificar os campos por níveis á semelhança da propriedade "Nós & Itens (Campos)"
Caso não seja nada especificado na propriedade "Nós & Itens (Dados extra - numérico)" ela irá assumir o valor zero em todos os níveis, caso seja especificado apenas um campo, ela irá assumir o valor desse campo em todos os níveis, ou então podemos especificar os campos por níveis á semelhança da propriedade "Nós & Itens (Campos)"
A propriedade "Nós & Itens (Tipo)" permite mostrar campos lógicos ou campos de escolha seletiva nos nós e itens do objeto. Caso se escolha campos lógicos então podemos definir se esses campos aparecem nos itens e nós do objeto ou apenas nos seus itens através da propriedade "Nós com campo lógico". Caso se defina campo de escolha seletiva e desejarmos incluir esses campos no primeiro nível, então temos que ativar a propriedade "Nó de Raiz" para que seja criado um nó extra no inicio, já que este objeto não suporta este tipo de campos no nível 1.
Esta propriedade "Nós & Itens (Tipo)" permite que o utilizador escolha determinados nós e itens e depois num evento de outro objeto, por exemplo no evento clique de um botão verificar que nós e itens foram escolhidos e realizar uma determinada acção baseada nessa escolha, por exemplo deste modo:
Local nx
Do Case
Case m.ObjRecebido.Janela.pageframe1.udcpage1.obj1.TipoItem = 1
Local nValor
m.nValor = 0
With m.ObjRecebido.Janela.pageframe1.udcpage1.obj1
For m.nx = 0 to .ListCount-1
if .CellCheck(m.nx,1) = 1 && 0 = Não selecionado, 1 = Selecionado, 2 = Desativo
m.nValor = m.nValor + 1
endif
Next
EndWith
Messagebox("Itens selecionados: "+Transform(m.nValor))
Case m.ObjRecebido.Janela.pageframe1.udcpage1.obj1.TipoItem = 2
Local cValor
m.cValor = ""
With m.ObjRecebido.Janela.pageframe1.udcpage1.obj1
For m.nx = 0 to .ListCount-1
if .NodeRadioSet(m.nx)
m.cValor = .NodeCargo(m.nx)
exit && Vamos sair porque neste exemplo só nos interessa mostrar algo ao utilizador
endif
Next
EndWith
messagebox("Valor do nó / item selecionado: " + m.cValor)
EndCase
As restantes propriedades deste tipo de objeto são (atenção que os eventos só existem em Enterprise):