Pragiva — Tecnologia na prática

ViewDef: a tela de uma rotina MVC

ViewDef não valida nem grava: só recebe o modelo pronto e diz onde cada componente aparece. O que acontece sem SetOwnerView e quando o box não é preciso.

Protheus Referência técnica 4 min de leitura

Assista o vídeo

Resposta rápida

O que monta
a ViewDef é a tela — só posiciona campos e grids que o ModelDef já definiu.
Sequência real
FWLoadModel, FWFormStruct(2,…), FWFormView():New(), SetModel, AddField/AddGrid, CreateHorizontalBox, SetOwnerView.
Decide alguma regra?
Não. Validação e gravação são do modelo; a view não valida nada.
Erro mais comum
componente sem SetOwnerView existe, mas não aparece na tela.
Box é obrigatório?
Não. Uma view com um único AddField pode não ter box nenhum.
Consulta rápida7 seções

A ViewDef é a terceira das três Static Function de nome obrigatório que o framework MVC procura num fonte — ao lado de MenuDef e ModelDef. Ela monta a interface, mas não guarda estado nenhum sobre o que é válido ou o que grava. Todo esse trabalho já foi feito por ModelDef antes de a ViewDef sequer ser chamada. O que ela faz é pegar o modelo pronto e responder uma pergunta diferente: onde, na tela, cada pedaço dele aparece.

Como o modelo chega até a view

O primeiro passo de uma ViewDef quase sempre é FWLoadModel(<fonte>). O corpo dessa função é curto — duas linhas — e explica por que o argumento precisa ser exatamente o nome do fonte, não o nome de uma rotina nem o ID do modelo: ela monta uma string e executa STATICCALL(<fonte>, ModelDef) com &(). É macro-execução pura. Se ModelDef não existir com esse nome exato no fonte informado, a chamada falha.

FWLoadView faz o mesmo truque, mais uma etapa que passa despercebida: antes de montar o STATICCALL, ela corta tudo antes do primeiro ponto do parâmetro recebido. É esse corte que faz ACTION "VIEWDEF.PGV010" funcionar — o framework recebe VIEWDEF.PGV010, descarta o VIEWDEF. e chama STATICCALL(PGV010, ViewDef, 'PGV010'). Repare no terceiro argumento: o nome do programa é passado como parâmetro para ViewDef. Quase nenhum fonte declara esse parâmetro na assinatura da própria Static Function ViewDef() — ele chega, mas ninguém o recebe, porque ADVPL não reclama de argumento sobrando.

A view lê o dicionário de novo

Dentro da ViewDef, a estrutura não é reaproveitada da ModelDef. Ela nasce de uma segunda chamada a FWFormStruct, agora com o primeiro parâmetro 2 em vez de 1, para o MESMO alias que o modelo já leu:

ADVPL / TLPP
Local oStruct := FWFormStruct(2, "Z50")

Modelo e tela leem o dicionário cada um para si — não existe conversão de uma estrutura na outra. É por isso que remover um campo da estrutura de view (com RemoveField) tira o campo da tela sem tirar do modelo: ele continua sendo gravado, só deixa de ser editável.

O terceiro parâmetro do AddField é quem une os dois lados

Depois de FWFormView():New() e SetModel(oModel), o componente entra com AddField:

ADVPL / TLPP
oView:AddField("VIEW_Z50", oStruct, "Z50MASTER")

A doc do próprio método é explícita sobre o primeiro parâmetro: ele representa o ID criado no modelo que esse campo de tela vai representar — ou um ID novo, e se for um ID novo é necessário passar o ID de vínculo no terceiro parâmetro. Ou seja: o ID da view e o ID do modelo podem ser diferentes, e é justamente o terceiro argumento — cLinkID — que faz a ligação entre os dois quando eles não coincidem. O quarto parâmetro, bValid, é documentado como uso interno exclusivo: não é um gancho para código de quem escreve o fonte.

O SetOwnerView que falta não reclama; o errado, sim

Os boxes têm a mesma assinatura nos dois eixos

ADVPL / TLPP
oView:CreateHorizontalBox("TELA", 100)

A assinatura completa é createHorizontalBox(cID, nPercHeight, cIDOwner, lFixPixel, cIDFolder, cIDSheet). O detalhe pouco notado é que createVerticalBox usa a MESMA assinatura, com o MESMO nome de segundo parâmetro: nPercHeight. O fonte do framework não troca o nome para “largura” quando o box é vertical — a nomenclatura oficial fica como está, mesmo divergindo do eixo real. lFixPixel é quem muda o significado do número: de percentual do espaço disponível para pixels fixos. CreateHorizontalBox é, disparado, o mais comum dos dois — a maioria das telas MVC se resolve empilhando faixas horizontais, uma embaixo da outra. CreateVerticalBox aparece quando a divisão precisa ser lado a lado, caso bem mais raro num cadastro comum.

Quando o box é dispensável

Uma view não precisa de nenhum box. Uma ViewDef de entidade única pode ficar em cinco linhas — FWLoadModel, FWFormView():New(), FWFormStruct(2,...), SetModel e um único AddField —, sem CreateHorizontalBox e sem SetOwnerView. Com um único AddField e nada mais, o próprio componente principal ocupa a tela inteira: não há disputa de espaço para o box resolver. O box só se torna necessário quando existe mais de um componente para dividir o espaço — um cabeçalho e um grid, por exemplo.

Abas e outros métodos com comportamento verificado

CreateFolder(cIdFolder, cIDOwner) seguido de addSheet cria a estrutura de abas. O comportamento tem duas faces, e as duas são reais: se o dono passado já for um campo de formulário com abas vindas do dicionário, a chamada não cria outra pasta por cima; se for um campo sem abas nenhuma, a chamada dispara uma exceção de classe. Vale testar o owner antes de assumir que CreateFolder sempre funciona.

Três métodos a mais valem citação pela assinatura verificada: addIncrementField(cIDView, cField) numera automaticamente um campo do grid sem envolver o modelo; addUserButton(cTitle, cResource, bBloco) acrescenta um botão — o exemplo do próprio fonte do framework escreve AddUserBotton, com erro de digitação, mas o método real que compila é addUserButton; e SetCloseOnOk(bBlock) recebe um bloco que tem a view como parâmetro e precisa devolver verdadeiro para fechar a janela ou falso para mantê-la aberta — a decisão de fechar sai da view, não do modelo.


Se a tela abre vazia e o modelo parece certo, fale com a gente.

Referências

  1. TDNtdn.totvs.com

    TDN — FWFormView

Continue por aqui

  1. Vídeo

    ProtheusReferência técnica

    MenuDef: as opções de uma rotina MVC

    MenuDef declara o aRotina do browse MVC: seis posições fixas, uma única ação repetida cinco vezes, e duas escalas de operação que não são a mesma coisa.

    5 min de leituraAssista o vídeo