FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour XBrowse com Array - Edição linear entre colunas específicas ao pressionar ENTER
Posts: 18
Joined: Wed Nov 08, 2023 02:58 PM

XBrowse com Array - Edição linear entre colunas específicas ao pressionar ENTER

Posted: Thu Jun 04, 2026 04:35 PM

Boa tarde,

Estou utilizando FiveWin com XBrowse baseado em Array, contendo aproximadamente 12 colunas.
Preciso implementar uma edição linear controlada pelo teclado, sem que o usuário precise selecionar manualmente a próxima coluna.

A necessidade é a seguinte:
Ao pressionar ENTER sobre uma linha do XBrowse:
O sistema deve posicionar na coluna 3 e iniciar a edição.
Após pressionar ENTER na coluna 3, deve validar o conteúdo digitado.
Se estiver válido, deve ir automaticamente para a coluna 5 e iniciar a edição.
Após ENTER na coluna 5, validar e ir para a coluna 7.
Após ENTER na coluna 7, validar e finalizar a edição da linha, podendo ir para a próxima linha ou voltar o foco ao browse.
Caso o usuário pressione ESC, deve sair da edição.

O objetivo é fazer uma edição sequencial, semelhante ao comportamento de lEditCol do WBrowse, porém usando XBrowse.

Exemplo do fluxo desejado:

ENTER na linha

-> edita coluna 3
-> valida

-> edita coluna 5
-> valida

-> edita coluna 7
-> valida
-> finaliza linha

Att.
lZanardo

Posts: 44235
Joined: Thu Oct 06, 2005 05:47 PM

Re: XBrowse com Array - Edição linear entre colunas específicas ao pressionar ENTER

Posted: Fri Jun 05, 2026 04:51 AM

Estimado Izanardo,

//
// Sequential keyboard-driven edit in XBrowse (Array data source)
// No timers ÔÇö uses PostMessage for column advance.
// Flow: ENTER on row -> edit col3 -> validate -> col5 -> validate -> col7 -> next row
// ESC cancels edit. UP/DOWN accept value and end sequence.
//

#include "FiveWin.ch"
#include "xbrowse.ch"

REQUEST DBFCDX

// --- Edit sequence: columns to edit in order ---
STATIC aEditSeq := { 3, 5, 7 }

// --- Validation functions ---
STATIC FUNCTION ValCol3( xVal )
   IF Empty( xVal )
      MsgStop( "Column 3 cannot be empty", "Validation" )
      RETURN .F.
   ENDIF
RETURN .T.

STATIC FUNCTION ValCol5( xVal )
   IF Empty( xVal )
      MsgStop( "Column 5 cannot be empty", "Validation" )
      RETURN .F.
   ENDIF
RETURN .T.

STATIC FUNCTION ValCol7( xVal )
   IF Empty( xVal )
      MsgStop( "Column 7 cannot be empty", "Validation" )
      RETURN .F.
   ENDIF
RETURN .T.

STATIC FUNCTION ValidateCol( nCol, xVal )
   DO CASE
   CASE nCol == 3 ; RETURN ValCol3( xVal )
   CASE nCol == 5 ; RETURN ValCol5( xVal )
   CASE nCol == 7 ; RETURN ValCol7( xVal )
   ENDCASE
RETURN .T.

// --- Main ---
FUNCTION Main()
   LOCAL oWnd, oBrw
   LOCAL aData := {}
   LOCAL nRow, nCol, nI
   LOCAL nDispPos

   // Build sample data: 20 rows x 12 columns
   FOR nRow := 1 TO 20
      AAdd( aData, Array( 12 ) )
      FOR nCol := 1 TO 12
         aData[ nRow, nCol ] := "R" + Str( nRow, 2 ) + "C" + Str( nCol, 2 )
      NEXT
   NEXT

   DEFINE WINDOW oWnd TITLE "XBrowse Sequential Edit (ENTER=col3->col5->col7)"

   @ 0, 0 XBROWSE oBrw OF oWnd ARRAY aData AUTOCOLS ;
      CELL LINES NOBORDER FASTEDIT

   // --- State: stored in browse Cargo ---
   oBrw:Cargo := { "lInSeqEdit" => .F. }

   // --- Browse configuration ---
   oBrw:nMarqueeStyle    := MARQSTYLE_HIGHLROWRC
   oBrw:nMoveType        := MOVE_NONE          // We control all edit navigation
   oBrw:lEnterKey2Edit   := .T.                // ENTER starts edit
   oBrw:nRowDividerStyle := LINESTYLE_LIGHTGRAY
   oBrw:nColDividerStyle := LINESTYLE_LIGHTGRAY

   // Set headers
   FOR nI := 1 TO 12
      oBrw:aCols[ nI ]:cHeader := "Column " + Str( nI, 2 )
   NEXT

   // Mark editable columns visually (light yellow background)
   FOR EACH nI IN aEditSeq
      oBrw:aCols[ nI ]:bClrStd := { || { CLR_BLACK, RGB( 255, 255, 220 ) } }
   NEXT

   // --- bKeyChar: redirect ENTER to first sequence column ---
   oBrw:bKeyChar := { | nKey, nFlags, oBrwRef, oCol |
      LOCAL nDispPos, hState := oBrwRef:Cargo
      IF nKey == VK_RETURN .and. ! oBrwRef:lEditMode
         // If user navigated away from sequence columns, reset
         IF hState[ "lInSeqEdit" ] .and. AScan( aEditSeq, oCol:nPos ) == 0
            hState[ "lInSeqEdit" ] := .F.
         ENDIF

     IF ! hState[ "lInSeqEdit" ]
        // Start new sequence: move to first column, let EnterKey2Edit start edit
        hState[ "lInSeqEdit" ] := .T.
        IF ( nDispPos := AScan( oBrwRef:aDisplay, aEditSeq[ 1 ] ) ) > 0
           oBrwRef:SelectCol( nDispPos )
        ENDIF
     ENDIF
     // During sequence (lInSeqEdit=.T.): EnterKey2Edit runs on current col
  ENDIF
  RETURN nil  // Let default KeyChar handling proceed
   }

   // --- Configure each column in the edit sequence ---
   FOR EACH nI IN aEditSeq
      WITH OBJECT oBrw:aCols[ nI ]
         :nEditType  := EDIT_GET

     // bEditValid: returns .F. to stay on column (re-edit), .T. to accept
     :bEditValid := { | oGet, oCol |
        ValidateCol( oCol:nPos, oGet:VarGet() )
     }

     // bOnPostEdit: save value, auto-advance to next column via PostMessage
     :bOnPostEdit := { | oCol, xVal, nKey |
        LOCAL nSeqIdx, nDispPos, hState := oCol:oBrw:Cargo
        IF nKey == VK_ESCAPE
           hState[ "lInSeqEdit" ] := .F.
           RETURN nil
        ENDIF

        // UP/DOWN/TAB: accept value, end sequence
        IF !( nKey == VK_RETURN )
           oCol:Value := xVal
           hState[ "lInSeqEdit" ] := .F.
           RETURN nil
        ENDIF

        // ENTER: save value
        oCol:Value := xVal

        // Find current position in sequence
        nSeqIdx := AScan( aEditSeq, oCol:nPos )
        IF nSeqIdx > 0 .and. nSeqIdx < Len( aEditSeq )
           // Advance to next column in sequence
           // Set nColSel + PostMessage ENTER to trigger EnterKey2Edit
           IF ( nDispPos := AScan( oCol:oBrw:aDisplay, aEditSeq[ nSeqIdx + 1 ] ) ) > 0
              oCol:oBrw:nColSel := nDispPos
           ENDIF
           PostMessage( oCol:oBrw:hWnd, WM_KEYDOWN, VK_RETURN, 0 )
        ELSE
           // Last column: sequence complete. Go to next row.
           hState[ "lInSeqEdit" ] := .F.
           IF oCol:oBrw:nRowSel < oCol:oBrw:nLen
              oCol:oBrw:GoDown()
           ENDIF
        ENDIF

        RETURN nil
     }
  ENDWITH
   NEXT

   oBrw:CreateFromCode()

   oWnd:oClient := oBrw

   ACTIVATE WINDOW oWnd CENTERED

RETURN nil
regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion