No artigo de hoje vamos demonstrar em como congelar uma coluna usando a classe FWBrowse.
Alguns alunos, vira e mexe, acabam me perguntando em como fixar (congelar) uma coluna usando a classe FWBrowse.
O que acontece pessoal, é que a FWBrowse, logo após o Activate, ela tem um atributo chamado “oBrowse”, que esse cara herda os métodos e atributos da TGrid.
Pensando nisso, um dos atributos disponíveis é o nFreeze, onde você passa o número da coluna que quer congelar.
Então a lógica do nosso exemplo é:
- Criar normalmente nossa grid
- Após o método Activate, definimos o atributo nFreeze, apontando para ser a coluna de número 1 (nesse caso, código do produto)
- Por mudar o nFreeze, ele muda a altura padrão da linha, então em seguida usamos o método SetRowHeight
Abaixo um exemplo de como ficou nossa tela com a primeira coluna congelada:
E abaixo o código fonte criado do exemplo acima:
//Bibliotecas
#Include "Totvs.ch"
#Include "TopConn.ch"
/*/{Protheus.doc} User Function zBrwFreeze
Função que demonstra como congelar uma coluna em uma grid
@type Function
@author Atilio
@since 31/10/2023
@version version
/*/
User Function zBrwFreeze()
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, {"B1_COD", "C", TamSX3("B1_COD")[1], 0})
aAdd(aFields, {"B1_DESC", "C", TamSX3("B1_DESC")[1], 0})
aAdd(aFields, {"B1_TIPO", "C", TamSX3("B1_TIPO")[1], 0})
aAdd(aFields, {"B1_UM", "C", TamSX3("B1_UM")[1], 0})
aAdd(aFields, {"B1_GRUPO", "C", TamSX3("B1_GRUPO")[1], 0})
aAdd(aFields, {"BM_DESC", "C", TamSX3("BM_DESC")[1], 0})
aAdd(aFields, {"B1_LOCPAD", "C", TamSX3("B1_LOCPAD")[1], 0})
aAdd(aFields, {"B2_QATU", "N", TamSX3("B2_QATU")[1], TamSX3("B2_QATU")[2]})
aAdd(aFields, {"SAID_PREV", "N", TamSX3("B2_RESERVA")[1], TamSX3("B2_RESERVA")[2]})
aAdd(aFields, {"ENTR_PREV", "N", TamSX3("B2_SALPEDI")[1], TamSX3("B2_SALPEDI")[2]})
//Define as colunas usadas, adiciona indice e cria a temporaria no banco
oTempTable:SetFields( aFields )
oTempTable:AddIndex("1", {"B1_COD"} )
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:lHeaderClick := .F.
oGetGrid:SetColumns(aColunas)
oGetGrid:SetOwner(oPanGrid)
oGetGrid:Activate()
//Após o Activate, o atributo oBrowse é carregado herdando da TGrid
oGetGrid:oBrowse:nFreeze := 1
oGetGrid:oBrowse:SetRowHeight(30)
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
aAdd(aHeadAux, {"B1_COD", "Código", "C", TamSX3("B1_COD")[1], 0, ""})
aAdd(aHeadAux, {"B1_DESC", "Descrição", "C", TamSX3("B1_DESC")[1], 0, ""})
aAdd(aHeadAux, {"B1_TIPO", "Tipo", "C", TamSX3("B1_TIPO")[1], 0, ""})
aAdd(aHeadAux, {"B1_UM", "Unid.Med.", "C", TamSX3("B1_UM")[1], 0, ""})
aAdd(aHeadAux, {"B1_GRUPO", "Grupo", "C", TamSX3("B1_GRUPO")[1], 0, ""})
aAdd(aHeadAux, {"BM_DESC", "Descr.Grupo", "C", TamSX3("BM_DESC")[1], 0, ""})
aAdd(aHeadAux, {"B1_LOCPAD", "Armazém Padr.", "C", TamSX3("B1_LOCPAD")[1], 0, ""})
aAdd(aHeadAux, {"B2_QATU", "Saldo Atual", "N", TamSX3("B2_QATU")[1], TamSX3("B2_QATU")[2], PesqPict("SB2", "B2_QATU")})
aAdd(aHeadAux, {"SAID_PREV", "Saída Prevista", "N", TamSX3("B2_RESERVA")[1], TamSX3("B2_RESERVA")[2], PesqPict("SB2", "B2_RESERVA")})
aAdd(aHeadAux, {"ENTR_PREV", "Entrada Prevista", "N", TamSX3("B2_SALPEDI")[1], TamSX3("B2_SALPEDI")[2], PesqPict("SB2", "B2_SALPEDI")})
//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])
aAdd(aColunas, oColumn)
Next
Return
Static Function fMontDados(oSay)
Local aArea := GetArea()
Local nAtual := 0
Local nTotal := 0
Local cQryAux := ""
//Busca informação de todos os produtos
cQryAux := " SELECT " + CRLF
cQryAux += " B1_COD, " + CRLF
cQryAux += " B1_DESC, " + CRLF
cQryAux += " B1_TIPO, " + CRLF
cQryAux += " B1_UM, " + CRLF
cQryAux += " B1_GRUPO, " + CRLF
cQryAux += " BM_DESC, " + CRLF
cQryAux += " B1_LOCPAD, " + CRLF
cQryAux += " B2_QATU, " + CRLF
cQryAux += " B2_RESERVA + B2_QPEDVEN AS SAID_PREV, " + CRLF
cQryAux += " B2_SALPEDI AS ENTR_PREV " + CRLF
cQryAux += " FROM " + CRLF
cQryAux += " " + RetSQLName("SB1") + " SB1 " + CRLF
cQryAux += " INNER JOIN " + RetSQLName("SBM") + " SBM ON ( " + CRLF
cQryAux += " BM_FILIAL = '" + FWxFilial("SBM") + "' " + CRLF
cQryAux += " AND BM_GRUPO = B1_GRUPO " + CRLF
cQryAux += " AND SBM.D_E_L_E_T_ = ' ' " + CRLF
cQryAux += " ) " + CRLF
cQryAux += " INNER JOIN " + RetSQLName("SB2") + " SB2 ON ( " + CRLF
cQryAux += " B2_FILIAL = '" + FWxFilial("SB2") + "' " + CRLF
cQryAux += " AND B2_COD = B1_COD " + CRLF
cQryAux += " AND B2_LOCAL = B1_LOCPAD " + CRLF
cQryAux += " AND SB2.D_E_L_E_T_ = ' ' " + CRLF
cQryAux += " ) " + CRLF
cQryAux += " WHERE " + CRLF
cQryAux += " B1_FILIAL = '" + FWxFilial("SB1") + "' " + CRLF
cQryAux += " AND SB1.D_E_L_E_T_ = ' ' " + CRLF
TCQuery cQryAux New Alias "QRYPROD"
//Percorre os dados da query
While ! QRYPROD->(EoF())
//Muda a mensagem na regua
nAtual++
oSay:SetText("Adicionando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
//Insere os dados na temporária
RecLock(cAliasTab, .T.)
(cAliasTab)->B1_COD := QRYPROD->B1_COD
(cAliasTab)->B1_DESC := QRYPROD->B1_DESC
(cAliasTab)->B1_TIPO := QRYPROD->B1_TIPO
(cAliasTab)->B1_UM := QRYPROD->B1_UM
(cAliasTab)->B1_GRUPO := QRYPROD->B1_GRUPO
(cAliasTab)->BM_DESC := QRYPROD->BM_DESC
(cAliasTab)->B1_LOCPAD := QRYPROD->B1_LOCPAD
(cAliasTab)->B2_QATU := QRYPROD->B2_QATU
(cAliasTab)->SAID_PREV := QRYPROD->SAID_PREV
(cAliasTab)->ENTR_PREV := QRYPROD->ENTR_PREV
(cAliasTab)->(MsUnlock())
QRYPROD->(DbSkip())
EndDo
QRYPROD->(DbCloseArea())
RestArea(aArea)
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.

Olá, muito legal o seu artigo. É possível congelar a coluna do acols do produto da tabela SC6 (Pedido de Venda).
Bom dia Denis, tudo joia?
Opa, obrigado pelo feedback.
Então, quanto a tela padrão, eu desconheço se tem algum ponto de entrada ou parâmetro para fixar a coluna.
Mas ai você poderia criar uma tela customizada, com a coluna fixa, e que por trás, acione o MsExecAuto da Mata410, ao confirmar a operação.
Tenha uma ótima e abençoada quinta feira.
Um grande abraço.