No vídeo de hoje vamos demonstrar em como criar uma ordenação de dados de um browse ao clicar na coluna usando a classe FWBrowse.
A dúvida de hoje foi feita pelo Ricardo, onde ele perguntou como seria possível fazer uma ordenação de dados em uma dialog customizada com FWBrowse.
Então através do método SetItemHeaderClick e uma função estática que criamos chamada fOrdena, nós podemos fazer essa tratativa.
E abaixo o código fonte desenvolvido para exemplificar:
//Bibliotecas #Include "Totvs.ch" #Include "FWMVCDef.ch" /*/{Protheus.doc} User Function zVid0060 Tela de Produtos @author Atilio @since 30/08/2022 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ User Function zVid0060() Local aArea := FWGetArea() Local aPergs := {} Local xPar0 := Space(15) Local xPar1 := StrTran(xPar0, ' ', 'Z') //Adicionando os parametros do ParamBox aAdd(aPergs, {1, "Produto De", xPar0, "", ".T.", "SB1", ".T.", 80, .F.}) aAdd(aPergs, {1, "Produto Até", xPar1, "", ".T.", "SB1", ".T.", 80, .T.}) //Se a pergunta for confirma, chama a tela If ParamBox(aPergs, "Informe os parametros", , , , , , , , , .F., .F.) fMontaTela() EndIf FWRestArea(aArea) Return /*/{Protheus.doc} fMontaTela Monta a tela com a marcação de dados @author Atilio @since 30/08/2022 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fMontaTela() Local aArea := GetArea() Local aCampos := {} Local oTempTable := Nil Local aColunas := {} Local cFontPad := 'Tahoma' Local oFontGrid := TFont():New(cFontPad,,-14) Local lOk := .F. Local bBlocoOk := {|| lOk := .T., oDlgBrowse:End()} Local bBlocoCan := {|| lOk := .F., oDlgBrowse:End()} Local aOutrasAc := { } Local bBlocoIni := {|| EnchoiceBar(oDlgBrowse, bBlocoOk, bBlocoCan, , aOutrasAc)} //Janela e componentes Private oDlgBrowse Private oPanGrid Private oBrowseTmp Private cAliasTmp := GetNextAlias() //Tamanho da janela Private aTamanho := MsAdvSize() Private nJanLarg := aTamanho[5] Private nJanAltu := aTamanho[6] //Ordenação da tela Private cUltOrdem := "" Private lDescend := .F. //Adiciona as colunas que serão criadas na temporária aAdd(aCampos, { 'B1_COD', 'C', 15, 0}) //Código aAdd(aCampos, { 'B1_DESC', 'C', 30, 0}) //Descrição aAdd(aCampos, { 'B1_TIPO', 'C', 2, 0}) //Tipo aAdd(aCampos, { 'B1_UM', 'C', 2, 0}) //Unidade Medida //Cria a tabela temporária oTempTable:= FWTemporaryTable():New(cAliasTmp) oTempTable:SetFields( aCampos ) oTempTable:AddIndex("1", {"B1_COD"} ) oTempTable:AddIndex("2", {"B1_DESC"} ) oTempTable:Create() //Popula a tabela temporária Processa({|| fPopula()}, 'Processando...') //Adiciona as colunas que serão exibidas no FWMarkBrowse aColunas := fCriaCols() //Criando a janela oDlgBrowse := TDialog():New(0, 0, nJanAltu, nJanLarg, 'Tela para Análise de Dados', , , , , , /*nCorFundo*/, , , .T.) //Dados oPanGrid := tPanel():New(030, 001, '', oDlgBrowse, , , , RGB(000,000,000), RGB(254,254,254), (nJanLarg/2)-1, (nJanAltu/2 - 10)) oBrowseTmp := FWBrowse():New() oBrowseTmp:SetDataTable() oBrowseTmp:SetInsert(.F.) oBrowseTmp:SetDelete(.F., { || .F. }) oBrowseTmp:SetAlias(cAliasTmp) oBrowseTmp:DisableReport() oBrowseTmp:DisableFilter() oBrowseTmp:DisableConfig() oBrowseTmp:DisableReport() oBrowseTmp:DisableSeek() oBrowseTmp:DisableSaveConfig() oBrowseTmp:AddLegend(cAliasTmp + "->B1_TIPO == 'PA'", "GREEN", "Produto Acabado") oBrowseTmp:AddLegend(cAliasTmp + "->B1_TIPO == 'PI'", "YELLOW", "Produto Intermediário") oBrowseTmp:AddLegend(cAliasTmp + "->B1_TIPO != 'PA' .And. " + cAliasTmp + "->B1_TIPO != 'PI'", "RED", "Outros Tipos") oBrowseTmp:SetFontBrowse(oFontGrid) oBrowseTmp:SetColumns(aColunas) oBrowseTmp:SetOwner(oPanGrid) oBrowseTmp:lHeaderClick := .T. oBrowseTmp:SetItemHeaderClick({"B1_COD", "B1_DESC"}) oBrowseTmp:Activate() oDlgBrowse:Activate(, , , .T., , , bBlocoIni) //Deleta a temporária e desativa a tela de marcação oTempTable:Delete() oBrowseTmp:DeActivate() RestArea(aArea) Return /*/{Protheus.doc} fPopula Executa a query SQL e popula essa informação na tabela temporária usada no browse @author Atilio @since 30/08/2022 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fPopula() Local cQryDados := '' Local nTotal := 0 Local nAtual := 0 //Monta a consulta cQryDados += "SELECT " + CRLF cQryDados += " B1_COD, " + CRLF cQryDados += " B1_DESC, " + CRLF cQryDados += " B1_TIPO, " + CRLF cQryDados += " B1_UM " + CRLF cQryDados += "FROM " + CRLF cQryDados += " SB1990 SB1 " + CRLF cQryDados += "WHERE " + CRLF cQryDados += " B1_FILIAL = '" + FWxfilial('SB1') + "' " + CRLF cQryDados += " AND B1_COD >= '" + MV_PAR01 + "' " + CRLF cQryDados += " AND B1_COD <= '" + MV_PAR02 + "' " + CRLF cQryDados += " AND SB1.D_E_L_E_T_ = ' ' " + CRLF cQryDados += "ORDER BY " + CRLF cQryDados += " B1_COD" + CRLF PLSQuery(cQryDados, 'QRYDADTMP') //Definindo o tamanho da régua DbSelectArea('QRYDADTMP') Count to nTotal ProcRegua(nTotal) QRYDADTMP->(DbGoTop()) //Enquanto houver registros, adiciona na temporária While ! QRYDADTMP->(EoF()) nAtual++ IncProc('Analisando registro ' + cValToChar(nAtual) + ' de ' + cValToChar(nTotal) + '...') RecLock(cAliasTmp, .T.) (cAliasTmp)->B1_COD := QRYDADTMP->B1_COD (cAliasTmp)->B1_DESC := QRYDADTMP->B1_DESC (cAliasTmp)->B1_TIPO := QRYDADTMP->B1_TIPO (cAliasTmp)->B1_UM := QRYDADTMP->B1_UM (cAliasTmp)->(MsUnlock()) QRYDADTMP->(DbSkip()) EndDo QRYDADTMP->(DbCloseArea()) (cAliasTmp)->(DbGoTop()) Return /*/{Protheus.doc} fCriaCols Função que gera as colunas usadas no browse (similar ao antigo aHeader) @author Atilio @since 30/08/2022 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fCriaCols() Local nAtual := 0 Local aColunas := {} Local aEstrut := {} Local oColumn //Adicionando campos que serão mostrados na tela //[1] - Campo da Temporaria //[2] - Titulo //[3] - Tipo //[4] - Tamanho //[5] - Decimais //[6] - Máscara aAdd(aEstrut, { 'B1_COD', 'Código', 'C', 15, 0, ''}) aAdd(aEstrut, { 'B1_DESC', 'Descrição', 'C', 30, 0, ''}) aAdd(aEstrut, { 'B1_TIPO', 'Tipo', 'C', 2, 0, ''}) aAdd(aEstrut, { 'B1_UM', 'Unidade Medida', 'C', 2, 0, ''}) //Percorrendo todos os campos da estrutura For nAtual := 1 To Len(aEstrut) //Cria a coluna oColumn := FWBrwColumn():New() oColumn:SetData(&('{|| ' + cAliasTmp + '->' + aEstrut[nAtual][1] +'}')) oColumn:SetTitle(aEstrut[nAtual][2]) oColumn:SetType(aEstrut[nAtual][3]) oColumn:SetSize(aEstrut[nAtual][4]) oColumn:SetDecimal(aEstrut[nAtual][5]) oColumn:SetPicture(aEstrut[nAtual][6]) //Se for o código ou descrição, adiciona a opção de clique no cabeçalho If Alltrim(aEstrut[nAtual][1]) $ "B1_COD;B1_DESC;" oColumn:bHeaderClick := &("{|| fOrdena('" + aEstrut[nAtual][1] + "') }") EndIf //Adiciona a coluna aAdd(aColunas, oColumn) Next Return aColunas Static Function fOrdena(cCampo) Default cCampo := "" //Pegando o Índice conforme o campo cCampo := Alltrim(cCampo) If cCampo == "B1_COD" nOrder := 1 ElseIf cCampo == "B1_DESC" nOrder := 2 EndIf //Ordena pelo Índice (cAliasTmp)->(DbSetOrder(nOrder)) //Se o último campo clicado é o mesmo, irá mudar entre crescente / decrescente If cUltOrdem == cCampo //Se esta como decrescente, ordena crescente If lDescend OrdDescend(nOrder, cValToChar(nOrder), .F.) lDescend := .F. //Se esta como crescente, ordena como decrescente Else OrdDescend(nOrder, cValToChar(nOrder), .T.) lDescend := .T. EndIf Else lDescend := .F. EndIf cUltOrdem := cCampo fRefresh() Return Static Function fRefresh() (cAliasTmp)->(DbGoTop()) oBrowseTmp:GoBottom(.T.) oBrowseTmp:Refresh(.T.) oBrowseTmp:GoTop(.T.) oBrowseTmp:Refresh(.T.) Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.
Mestre Atilio, bom dia! Estava procurando por essa dica e me deparei com seu artigo.
Fazendo bagunça por aqui descobri algo interessante!
Quando os campos do FWMBrowse() estão com SetEdit(.T.) e SetReadVar(x) habilitados eles se tornam ordenáveis por natureza, isso vindo de uma tabela temporária.
O problema é que eles ficam editáveis, mas talvez dê pra resolver na estrutura do browse através do SetEditCell(.T., {|| AQUI})…
Fica a dica aí como estudo científico… rs… Abraços!
Boa notícia! Complementando o comentário anterior:
Você pode deixar o SetEditCell() full, ou seja oBrowse:SetEditCell(.T., {|| .T.})
Aí tem duas formas de bloquear a edição somente de campos escolhidos:
oBrowse:SetPreEditCell({|| ReadVar() == “C6_ENTREG”}), como exemplo para editar somente o C6_ENTREG.
E a outra forma é adicionar um bloco de código com return false no SetDoubleClick(), já que o click duplo substitui o SetEdit() até para o ENTER 🙂
Fala Brunão, bom dia, tudo joia?
Opa, show de bola man. Obrigado pelo comentário e pela contribuição.
Com certeza vai ajudar bastante. Dessa forma fica mais fácil do que ter que tratar com ordenação manual no Alias.
Tenha uma ótima e abençoada quinta feira.
Um grande abraço.