Migrando do MSNewGetDados para FWBrowse e MVC

Hoje vou mostrar como migrar para outros tipos de grid já que a MsNewGetDados foi descontinuada.

Uma das dúvidas que mais me perguntam, é como fazer grids em telas customizadas no Protheus, já que a MsNewGetDados, a clássica grid, foi descontinuada.

Ah Daniel, mas e se eu continuar usando MsNewGetDados tem algum problema? Pois bem jovens, faz um bom tempo que a classe foi descontinuada pela TOTVS, provavelmente problema não terá, pois eles irão manter o legado, mas se algum dia acontecer algum bug na Lib, ou se forem atualizar alguma coisa do Core do Protheus e a MsNewGetDados apresente algum comportamento estranho, você não poderá abrir nenhum chamado e dificilmente conseguirá resolver, a não ser se migrar a rotina para uma classe mais nova.

Pois bem, pensando nisso, venho trazer para vocês 3 exemplos de como fazer grids sem a MsNewGetDados e a vantagem e desvantagem de cada uma delas.

Dois exemplos, eu utilizo o FWBrowse, que é facilmente colocado em qualquer dialog ou panel, porém ele não permite alteração, então esse seria um exemplo apenas para visualização de dados.

Já o terceiro exemplo, eu uso uma grid em MVC, nessa sendo possível a manipulação, conforme o link do TDN – https://centraldeatendimento.totvs.com/hc/pt-br/articles/360047143634-MP-ADVPL-Criando-uma-tela-MVC-s%C3%B3-com-GRID . Nesse exemplo, criei uma temporária, e chamei a tela em seguida, porém você pode usar tabelas padrões e até interceptar o commit com FWModelEvent ( https://tdn.totvs.com/pages/viewpage.action?pageId=269552294 ).

FWBrowse com FWTemporaryTable (Grid em Dialog com Tabela Temporária):

FWBrowse com FWTemporaryTable

#Include "Totvs.ch"
 
/*/{Protheus.doc} User Function zGrid
Visualizacao de Grupos de Produtos com FWBrowse e FWTemporaryTable
@type  Function
@author Atilio
@since  14/06/2020
@version version
/*/
 
User Function zGrid()
    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", TamSX3('BM_GRUPO')[01],   0})
    aAdd(aFields, {"XXDESCRI", "C", TamSX3('BM_DESC')[01],    0})
    aAdd(aFields, {"XXSTATUS", "C", TamSX3('BM_STATUS')[01],  0})
    aAdd(aFields, {"XXPROORI", "C", TamSX3('BM_PROORI')[01],  0})
    aAdd(aFields, {"XXTOTALP", "N", 18,                       0})
    aAdd(aFields, {"XXSBMREC", "N", 18,                       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 "Grupos de Produtos" 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 de"              SIZE 200, 030 FONT oFontSub  OF oDlgGrp COLORS RGB(031,073,125) PIXEL
        @ 014, 050 SAY "Grupos de Produtos"       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
        @ 006, (nJanLarg/2-001)-(0052*02) BUTTON oBtnLege  PROMPT "Ver Grupo"     SIZE 050, 018 OF oDlgGrp ACTION (fGrupo()) PIXEL
 
        //Dados
        @ 024, 003 GROUP oGrpDad TO (nJanAltu/2-003), (nJanLarg/2-003) PROMPT "Grupos (Para ver a legenda basta clicar duas vezes em alguma bolinha): " 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:SetInsert(.F.)
            oGetGrid:SetDelete(.F., { || .F. })
            oGetGrid:lHeaderClick := .F.
            oGetGrid:AddLegend(cAliasTab + "->XXPROORI == '0'", "RED",    "Nao Original")
            oGetGrid:AddLegend(cAliasTab + "->XXPROORI == '1'", "GREEN",  "Original")
            oGetGrid:AddLegend("Empty(" + cAliasTab + "->XXPROORI)", "BLACK",  "Sem Classificacao")
            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
    aAdd(aHeadAux, {"XXCODIGO", "Código",            "C", TamSX3('BM_GRUPO')[01],   0, ""})
    aAdd(aHeadAux, {"XXDESCRI", "Descricao",         "C", TamSX3('BM_DESC')[01],    0, ""})
    aAdd(aHeadAux, {"XXSTATUS", "Status Grupo",      "C", TamSX3('BM_STATUS')[01],  0, ""})
    aAdd(aHeadAux, {"XXPROORI", "Procedencia",       "C", TamSX3('BM_PROORI')[01],  0, ""})
    aAdd(aHeadAux, {"XXTOTALP", "Total de Produtos", "N", 18,                       0, "@E 999,999,999,999,999,999"})
    aAdd(aHeadAux, {"XXSBMREC", "SBM RecNo",         "N", 18,                       0, "@E 999,999,999,999,999,999"})
 
    //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 cQry  := ""
    Local nAtual := 0
    Local nTotal := 0
 
    //Zera a grid
    aColsGrid := {}
     
    //Montando a query
    oSay:SetText("Montando a consulta")
    cQry := " SELECT "                                                  + CRLF
    cQry += "     BM_GRUPO, "                                           + CRLF
    cQry += "     BM_DESC, "                                            + CRLF
    cQry += "     BM_STATUS, "                                          + CRLF
    cQry += "     BM_PROORI, "                                          + CRLF
    cQry += "     ( "                                                   + CRLF
    cQry += "         SELECT "                                          + CRLF
    cQry += "             COUNT(*) "                                    + CRLF
    cQry += "         FROM "                                            + CRLF
    cQry += "             " + RetSQLName('SB1') + " SB1 "               + CRLF
    cQry += "         WHERE "                                           + CRLF
    cQry += "             B1_FILIAL = '" + FWxFilial('SB1') + "' "      + CRLF
    cQry += "             AND B1_GRUPO = BM_GRUPO "                     + CRLF
    cQry += "             AND B1_MSBLQL != '1' "                        + CRLF
    cQry += "             AND SB1.D_E_L_E_T_ = ' ' "                    + CRLF
    cQry += "     ) AS TOT_PROD, "                                      + CRLF
    cQry += "     SBM.R_E_C_N_O_ AS SBMREC "                            + CRLF
    cQry += " FROM "                                                    + CRLF
    cQry += "     " + RetSQLName('SBM') + " SBM "                       + CRLF
    cQry += " WHERE "                                                   + CRLF
    cQry += "     BM_FILIAL = '" + FWxFilial('SBM') + "' "              + CRLF
    cQry += "     AND SBM.D_E_L_E_T_ = ' ' "                            + CRLF
    cQry += " ORDER BY "                                                + CRLF
    cQry += "     BM_GRUPO "                                            + CRLF
 
    //Executando a query
    oSay:SetText("Executando a consulta")
    PLSQuery(cQry, "QRY_SBM")
 
    //Se houve dados
    If ! QRY_SBM->(EoF())
        //Pegando o total de registros
        DbSelectArea("QRY_SBM")
        Count To nTotal
        QRY_SBM->(DbGoTop())
 
        //Enquanto houver dados
        While ! QRY_SBM->(EoF())
 
            //Muda a mensagem na regua
            nAtual++
            oSay:SetText("Adicionando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
 
            RecLock(cAliasTab, .T.)
                (cAliasTab)->XXCODIGO := QRY_SBM->BM_GRUPO
                (cAliasTab)->XXDESCRI := QRY_SBM->BM_DESC
                (cAliasTab)->XXSTATUS := QRY_SBM->BM_STATUS
                (cAliasTab)->XXPROORI := QRY_SBM->BM_PROORI
                (cAliasTab)->XXTOTALP := QRY_SBM->TOT_PROD
                (cAliasTab)->XXSBMREC := QRY_SBM->SBMREC
            (cAliasTab)->(MsUnlock())
 
            QRY_SBM->(DbSkip())
        EndDo
 
    Else
        MsgStop("Nao foi encontrado registros!", "Atencao")
 
        RecLock(cAliasTab, .T.)
            (cAliasTab)->XXCODIGO := ""
            (cAliasTab)->XXDESCRI := ""
            (cAliasTab)->XXSTATUS := ""
            (cAliasTab)->XXPROORI := ""
            (cAliasTab)->XXTOTALP := 0
            (cAliasTab)->XXSBMREC := 0
        (cAliasTab)->(MsUnlock())
    EndIf
    QRY_SBM->(DbCloseArea())
    (cAliasTab)->(DbGoTop())
 
    RestArea(aArea)
Return
 
Static Function fGrupo()
    MsgInfo("Estou no grupo: " + (cAliasTab)->XXCODIGO, "Atencao")
Return

FWBrowse com Array (Grid em Dialog com Array Multidimensional):

FWBrowse com Array

#Include "Totvs.ch"
 
/*/{Protheus.doc} User Function zGrid
Visualizacao de Grupos de Produtos com FWBrowse e Array
@type  Function
@author Atilio
@since  14/06/2020
@version version
/*/
 
User Function zGrid()
    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 aHeaderGrid := {}
    Private aColsGrid := {}
    //Tamanho da janela
    Private    aTamanho := MsAdvSize()
    Private    nJanLarg := aTamanho[5]
    Private    nJanAltu := aTamanho[6]
 
    //Monta o cabecalho
    fMontaHead()
 
    //Criando a janela
    DEFINE MSDIALOG oDlgGrp TITLE "Grupos de Produtos" 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 de"              SIZE 200, 030 FONT oFontSub  OF oDlgGrp COLORS RGB(031,073,125) PIXEL
        @ 014, 050 SAY "Grupos de Produtos"       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
        @ 006, (nJanLarg/2-001)-(0052*02) BUTTON oBtnLege  PROMPT "Ver Grupo"     SIZE 050, 018 OF oDlgGrp ACTION (fGrupo()) PIXEL
 
        //Dados
        @ 024, 003 GROUP oGrpDad TO (nJanAltu/2-003), (nJanLarg/2-003) PROMPT "Grupos (Para ver a legenda basta clicar duas vezes em alguma bolinha): " 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:SetDataArray()
            oGetGrid:lHeaderClick :=.f.
            oGetGrid:AddLegend("oGetGrid:oData:aArray[oGetGrid:At(), 4] == '0'", "RED",    "Nao Original")
            oGetGrid:AddLegend("oGetGrid:oData:aArray[oGetGrid:At(), 4] == '1'", "GREEN",  "Original")
            oGetGrid:AddLegend("Empty(oGetGrid:oData:aArray[oGetGrid:At(), 4])", "BLACK",  "Sem Classificacao")
            oGetGrid:SetColumns(aHeaderGrid)
            oGetGrid:SetArray(aColsGrid)
            oGetGrid:SetOwner(oPanGrid)
            oGetGrid:Activate()
 
        FWMsgRun(, {|oSay| fMontDados(oSay) }, "Processando", "Buscando grupos")
    ACTIVATE MsDialog oDlgGrp CENTERED
 
    RestArea(aArea)
Return
 
Static Function fMontaHead()
    Local nAtual
    Local aHeadAux := {}
 
    //Adicionando colunas
    //[1] - Titulo
    //[2] - Tipo
    //[3] - Tamanho
    //[4] - Decimais
    //[5] - Máscara
    aAdd(aHeadAux, {"Código",            "C", TamSX3('BM_GRUPO')[01],   0, ""})
    aAdd(aHeadAux, {"Descricao",         "C", TamSX3('BM_DESC')[01],    0, ""})
    aAdd(aHeadAux, {"Status Grupo",      "C", TamSX3('BM_STATUS')[01],  0, ""})
    aAdd(aHeadAux, {"Procedencia",       "C", TamSX3('BM_PROORI')[01],  0, ""})
    aAdd(aHeadAux, {"Total de Produtos", "N", 18,                       0, "@E 999,999,999,999,999,999"})
    aAdd(aHeadAux, {"SBM RecNo",         "N", 18,                       0, "@E 999,999,999,999,999,999"})
 
    //Percorrendo e criando as colunas
    For nAtual := 1 To Len(aHeadAux)
        aAdd(aHeaderGrid, FWBrwColumn():New())
        aHeaderGrid[nAtual]:SetData(&("{||oGetGrid:oData:aArray[oGetGrid:At(),"+Str(nAtual)+"]}"))
        aHeaderGrid[nAtual]:SetTitle( aHeadAux[nAtual][1] )
        aHeaderGrid[nAtual]:SetType(aHeadAux[nAtual][2] )
        aHeaderGrid[nAtual]:SetSize( aHeadAux[nAtual][3] )
        aHeaderGrid[nAtual]:SetDecimal( aHeadAux[nAtual][4] )
        aHeaderGrid[nAtual]:SetPicture( aHeadAux[nAtual][5] )
    Next
Return
 
Static Function fMontDados(oSay)
    Local aArea := GetArea()
    Local cQry  := ""
    Local nAtual := 0
    Local nTotal := 0
 
    //Zera a grid
    aColsGrid := {}
     
    //Montando a query
    oSay:SetText("Montando a consulta")
    cQry := " SELECT "                                                  + CRLF
    cQry += "     BM_GRUPO, "                                           + CRLF
    cQry += "     BM_DESC, "                                            + CRLF
    cQry += "     BM_STATUS, "                                          + CRLF
    cQry += "     BM_PROORI, "                                          + CRLF
    cQry += "     ( "                                                   + CRLF
    cQry += "         SELECT "                                          + CRLF
    cQry += "             COUNT(*) "                                    + CRLF
    cQry += "         FROM "                                            + CRLF
    cQry += "             " + RetSQLName('SB1') + " SB1 "               + CRLF
    cQry += "         WHERE "                                           + CRLF
    cQry += "             B1_FILIAL = '" + FWxFilial('SB1') + "' "      + CRLF
    cQry += "             AND B1_GRUPO = BM_GRUPO "                     + CRLF
    cQry += "             AND B1_MSBLQL != '1' "                        + CRLF
    cQry += "             AND SB1.D_E_L_E_T_ = ' ' "                    + CRLF
    cQry += "     ) AS TOT_PROD, "                                      + CRLF
    cQry += "     SBM.R_E_C_N_O_ AS SBMREC "                            + CRLF
    cQry += " FROM "                                                    + CRLF
    cQry += "     " + RetSQLName('SBM') + " SBM "                       + CRLF
    cQry += " WHERE "                                                   + CRLF
    cQry += "     BM_FILIAL = '" + FWxFilial('SBM') + "' "              + CRLF
    cQry += "     AND SBM.D_E_L_E_T_ = ' ' "                            + CRLF
    cQry += " ORDER BY "                                                + CRLF
    cQry += "     BM_GRUPO "                                            + CRLF
 
    //Executando a query
    oSay:SetText("Executando a consulta")
    PLSQuery(cQry, "QRY_SBM")
 
    //Se houve dados
    If ! QRY_SBM->(EoF())
        //Pegando o total de registros
        DbSelectArea("QRY_SBM")
        Count To nTotal
        QRY_SBM->(DbGoTop())
 
        //Enquanto houver dados
        While ! QRY_SBM->(EoF())
 
            //Muda a mensagem na regua
            nAtual++
            oSay:SetText("Adicionando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
 
            aAdd(aColsGrid, {;
                QRY_SBM->BM_GRUPO,;
                QRY_SBM->BM_DESC,;
                QRY_SBM->BM_STATUS,;
                QRY_SBM->BM_PROORI,;
                QRY_SBM->TOT_PROD,;
                QRY_SBM->SBMREC,;
                .F.;
            })
 
            QRY_SBM->(DbSkip())
        EndDo
 
    Else
        MsgStop("Nao foi encontrado registros!", "Atencao")
 
        aAdd(aColsGrid, {;
            "",;
            "",;
            "",;
            "",;
            0,;
            0,;
            .F.;
        })
    EndIf
    QRY_SBM->(DbCloseArea())
 
    //Define o array
    oSay:SetText("Atribuindo os dados na tela")
    oGetGrid:SetArray(aColsGrid)
    oGetGrid:Refresh()
 
    RestArea(aArea)
Return
 
Static Function fGrupo()
    Local nLinha   := oGetGrid:At()   
    Local nColCod  := 1
 
    MsgInfo("Estou no grupo: " + aColsGrid[nLinha][nColCod], "Atencao")
Return

Grid em MVC (Grid alterável utilizando conceito de MVC em AdvPL):

Grid em MVC

#Include 'Totvs.ch'
#Include 'FWMVCDef.ch'
 
Static cTitle := "Grupo de Produtos em MVC"
Static cKey    := "FAKE"
Static nTamFake := 15
 
/*/{Protheus.doc} User Function zGrid
Visualizacao de Grupos de Produtos em MVC (com tabela temporaria)
@type  Function
@author Atilio
@since  14/06/2020
@version version
@obs Foi baseado no exemplo de Izac Ciszevski (https://centraldeatendimento.totvs.com/hc/pt-br/articles/360047143634-MP-ADVPL-Criando-uma-tela-MVC-s%C3%B3-com-GRID)
/*/
 
User Function zGrid()
    Local aArea := GetArea()
    Private cAliasTmp := "TMPSBM"
 
    //Cria a temporária
    oTempTable := FWTemporaryTable():New(cAliasTmp)
     
    //Adiciona no array das colunas as que serão incluidas (Nome do Campo, Tipo do Campo, Tamanho, Decimais)
    aFields := {}
    aAdd(aFields, {"XXCODIGO",  "C", TamSX3('BM_GRUPO')[01],   0})
    aAdd(aFields, {"XXDESCRI",  "C", TamSX3('BM_DESC')[01],    0})
     
    //Define as colunas usadas, adiciona indice e cria a temporaria no banco
    oTempTable:SetFields( aFields )
    oTempTable:AddIndex("1", {"XXCODIGO"} )
    oTempTable:Create()
 
    //Executa a inclusao na tela
    FWExecView('GRID Sem Cabeçalho', "VIEWDEF.zGrid", MODEL_OPERATION_INSERT, , { || .T. }, , 30)
 
    //Agora percorre todos os dados digitados
    (cAliasTmp)->(DbGoTop())
    While ! (cAliasTmp)->(EoF())
        MsgInfo("Código: " + (cAliasTmp)->XXCODIGO + ", Descrição: " + (cAliasTmp)->XXDESCRI, "Atenção")
        (cAliasTmp)->(DbSkip())
    EndDo
 
    //Deleta a temporaria
    oTempTable:Delete()
     
    RestArea(aArea)
Return
 
Static Function ModelDef()
    Local oModel  As Object
    Local oStrField As Object
    Local oStrGrid As Object
 
    //Criamos aqui uma estrutura falsa que sera uma tabela que ficara escondida no cabecalho
    oStrField := FWFormModelStruct():New()
    oStrField:AddTable('' , { 'XXTABKEY' } , cTitle, {|| ''})
    oStrField:AddField('String 01' , 'Campo de texto' , 'XXTABKEY' , 'C' , nTamFake)
 
    //Criamos aqui a estrutura da grid
    oStrGrid := FWFormModelStruct():New() 
    oStrGrid:AddTable(cAliasTmp, {'XXTABKEY', 'XXCODIGO', 'XXDESCRI'}, "Temporaria")
      
    //Adiciona os campos da estrutura
    oStrGrid:AddField(;
        "Codigo",;                                                                                  // [01]  C   Titulo do campo
        "Codigo",;                                                                                  // [02]  C   ToolTip do campo
        "XXCODIGO",;                                                                                // [03]  C   Id do Field
        "C",;                                                                                       // [04]  C   Tipo do campo
        06,;                                                                                        // [05]  N   Tamanho do campo
        0,;                                                                                         // [06]  N   Decimal do campo
        Nil,;                                                                                       // [07]  B   Code-block de validação do campo
        Nil,;                                                                                       // [08]  B   Code-block de validação When do campo
        {},;                                                                                        // [09]  A   Lista de valores permitido do campo
        .T.,;                                                                                       // [10]  L   Indica se o campo tem preenchimento obrigatório
        FwBuildFeature( STRUCT_FEATURE_INIPAD, cAliasTmp+"->XXCODIGO" ),;                           // [11]  B   Code-block de inicializacao do campo
        .T.,;                                                                                       // [12]  L   Indica se trata-se de um campo chave
        .F.,;                                                                                       // [13]  L   Indica se o campo pode receber valor em uma operação de update.
        .F.)                                                                                        // [14]  L   Indica se o campo é virtual
    oStrGrid:AddField(;
        "Descricao",;                                                                               // [01]  C   Titulo do campo
        "Descricao",;                                                                               // [02]  C   ToolTip do campo
        "XXDESCRI",;                                                                                // [03]  C   Id do Field
        "C",;                                                                                       // [04]  C   Tipo do campo
        50,;                                                                                        // [05]  N   Tamanho do campo
        0,;                                                                                         // [06]  N   Decimal do campo
        Nil,;                                                                                       // [07]  B   Code-block de validação do campo
        Nil,;                                                                                       // [08]  B   Code-block de validação When do campo
        {},;                                                                                        // [09]  A   Lista de valores permitido do campo
        .T.,;                                                                                       // [10]  L   Indica se o campo tem preenchimento obrigatório
        FwBuildFeature( STRUCT_FEATURE_INIPAD, cAliasTmp+"->XXDESCRI" ),;                           // [11]  B   Code-block de inicializacao do campo
        .F.,;                                                                                       // [12]  L   Indica se trata-se de um campo chave
        .F.,;                                                                                       // [13]  L   Indica se o campo pode receber valor em uma operação de update.
        .F.)                                                                                        // [14]  L   Indica se o campo é virtual
 
    //Agora criamos o modelo de dados da nossa tela
    oModel := MPFormModel():New('zGridM')
    oModel:AddFields('CABID', , oStrField)
    oModel:AddGrid('GRIDID', 'CABID', oStrGrid)
    oModel:SetRelation('GRIDID', { { 'XXTABKEY', 'XXTABKEY' } })
    oModel:SetDescription(cTitle)
    oModel:SetPrimaryKey({ 'XXTABKEY' })
 
    //Ao ativar o modelo, irá alterar o campo do cabeçalho mandando o conteúdo FAKE pois é necessário alteração no cabeçalho
    oModel:SetActivate({ | oModel | FwFldPut("XXTABKEY", cKey) })
Return oModel
 
Static Function ViewDef()
    Local oView    As Object
    Local oModel   As Object
    Local oStrCab  As Object
    Local oStrGrid As Object
 
    //Criamos agora a estrtutura falsa do cabeçalho na visualização dos dados
    oStrCab := FWFormViewStruct():New()
    oStrCab:AddField('XXTABKEY' , '01' , 'String 01' , 'Campo de texto', , 'C')
 
    //Agora a estrutura da Grid
    oStrGrid := FWFormViewStruct():New()
  
    //Adicionando campos da estrutura
    oStrGrid:AddField(;
        "XXCODIGO",;                // [01]  C   Nome do Campo
        "01",;                      // [02]  C   Ordem
        "Codigo",;                  // [03]  C   Titulo do campo
        "Codigo",;                  // [04]  C   Descricao do campo
        Nil,;                       // [05]  A   Array com Help
        "C",;                       // [06]  C   Tipo do campo
        "@!",;                      // [07]  C   Picture
        Nil,;                       // [08]  B   Bloco de PictTre Var
        Nil,;                       // [09]  C   Consulta F3
        .T.,;                       // [10]  L   Indica se o campo é alteravel
        Nil,;                       // [11]  C   Pasta do campo
        Nil,;                       // [12]  C   Agrupamento do campo
        Nil,;                       // [13]  A   Lista de valores permitido do campo (Combo)
        Nil,;                       // [14]  N   Tamanho maximo da maior opção do combo
        Nil,;                       // [15]  C   Inicializador de Browse
        Nil,;                       // [16]  L   Indica se o campo é virtual
        Nil,;                       // [17]  C   Picture Variavel
        Nil)                        // [18]  L   Indica pulo de linha após o campo
    oStrGrid:AddField(;
        "XXDESCRI",;                // [01]  C   Nome do Campo
        "02",;                      // [02]  C   Ordem
        "Descricao",;               // [03]  C   Titulo do campo
        "Descricao",;               // [04]  C   Descricao do campo
        Nil,;                       // [05]  A   Array com Help
        "C",;                       // [06]  C   Tipo do campo
        "@!",;                      // [07]  C   Picture
        Nil,;                       // [08]  B   Bloco de PictTre Var
        Nil,;                       // [09]  C   Consulta F3
        .T.,;                       // [10]  L   Indica se o campo é alteravel
        Nil,;                       // [11]  C   Pasta do campo
        Nil,;                       // [12]  C   Agrupamento do campo
        Nil,;                       // [13]  A   Lista de valores permitido do campo (Combo)
        Nil,;                       // [14]  N   Tamanho maximo da maior opção do combo
        Nil,;                       // [15]  C   Inicializador de Browse
        Nil,;                       // [16]  L   Indica se o campo é virtual
        Nil,;                       // [17]  C   Picture Variavel
        Nil)                        // [18]  L   Indica pulo de linha após o campo
 
    //Carrega o ModelDef
    oModel  := FWLoadModel('zGrid')
 
    //Agora na visualização, carrega o modelo, define o cabeçalho e a grid, e no cabeçalho coloca 0% de visualização, e na grid coloca 100%
    oView := FwFormView():New()
    oView:SetModel(oModel)
    oView:AddField('CAB', oStrCab, 'CABID')
    oView:AddGrid('GRID', oStrGrid, 'GRIDID')
    oView:CreateHorizontalBox('TOHID', 0)
    oView:CreateHorizontalBox('TOSHOW', 100)
    oView:SetOwnerView('CAB' , 'TOHID')
    oView:SetOwnerView('GRID', 'TOSHOW')
    oView:SetDescription(cTitle)
Return oView

Update – Setembro de 2023

Recentemente um aluno perguntou se era possível criar uma tela similar a de cima, porém com duas grids.

Pensando nisso, montamos uma lógica de criar uma tabela temporária fake (PAI), que ficará escondida, e irá se relacionar com o grupo de produto da SBM (FILHO) e com os produtos da SB1 (NETO).

Abaixo um gif de exemplo:

Exemplo da grid SBM e SB1 sem cabeçalho

Abaixo o fonte da Grid dupla em MVC com cabeçalho FAKE:

//Bibliotecas
#Include "Totvs.ch"
#Include "FWMVCDef.ch"

//Variveis Estaticas
Static cTitulo := "Grupo de Produtos x Produtos"
Static cTabPai := "TMPTAB"
Static cTabFilho := "SBM"
Static cTabNeto := "SB1"
Static cKey    := "FAKE"
Static nTamFake := 15

/*/{Protheus.doc} User Function zDouble
Exemplo de visualização de duas grids em uma tela MVC
@author Daniel Atilio
@since 17/09/2023
@version 1.0
@type function
/*/

User Function zDouble()
	Local aArea   := GetArea()

	//Cria a temporária
    oTempTable := FWTemporaryTable():New(cTabPai)
     
    //Adiciona no array das colunas as que serão incluidas (Nome do Campo, Tipo do Campo, Tamanho, Decimais)
    aFields := {}
    aAdd(aFields, {"XXTABKEY",  "C", nTamFake,                0})
     
    //Define as colunas usadas, adiciona indice e cria a temporaria no banco
    oTempTable:SetFields( aFields )
    oTempTable:AddIndex("1", {"XXTABKEY"} )
    oTempTable:Create()

    //Cria um registro para ser possível acionar o FWExecView
    RecLock(cTabPai, .T.)
        XXTABKEY := cKey
    (cTabPai)->(MsUnlock())
 
    //Executa a inclusao na tela
    INCLUI := .F.
    FWExecView('GRID Sem Cabeçalho', "VIEWDEF.zDouble", MODEL_OPERATION_VIEW, , { || .T. }, , 30)
 
    //Deleta a temporaria
    oTempTable:Delete()

	RestArea(aArea)
Return Nil

/*/{Protheus.doc} ModelDef
Modelo de dados na funcao zDouble
@author Daniel Atilio
@since 17/09/2023
@version 1.0
@type function
/*/

Static Function ModelDef()
	Local oStruPai
	Local oStruFilho := FWFormStruct(1, cTabFilho)
    Local oStruNeto := FWFormStruct(1, cTabNeto)
	Local aRelFilho := {}
    Local aRelNeto := {}
	Local oModel
	Local bPre := Nil
	Local bPos := Nil
	Local bCommit := Nil
	Local bCancel := Nil

    //Criamos aqui uma estrutura falsa que sera uma tabela que ficara escondida no cabecalho
    oStruPai := FWFormModelStruct():New()
    oStruPai:AddTable(cTabPai, {'XXTABKEY'}, "Temporaria")
    oStruPai:AddField(;
        "Codigo",;                                                                                  // [01]  C   Titulo do campo
        "Codigo",;                                                                                  // [02]  C   ToolTip do campo
        "XXTABKEY",;                                                                                // [03]  C   Id do Field
        "C",;                                                                                       // [04]  C   Tipo do campo
        nTamFake,;                                                                                  // [05]  N   Tamanho do campo
        0,;                                                                                         // [06]  N   Decimal do campo
        Nil,;                                                                                       // [07]  B   Code-block de validação do campo
        Nil,;                                                                                       // [08]  B   Code-block de validação When do campo
        {},;                                                                                        // [09]  A   Lista de valores permitido do campo
        .T.,;                                                                                       // [10]  L   Indica se o campo tem preenchimento obrigatório
        FwBuildFeature( STRUCT_FEATURE_INIPAD, "Iif(!INCLUI,"+cTabPai+"->XXTABKEY,'')" ),;          // [11]  B   Code-block de inicializacao do campo
        .T.,;                                                                                       // [12]  L   Indica se trata-se de um campo chave
        .F.,;                                                                                       // [13]  L   Indica se o campo pode receber valor em uma operação de update.
        .F.)                                                                                        // [14]  L   Indica se o campo é virtual
    
	//Cria o modelo de dados para cadastro
	oModel := MPFormModel():New("zDoubleM", bPre, bPos, bCommit, bCancel)
	oModel:AddFields("TMPMASTER", /*cOwner*/, oStruPai)
	oModel:AddGrid("SBMDETAIL","TMPMASTER",oStruFilho,/*bLinePre*/, /*bLinePost*/,/*bPre - Grid Inteiro*/,/*bPos - Grid Inteiro*/,/*bLoad - Carga do modelo manualmente*/)
    oModel:AddGrid("SB1DETAIL","SBMDETAIL",oStruNeto,/*bLinePre*/, /*bLinePost*/,/*bPre - Grid Inteiro*/,/*bPos - Grid Inteiro*/,/*bLoad - Carga do modelo manualmente*/)
	oModel:SetPrimaryKey({})

    //Fazendo o relacionamento (pai e filho)
	aAdd(aRelFilho, {"BM_FILIAL", "FWxFilial('SBM')"} )
	oModel:SetRelation("SBMDETAIL", aRelFilho, SBM->(IndexKey(1)))

	//Fazendo o relacionamento (filho e neto)
	aAdd(aRelNeto, {"B1_FILIAL", "FWxFilial('SB1')"} )
	aAdd(aRelNeto, {"B1_GRUPO", "BM_GRUPO"})
	oModel:SetRelation("SB1DETAIL", aRelNeto, SB1->(IndexKey(1)))

Return oModel

/*/{Protheus.doc} ViewDef
Visualizacao de dados na funcao zDouble
@author Daniel Atilio
@since 17/09/2023
@version 1.0
@type function
/*/

Static Function ViewDef()
	Local oModel := FWLoadModel("zDouble")
	Local oStruPai
	Local oStruFilho := FWFormStruct(2, cTabFilho)
    Local oStruNeto := FWFormStruct(2, cTabNeto)
	Local oView

    //Criamos agora a estrtutura falsa do cabeçalho na visualização dos dados
    oStruPai := FWFormViewStruct():New()
    oStruPai:AddField(;
        "XXTABKEY",;                // [01]  C   Nome do Campo
        "01",;                      // [02]  C   Ordem
        "Codigo",;                  // [03]  C   Titulo do campo
        "Codigo",;                  // [04]  C   Descricao do campo
        Nil,;                       // [05]  A   Array com Help
        "C",;                       // [06]  C   Tipo do campo
        "@!",;                      // [07]  C   Picture
        Nil,;                       // [08]  B   Bloco de PictTre Var
        Nil,;                       // [09]  C   Consulta F3
        Iif(INCLUI, .T., .F.),;     // [10]  L   Indica se o campo é alteravel
        Nil,;                       // [11]  C   Pasta do campo
        Nil,;                       // [12]  C   Agrupamento do campo
        Nil,;                       // [13]  A   Lista de valores permitido do campo (Combo)
        Nil,;                       // [14]  N   Tamanho maximo da maior opção do combo
        Nil,;                       // [15]  C   Inicializador de Browse
        Nil,;                       // [16]  L   Indica se o campo é virtual
        Nil,;                       // [17]  C   Picture Variavel
        Nil)                        // [18]  L   Indica pulo de linha após o campo

	//Cria a visualizacao do cadastro
	oView := FWFormView():New()
	oView:SetModel(oModel)
	oView:AddField("VIEW_TMP", oStruPai, "TMPMASTER")
	oView:AddGrid("VIEW_SBM",  oStruFilho,  "SBMDETAIL")
    oView:AddGrid("VIEW_SB1",  oStruNeto,  "SB1DETAIL")

	//Partes da tela
	oView:CreateHorizontalBox("CABEC_PAI", 0)
	oView:CreateHorizontalBox("GRID_FILHO", 40)
    oView:CreateHorizontalBox("GRID_NETO", 60)
	oView:SetOwnerView("VIEW_TMP", "CABEC_PAI")
	oView:SetOwnerView("VIEW_SBM", "GRID_FILHO")
    oView:SetOwnerView("VIEW_SB1", "GRID_NETO")

	//Titulos
    oView:EnableTitleView("VIEW_TMP", "Pai - TMP (Fake)")
	oView:EnableTitleView("VIEW_SBM", "Filho - SBM (Grupos de Produtos)")
	oView:EnableTitleView("VIEW_SB1", "Neto - SB1 (Produtos)")

Return oView

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

20 Responses

  1. Armando Neto disse:

    Muito bom.
    Estou trabalhando em algumas personalizações, que vão trabalhar exatamente com este conceito MVC.
    Obrigado por compartilhar.

  2. Renato Ikeda disse:

    Obrigado pelo material técnico de excelente qualidade eu estava procurando como usar temporárias com grid em MVC e caí aqui no seu site.
    Agora estou com a seguinte dificuldade na classe MsNewGetDados tínhamos a opção de congelar a primeira coluna para facilitar a visualização / navegação, vc sabe se isto é possível no FwFormGrid?

    Grato!

  3. Flavio Dias disse:

    Bom dia, como eu faria para dar um refresc nela 1 opção após carregado a grid?

    • Dan_Atilio disse:

      Boa tarde.
      Se você usar com uma FWTemporaryTable, você teria que dar um reclock na temporária, depois forçar uma atualização indo ao fim e voltando ao topo, +- assim:
      (cAliasTmp)->(DbGoTop())
      oGetGrid:GoBottom(.T.)
      oGetGrid:Refresh(.T.)
      oGetGrid:GoTop(.T.)
      oGetGrid:Refresh(.T.)

  4. Eduardo Antunes disse:

    Bom dia.
    Estou tentando implementar uma rotina que a presenta detalhes ao clicar em uma linha do controle FWBrowse(). Você saberia dizer qual o método?

    • Dan_Atilio disse:

      Boa tarde Eduardo.
      Um clique apenas nunca precisei, mas já tive que usar o duplo clique, então usei o método SetDoubleClick, passando um bloco de código que chama uma função estática dentro, por exemplo:

      oGetGrid:SetDoubleClick( {|| fSuaFuncao() } ) 
      
  5. Cristian Camillo Verneque disse:

    Boa tarde .. tudo bem??? você sabe se é possivel desabilitar a edição de uma linha no FwBrowse por uma determinada condição?? por exemplo: posso editar a linha se a legenda for VERDE e quando for VERMELHA nao editar nenhum campo?

    • Dan_Atilio disse:

      Bom dia Cristian, tudo sim graças a Deus e você?
      Olha, eu desconheço a existência de algum método assim nativamente.
      O que você pode fazer é na mudança de linha, na validação de linha, ou na edição da célula, você validar a condição da legenda.

  6. Marcos Abrahão disse:

    Bom dia Atílio, tudo bem?
    Mudei a chamada do FWExecView para (só quero abrir uma tela de consulta) :

    FWExecView(‘GRID Sem Cabeçalho’, “VIEWDEF.zGrid”, MODEL_OPERATION_VIEW, , { || .T. }, , ,aButtons )

    Mas assim o MVC não abre nada na tela, sabe porquê?
    Obrigado!!

    • Dan_Atilio disse:

      Bom dia Marcos, bem graças a Deus e você?
      Certo, no caso como está sua ViewDef dentro do fonte zGrid?
      Se o fonte e a User Function estiverem com outro nome, por exemplo, zDaniel, você tem que mudar também no ExecView, por exemplo, VIEWDEF.zDaniel.

  7. Robson disse:

    Bom dia Atílio, tudo bem?
    como faço para adicionar o campo de pesquisa?
    vi que tem a criação de índices, porem não exibe o campo de pesquisa pra selecionar o índice e info0rmar o conteúdo da pesquisa.

    • Bom dia Robson, tudo sim graças a Deus e você?
      Com FWBrowse, nunca precisei também, mas já fiz com FWMarkBrowse e suponho que deva ser o mesmo procedimento.
      Primeiro, você precisa de um array (vamos chamar de aSeek), que irá conter os índices que poderão ser pesquisados, e depois iremos usar o método SetSeek dentro do atributo oBrowse, ficando +- assim:

      //Para exemplo, caso precise, esses são os campos: aAdd(aSeek,{AllTrim(SX3->X3_TITULO)	,{{"",SX3->X3_TIPO,SX3->X3_TAMANHO, SX3->X3_DECIMAL,AllTrim(SX3->X3_TITULO),AllTrim(SX3->X3_PICTURE)}} } )
      
      aAdd(aSeek, {"Produto", {{"", "C", 15, 0, "Produto", "@!}} } )
      
      //...
      oBrowse:oBrowse:SetSeek(.T., aSeek)
      
  8. Tauani disse:

    Bom dia, Atílio!
    Tudo bem?
    Uma dúvida de principiante em AdvPl hiihihi, aqui na parte do código: “oGetGrid:AddLegend(“oGetGrid:oData:aArray[oGetGrid:At(), 4] == ‘0’”, “RED”, “Nao Original”)”, esse treco em específico o que diz: [oGetGrid:At(), 4] em relação ao lugar do número 4?

    • Bom dia Tauani, tudo joia graças a Deus e você?

      Acha jovem, todos estamos sempre em constante aprendizado.

      O que o código esta fazendo é o seguinte:
      + Acessando a grid: oGetGrid:oData:aArray
      + Pegando a linha atual: oGetGrid:At()
      + Pegando a coluna: 4

      Então ele esta indo na grid, buscando a informação da linha atual, na coluna de número 4 (essa coluna é que representa o campo BM_PROORI).

      Um grande abraço.

  9. Dayana Rocha disse:

    Bom dia Atílio, td bem?
    No MsNewGetDados quando precisava pegar o valor de um campo usavamos o nAT para saber a linha (oApont:OBROWSE:NAT ) e ColPos para saber a coluna (oApont:OBROWSE:COLPOS) assim conseguia chegar no campo q estavamos alterando para realizar algum processamento.
    Nesse novo formato, como consigo essas informações?

    • Bom dia Dayana, tudo joia graças a Deus e você?

      A FWBrowse, logo após você ativar ela, ela tem um atributo chamado oBrowse que herda da TGrid ( https://tdn.totvs.com/display/tec/TGrid ).

      Então você pode usar os métodos e atributos da TGrid. Com isso em mente, tem 3 métodos interessantes (no TDN acima), que são o GetCursorPos (pega a linha e coluna), o SetSelectedRow (define a linha) e GoColumn (define a coluna).

      Supondo que sua FWBrowse se chame oBrwTeste, e você queira mostrar em qual linha e coluna você está, seria algo assim:

      aPosic := oBrwTeste:oBrowse:GetCursorPos()
      Alert("linha " + cValToChar(aPosic[1]) + ", coluna " + cValToChar(aPosic[2]))
      

      Já, supondo que você queira posicionar na linha 3, coluna 2, seria:

      nLinha := 3
      nColuna := 2
      
      oBrwTeste:oBrowse:SetSelectedRow(nLinha)
      oBrwTeste:oBrowse:GoColumn(nColuna)
      

      Obs.: Se você usa tabela temporária na sua FWBrowse, ao invés de usar SetSelectedRow, basta você posicionar no registro da sua temporária seja com MsSeek ou com DbSkip.

      Abraços.

  10. Mardyore disse:

    Boa tarde jovem!
    Primeiramente, muito obrigada por compartilhar sua experiência e seu conhecimento!
    Estou migrando um MSGetDados para FWBrowse COM ARRAY, seguindo o exemplo que você citou e até agora tudo funcionando certinho.
    Mas preciso que algumas linhas da grid sejam apresentadas com a cor de fundo diferente do padrão a partir do conteúdo de uma célula da grid.
    Através da MSGetDados eu fazia isto utilizando a função SetBlkBackColor.
    Vi no TDN que através FWBrowse também é possível esta ação da mudança de cor da linha: FWBrowse():SetBlkBackColor()
    Mas não consegui entender como faço para retornar a cor que eu quero através de algum condicional, pois na MSGetDados eu tinha a posição da linha e coluna do array para analisar o conteúdo e determinar a cor em cada situação.
    Não encontrei como faço para substituir o tratamento abaixo para uma FWBrowse. Help! Please!

    Exemplo:

    oGet := MSGetDados():New( aPosObj[2,1], aPosObj[2,2], aPosObj[2,3], aPosObj[2,4],nOpcOper,”AllwaysTrue”,”AllwaysTrue()”,, .T.,aCpoAlter, , , Len(aCols) )
    oGet:oBrowse:lUseDefaultcolors := .F.
    oGet:oBrowse:SetBlkBackColor({|| GETDCLR( aHeader, aCols, oGet:oBrowse:nAt ) } )

    Static Function GETDCLR( aHeader, aCols, nLinha )
    Local nCorRet := Nil
    Local nPQuant1 := aScan(aHeader,{|x| Alltrim(x[2]) == “ZZ_QUANTC1”})
    Local nPQuant2 := aScan(aHeader,{|x| Alltrim(x[2]) == “ZZ_QUANTC2”})

    if !aCols[ nLinha, Len(aHeader)+1 ] .and. !( aCols[ nLinha, nPQuant1 ] == aCols[ nLinha, nPQuant2 ] )
    nCorRet := CLR_HRED
    Elseif aCols[ nLinha, Len(aHeader)+1 ]
    nCorRet := CLR_LIGHTGRAY
    Endif

    Return( nCorRet )

    • Bom dia, tudo joia?

      Acha, eu que agradeço pelo comentário e pela bondade nas suas palavras.

      A pedido de um assinante premium no grupo do WhatsApp, montamos um artigo, que irá ao ar dia 21/08/2024, segue um print de como ficou a tela: https://terminaldeinformacao.com/wp-content/uploads/2023/11/print_cores.png

      A lógica seria a seguinte:
      1. Criar normalmente nossa dialog e nossa grid com FWBrowse
      2. Logo após acionar o método Activate, vamos chamar uma função que aqui demos o nome de fDefinCor
      3. Nessa função, iremos percorrer os dados da temporária, e da linha 0 a 5 vamos pintar de uma cor, da 6 a 10 de outra, e a partir da 11 de outra

      Abaixo o trecho após criar o FWBrowse:
      […]
      oGetGrid:SetColumns(aColunas)
      oGetGrid:SetOwner(oPanGrid)
      oGetGrid:Activate()

      //Após o activate para carregar o “oBrowse” instanciado da TGrid
      fDefinCor()
      […]

      E abaixo o trecho da função fDefinCor:
      […]
      Static Function fDefinCor()
      Local nCor := 0
      Local nLinha := -1

      //Percorre a temporária
      While ! (cAliasTab)->(EoF())
      nLinha++

      //Se for a linha 0 a 5, será na cor amarela e o texto na cor cinza meio escuro
      If nLinha <= 5 nCorFundo := RGB(232, 216, 000) nCorTexto := RGB(108, 108, 108) //Se for a linha 6 a 10, fundo será vermelho e o texto será branco ElseIf nLinha >= 6 .And. nLinha <= 10 nCorFundo := RGB(255, 000, 000) nCorTexto := RGB(255, 255, 255) //Senão, fundo será verde o texto será preto Else nCorFundo := RGB(000, 255, 000) nCorTexto := RGB(000, 000, 000) EndIf //Caso queira ver a documentação do método - https://tdn.totvs.com/display/tec/TGrid%3ASetRowColor oGetGrid:oBrowse:SetRowColor(nLinha, nCorFundo, nCorTexto ) (cAliasTab)->(DbSkip())
      EndDo
      (cAliasTab)->(DbGoTop())

      Return nCor
      […]

      Um grande abraço.

Deixe uma resposta para Dan_AtilioCancelar resposta

Terminal de Informação