No artigo de hoje vamos demonstrar em como criar um campo Combo em uma grid com FWBrowse.
Para criarmos um campo do tipo Combo em um FWBrwColumn numa FWBrowse, temos que utilizar o método SetOptions, então a lógica que vamos desenvolver será a seguinte:
- Iremos criar um campo com tamanho 1 na nossa FWTemporaryTable
- Então na montagem do nosso cabeçalho, no nosso array base, iremos ter uma posição com array com as opções do combo
- Nisso na criação das nossas colunas, iremos usar o método SetOptions
Abaixo o código fonte desenvolvido:
#Include "Totvs.ch"
/*/{Protheus.doc} User Function zTstCol
Função que demonstra como atualizar valores da grid usando FWBrowse (com campo combo)
@type Function
@author Atilio
@since 18/12/2022
@version version
/*/
User Function zTstCol()
Local aArea := GetArea()
//Fontes
Local cFontUti := "Tahoma"
Local oFontAno := TFont():New(cFontUti,,-38)
Local oFontSub := TFont():New(cFontUti,,-20)
Local oFontSubN := TFont():New(cFontUti,,-20,,.T.)
Local oFontBtn := TFont():New(cFontUti,,-14)
//Janela e componentes
Private oDlgGrp
Private oPanGrid
Private oGetGrid
Private aColunas := {}
Private cAliasTab := "TMPSBM"
//Tamanho da janela
Private aTamanho := MsAdvSize()
Private nJanLarg := aTamanho[5]
Private nJanAltu := aTamanho[6]
//Cria a temporária
oTempTable := FWTemporaryTable():New(cAliasTab)
//Adiciona no array das colunas as que serão incluidas (Nome do Campo, Tipo do Campo, Tamanho, Decimais)
aFields := {}
aAdd(aFields, {"XXCODIGO", "C", 6, 0})
aAdd(aFields, {"XXDESCRI", "C", 30, 0})
aAdd(aFields, {"XXQUANTI", "N", 9, 2})
aAdd(aFields, {"XXEMISSA", "D", 8, 0})
aAdd(aFields, {"XXOPCOES", "C", 1, 0})
aAdd(aFields, {"XXOBSERV", "C", 100, 0})
//Define as colunas usadas, adiciona indice e cria a temporaria no banco
oTempTable:SetFields( aFields )
oTempTable:AddIndex("1", {"XXCODIGO"} )
oTempTable:Create()
//Monta o cabecalho
fMontaHead()
//Montando os dados, eles devem ser montados antes de ser criado o FWBrowse
FWMsgRun(, {|oSay| fMontDados(oSay) }, "Processando", "Buscando grupos")
//Criando a janela
DEFINE MSDIALOG oDlgGrp TITLE "Dados" FROM 000, 000 TO nJanAltu, nJanLarg COLORS 0, 16777215 PIXEL
//Labels gerais
@ 004, 003 SAY "FAT" SIZE 200, 030 FONT oFontAno OF oDlgGrp COLORS RGB(149,179,215) PIXEL
@ 004, 050 SAY "Listagem Genérica de" SIZE 200, 030 FONT oFontSub OF oDlgGrp COLORS RGB(031,073,125) PIXEL
@ 014, 050 SAY "Dados Temporários" SIZE 200, 030 FONT oFontSubN OF oDlgGrp COLORS RGB(031,073,125) PIXEL
//Botões
@ 006, (nJanLarg/2-001)-(0052*01) BUTTON oBtnFech PROMPT "Fechar" SIZE 050, 018 OF oDlgGrp ACTION (oDlgGrp:End()) FONT oFontBtn PIXEL
//Dados
@ 024, 003 GROUP oGrpDad TO (nJanAltu/2-003), (nJanLarg/2-003) PROMPT "Browse" OF oDlgGrp COLOR 0, 16777215 PIXEL
oGrpDad:oFont := oFontBtn
oPanGrid := tPanel():New(033, 006, "", oDlgGrp, , , , RGB(000,000,000), RGB(254,254,254), (nJanLarg/2 - 13), (nJanAltu/2 - 45))
oGetGrid := FWBrowse():New()
oGetGrid:DisableFilter()
oGetGrid:DisableConfig()
oGetGrid:DisableReport()
oGetGrid:DisableSeek()
oGetGrid:DisableSaveConfig()
oGetGrid:SetFontBrowse(oFontBtn)
oGetGrid:SetAlias(cAliasTab)
oGetGrid:SetDataTable()
oGetGrid:SetEditCell(.T., {|| .T.})
oGetGrid:lHeaderClick := .F.
oGetGrid:AddLegend(cAliasTab + "->XXQUANTI == 0", "YELLOW", "Quantidade zerada")
oGetGrid:AddLegend(cAliasTab + "->XXQUANTI < 0", "RED", "Quantidade menor que zero")
oGetGrid:AddLegend(cAliasTab + "->XXQUANTI > 0", "GREEN", "Quantidade maior que zero")
oGetGrid:SetColumns(aColunas)
oGetGrid:SetOwner(oPanGrid)
oGetGrid:Activate()
ACTIVATE MsDialog oDlgGrp CENTERED
//Deleta a temporaria
oTempTable:Delete()
RestArea(aArea)
Return
Static Function fMontaHead()
Local nAtual
Local aHeadAux := {}
//Adicionando colunas
//[1] - Campo da Temporaria
//[2] - Titulo
//[3] - Tipo
//[4] - Tamanho
//[5] - Decimais
//[6] - Máscara
//[7] - Editável? .T. = sim, .F. = não
//[8] - Array com opções do combo
aAdd(aHeadAux, {"XXCODIGO", "Código", "C", 6, 0, "", .F., Nil })
aAdd(aHeadAux, {"XXDESCRI", "Descricao", "C", 30, 0, "", .F., Nil })
aAdd(aHeadAux, {"XXQUANTI", "Quantidade", "N", 9, 2, "@E 999,999.99", .T., Nil })
aAdd(aHeadAux, {"XXEMISSA", "Emissão", "D", 8, 0, "", .T., Nil })
aAdd(aHeadAux, {"XXOPCOES", "Opções", "C", 1, 0, "", .T., {"1=Sim", "2=Não", "3=Talvez", "A=Teste", "B=Teste 2"} })
aAdd(aHeadAux, {"XXOBSERV", "Observação", "C", 100, 0, "", .T., Nil })
//Percorrendo e criando as colunas
For nAtual := 1 To Len(aHeadAux)
oColumn := FWBrwColumn():New()
oColumn:SetData(&("{|| " + cAliasTab + "->" + aHeadAux[nAtual][1] +"}"))
oColumn:SetTitle(aHeadAux[nAtual][2])
oColumn:SetType(aHeadAux[nAtual][3])
oColumn:SetSize(aHeadAux[nAtual][4])
oColumn:SetDecimal(aHeadAux[nAtual][5])
oColumn:SetPicture(aHeadAux[nAtual][6])
//Se for ser possível ter o duplo clique
If aHeadAux[nAtual][7]
oColumn:SetEdit(.T.)
oColumn:SetReadVar(aHeadAux[nAtual][1])
//oColumn:SetValid({|| fSuaValid()})
EndIf
//Se tiver opções do combo
If ! Empty(aHeadAux[nAtual][8])
oColumn:SetOptions(aHeadAux[nAtual][8])
EndIf
aAdd(aColunas, oColumn)
Next
Return
Static Function fMontDados(oSay)
Local aArea := GetArea()
Local nAtual := 0
Local nTotal := 50
Local dDtRef := Date()
Local cCodAtu := "000000"
Local cDescri := ""
Local nQuanti := 0
Local dEmissa := sToD("")
//Faz um laço de repetição
For nAtual := 1 To nTotal
//Muda a mensagem na regua
nAtual++
oSay:SetText("Adicionando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
//Pega as variáveis que vão ser gravadas
cCodAtu := Soma1(cCodAtu)
cDescri := "[" + Time() + "] Teste - " + cCodAtu
nQuanti := Randomize(-5, 5)
dEmissa := DaySub(dDtRef, nAtual)
//Insere os dados na temporária
RecLock(cAliasTab, .T.)
(cAliasTab)->XXCODIGO := cCodAtu
(cAliasTab)->XXDESCRI := cDescri
(cAliasTab)->XXQUANTI := nQuanti
(cAliasTab)->XXEMISSA := dEmissa
(cAliasTab)->XXOPCOES := ""
(cAliasTab)->XXOBSERV := ""
(cAliasTab)->(MsUnlock())
Next
RestArea(aArea)
Return
E abaixo um print de como ficou o campo Combo:
Bom pessoal, por hoje é só.
Abraços e até a próxima.

Daniel Parabéns, eu tinha uma certa dificuldade de entender a famosa FWTemporaryTable e com este exemplo seu, entendi o conceito e criei um modo de estudo que é lendo o código e olhando a tela como resultado, eu aprendi muito a logica. obrigado
Bom dia Luis, tudo joia?
Opa, muito obrigado pelo comentário e feedback. É muita bondade e generosidade sua.
Um grande abraço.
Atilio, estou com uma dificuldade de configurar corretamente o F3 no FWBrwColumn, até achei como, mas fala que da erro ao confirmar o registro, oColumn:SetF3(aHeadAux[nAtual][8]) Pode me ajudar com um exemplo pegando o F3 da SB1?
Bom dia Lucas, tudo joia?
Há um tempo atrás, perguntaram algo parecido no nosso grupo do WhatsApp, segue a resposta que havia enviado na ocasião:
[…]
Eu tive que fazer isso uma vez, e foram dois pontos que alterei, o primeiro foi no SetReadVar para ter também o alias da temporária, e o segundo foi direto no atributo da xF3 apontando para a consulta padrão, então a coluna ficou dessa forma:
oColumn:SetEdit(.T.)
oColumn:SetReadVar(cAliasTab + “->” + aHeadAux[nAtual][1])
oColumn:xF3 := aHeadAux[nAtual][9]
No exemplo acima, lembre-se de no aHeadAux, adicionar uma nona posição que vai ser o código da consulta padrão na SXB.
[…]
Um grande abraço.
Deu muito certo assim, muito obrigado, se não for pedir muito, estou usando uma consulta padrão que retorna B1_COD e B1_DESC, o B1_COD retorna no campo que está o F3, mas a descrição não vai para o próximo campo, alguma dica que eu possa seguir quando é assim?
Bom dia Lucas, tudo joia?
Olha, teve um cenário em que perguntaram no nosso grupo do WhatsApp, que pode ser que te ajude, segue a resposta que publiquei lá um tempo atrás:
[…]
1. Essa validação, você adiciona no array aHeadAux, onde você pega e aciona uma função de validação, exemplo:
aAdd(aHeadAux, {“XX_CODPRO”, “Cod. Produto”, “C”, TamSX3(“B1_COD”)[1], 0, “”, .T., Nil , “SB1”, {|| ExistCPO(“SB1”) .And. fVldPro()}})
2. Para gatilhar uma informação de um campo para outro em uma FWBrowse, você pode usar na validação do campo, por exemplo, logo após criar a coluna, você usa o SetValid, exemplo:
oColuna:SetValid(aHeadAux[nAtual][10])
3. Ai na sua função estática, você atualiza a temporária nos outros campos que precisar (no caso a ExistCPO já vai deixar a outra tabela posicionada), exemplo:
Static Function fVldPro()
Local lContinua := .T.
//Atualiza o registro, marcando a flag que foi atualizado
RecLock(cAliasTab, .F.)
(cAliasTab)->XX_DESCRI := SB1->B1_DESC
(cAliasTab)->XX_UM := SB1->B1_UM
(cAliasTab)->(MsUnlock())
Return lContinua
[…]
Um grande abraço.