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):
#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):
#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):
#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:
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.
Muito bom.
Estou trabalhando em algumas personalizações, que vão trabalhar exatamente com este conceito MVC.
Obrigado por compartilhar.
Eu que agradeço pelo comentário jovem.
Grande abraço.
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!
Bom dia Renato, tudo bem?
Não sei se é possível, pode ser que tenha sim algum atributo ou método, mas eu nunca utilizei nas classes mais novas.
Grande abraço.
Bom dia, como eu faria para dar um refresc nela 1 opção após carregado a grid?
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.)
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?
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:
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?
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.
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!!
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.
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:
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.
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:
Já, supondo que você queira posicionar na linha 3, coluna 2, seria:
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.
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.