FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Manual TXBrowse
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Manual TXBrowse
Posted: Mon Mar 09, 2026 04:12 PM

Gracias a Carlos Atúncar Hernandez

MANUAL DE REFERENCIA

TXBrowse / XBrowse

FiveWin para Harbour

Propiedades · Métodos · Eventos · Columnas

Basado en ejemplos oficiales de FiveWin

1\. Introducción

TXBrowse (invocado mediante el comando @ X,Y XBROWSE o XBROWSER) es el control de grilla más potente de FiveWin para Harbour. Permite mostrar y editar datos provenientes de tablas DBF/RDD, RecordSets ADO, arrays, objetos TDatabase, árboles (TTree) y archivos de texto.

1.1 Fuentes de Datos Soportadas

Propiedad / EventoTipoDescripción y Uso
ALIAS / DATASOURCERDDTabla DBF abierta. Uso: ALIAS 'CUST' o DATASOURCE "CUSTOMER"
RECORDSET / RECSETADORecordSet ADO. Uso: RECSET oRs o RECORDSET oRs
ARRAYArrayArray bidimensional. Uso: ARRAY aData
OBJECTObjetoObjeto TDatabase u otro con interfaz similar. Uso: OBJECT oCust
DATASOURCE oTreeTTreeÁrbol de nodos TTree para browse jerárquico

1.2 Formas de Crear el Browse

Comando declarativo

@ nRow, nCol XBROWSE oBrw OF oParent ALIAS 'CUST' AUTOCOLS LINES CELL

Programático (clase)

oBrw := TXBrowse():New( oWnd )

oBrw:SetRDD() // o :SetArray( aData ) / :SetRecordSet( oRs )

oBrw:CreateFromCode()

📌 NOTA: Siempre llamar :CreateFromCode() al final de la configuración, antes de activar el diálogo/ventana.

2\. Propiedades del Browse (TXBrowse)

2.1 Apariencia y Estilo

Propiedad / EventoTipoDescripción y Uso
nMarqueeStyleNuméricoEstilo del selector de fila/celda. Ver constantes MARQSTYLE\_\* (sección 7\)
nRowDividerStyleNuméricoEstilo de línea divisoria entre filas. Ver LINESTYLE\_\* (sección 7\)
nColDividerStyleNuméricoEstilo de línea divisoria entre columnas. Ver LINESTYLE\_\*
lColDividerCompleteLógicoSi .T., la línea de columna se extiende hasta el encabezado. Default: .F.
nRowHeightNuméricoAltura de cada fila en píxeles. Ej: oBrw:nRowHeight := 40
nHeaderHeightNuméricoAltura del encabezado en píxeles. Ej: oBrw:nHeaderHeight := 30
nDataLinesNuméricoNúmero de líneas de texto por fila de datos. Ej: := 2 para multilinea
nHeaderLinesNuméricoNúmero de líneas del encabezado. Ej: := 2 para encabezados de 2 líneas
nFooterLinesNuméricoNúmero de líneas del pie de columna
l2007LógicoActiva el estilo visual Office 2007\. Ej: oBrw:l2007 := .T.
nWidthsNuméricoAncho uniforme para todas las columnas en píxeles

2.2 Desplazamiento y Columnas

Propiedad / EventoTipoDescripción y Uso
nStretchColNuméricoColumna que se expande para llenar el espacio disponible. Ver STRETCHCOL\_\* (sección 7\)
nFreezeNuméricoNúmero de columnas congeladas (no se desplazan horizontalmente). Ej: oBrw:nFreeze := 2
lHScrollLógicoMuestra/oculta la barra de desplazamiento horizontal. Default: .T.
lVScrollLógicoMuestra/oculta la barra de desplazamiento vertical. Default: .T.
lRecordSelectorLógicoMuestra/oculta el selector de registro (columna izquierda). Default: .T.
nColSelNuméricoNúmero de columna actualmente seleccionada (lectura)

2.3 Colores

Propiedad / EventoTipoDescripción y Uso
bClrStdBloqueColor estándar de celdas. {{ nColorTexto, nColorFondo } }. Ej: {{CLR\_BLACK, CLR\_WHITE}}
bClrRowFocusBloqueColor de la fila con foco. Ej: {{CLR\_BLACK, RGB(185,220,255)}}
bClrSelFocusBloqueColor de la celda/fila seleccionada con foco. Igual formato que bClrStd
bClrHeaderBloqueColor del encabezado. Ej: {{CLR\_WHITE, RGB(134,219,9)}}
oDataFontsObjeto/BloqueFuente para datos. Puede ser objeto font o bloque {If(cond, oFontA, oFontB)}
oFooterFontsObjetoFuente para los pies de columna

📌 NOTA: Los bloques de color devuelven un array de 2 elementos: { nColorTexto, nColorFondo }. El fondo puede ser un color RGB o incluso un array de gradiente.

2.4 Fondo (Background)

Propiedad / EventoTipoDescripción y Uso
SetBackGround(cRes)MétodoEstablece imagen de fondo. Ej: oBrw:SetBackGround("PAPER")
SetBackGround(aGrad, lVert)MétodoFondo degradado. aGrad es array de tramos, lVert orienta vertical/horizontal
oBrushObjetoBrush actual del fondo. oBrw:oBrush:nResizeMode para el modo (BCK\_TILED, etc.)

Ejemplo de gradiente

local aGrad := {{ .5, CLR\_BLUE, RGB(202,202,255) }, { .5, RGB(202,202,255), CLR\_BLUE }}

// En el comando XBROWSE: BACKGROUND aGrad VERTICAL

// O en código: oBrw:SetBackGround( aGrad, .T. )

2.5 Edición

Propiedad / EventoTipoDescripción y Uso
nEditTypesNuméricoTipo de edición para TODAS las columnas de una vez. Ej: oBrw:nEditTypes := EDIT\_GET
lFastEditLógicoPermite edición rápida sin necesidad de pulsar Enter primero. Ej: := .T.
lFormulaEditLógicoActiva edición de fórmulas en las celdas. Ej: oBrw:lFormulaEdit := .T.
bEditWhensBloqueCondición global de cuándo permitir edición. {oColcondicion}
lAutoAppendLógicoPermite agregar filas automáticamente al llegar al final del array
bPastEofBloqueBloque ejecutado al intentar ir más allá del último registro

2.6 Pies de Columna (Footers)

Propiedad / EventoTipoDescripción y Uso
lFooterLógicoActiva/desactiva la fila de pie. Ej: oBrw:lFooter := .T.
cFootersArrayArray con textos de pie para todas las columnas
MakeTotals(\[oCol\])MétodoRecalcula los totales/agregados de los pies. Se llama tras configurar columnas

2.7 Ordenamiento y Búsqueda

Propiedad / EventoTipoDescripción y Uso
bSeekBloqueBloque para búsqueda incremental. {cDbSeek(Upper(c))}
nArrayAtNuméricoÍndice de la fila actual en el array (sólo para fuente ARRAY)
aArrayDataArrayReferencia al array de datos (fuente ARRAY)
aRowArrayArray con los valores de la fila actual
BookMarkVariosBookmark del registro actual (RDD/ADO)
nLenNuméricoNúmero total de registros/filas del browse

2.8 Multiselección

Propiedad / EventoTipoDescripción y Uso
aSelectedArrayArray con los bookmarks de las filas seleccionadas (multiselect)
SelectAll()MétodoSelecciona todas las filas
SelectNone()MétodoDeselecciona todas las filas
nMarqueeStyle := MARQSTYLE\_HIGHLROWMSConstanteActiva modo multiselección con Ctrl+clic

2.9 Grupos de Cabecera (Group Headers)

Propiedad / EventoTipoDescripción y Uso
SetGroupHeader(cTxt,n1,n2,oFont)MétodoDefine un encabezado de grupo que abarca columnas n1 a n2. Ej: :SetGroupHeader('EMPLOYEE'+CRLF+'NAME', 1, 2, oBold)
lAllowColReGroupLógicoPermite reordenar columnas dentro de un grupo. Ej: := .T.

Ejemplo

oBrw:SetGroupHeader( 'EMPLOYEE' \+ CRLF \+ 'NAME', 1, 2, oBold )

oBrw:SetGroupHeader( 'ADDRESS', 3, 6, oBold )

2.10 Árbol (Tree Browse)

Propiedad / EventoTipoDescripción y Uso
DATASOURCE oTreeTTreeFuente de datos tipo árbol para browse jerárquico
oTreeItemTTreeItemNodo actualmente seleccionado en el árbol
oTreeTTreeReferencia al árbol de datos del browse
SetTree(nil, aBmps)MétodoConvierte un browse normal en árbol automático
nDataTypeNuméricoTipo de datos. DATATYPE\_TREE si es árbol

2.11 Estado y Persistencia

Propiedad / EventoTipoDescripción y Uso
SaveState()MétodoGuarda el estado actual (columnas, orden, etc.) en una cadena
RestoreState(cState)MétodoRestaura el estado guardado con SaveState()
lKineticBrwLógicoActiva el desplazamiento cinético (táctil). Ej: := .T.

2.12 Tooltip de Celda

Propiedad / EventoTipoDescripción y Uso
bCellToolTipsBloqueTooltip genérico para cualquier celda. {oCol{oCol:Value, oCol:cHeader}}
cToolTipCadenaTooltip simple del browse completo. Ej: oBrw:cToolTip := 'Texto'

📌 NOTA: bCellToolTips aplica a todas las columnas, pero puede sobrescribirse por columna con oCols\[n\]:bCellToolTip

3\. Eventos del Browse (TXBrowse)

Propiedad / EventoTipoDescripción y Uso
bChangeBloqueSe ejecuta al cambiar de fila. Ej: {oCol:RefreshFooter() }
bRClickedBloqueClic derecho sobre datos. {nRow, nColShowPopup(nRow,nCol,oBrw)}
bLDClickDataBloqueDoble clic izquierdo sobre datos
bKeyDownBloqueTecla presionada. {oDlg:SetText(Str(oBrw:nColSel))}
bKeyCharBloqueCarácter tecleado. {kIf(k==VK\_RETURN, ..., nil)}
bPopUpBloqueMenú popup al hacer clic derecho. {oColMenu(o)}
bSeekBloqueBúsqueda incremental al escribir. {cDbSeek(Upper(c))}
bClrSelFocusBloqueColor de fila/celda con foco y selección activa

4\. Propiedades de la Columna (TXBrowseCol)

4.1 Identificación y Encabezado

Propiedad / EventoTipoDescripción y Uso
cHeaderCadenaTexto del encabezado. Puede incluir CRLF para multilínea. Ej: "Nombre" \+ CRLF \+ "Empleado"
nHeadStrAlignNuméricoAlineación del texto del encabezado. AL\_LEFT / AL\_CENTER / AL\_RIGHT
oHeaderFontObjeto fontFuente especial para el encabezado. Puede ser fuente vertical (NESCAPEMENT 900\)
nCreationOrderNuméricoOrden de creación de la columna. Útil con nStretchCol
nPosNuméricoPosición visible actual de la columna
nWidthNuméricoAncho de la columna en píxeles. Ej: :nWidth := 80
nArrayColNuméricoÍndice de columna en el array fuente (para fuente ARRAY)

4.2 Datos y Formato

Propiedad / EventoTipoDescripción y Uso
cEditPictureCadenaMáscara de formato/edición. Ej: :HireDate:cEditPicture := 'dd mmm yyyy'
bStrDataBloqueBloque que devuelve el texto de la celda. {\_FIELD-\>First}
bEditValueBloqueBloque que devuelve el valor editable. {\_FIELD-\>Married}
ValueVariosValor actual de la celda (lectura/escritura). oBrw:oCol("City"):Value
lDisplayZerosLógicoSi .F., no muestra ceros. Ej: oBrw:lDisplayZeros := .F.
nDataStrAlignsNuméricoAlineación de datos para TODAS las columnas a la vez (propiedad del browse)
nDataStrAlignNuméricoAlineación de datos de la columna. AL\_LEFT / AL\_CENTER / AL\_RIGHT
bDataStrAlignBloqueAlineación dinámica. {oIf(o:oBrw:aRow\[1\]==0, AL\_CENTER, AL\_LEFT)}
nDataStyleNuméricoEstilo de dibujo del texto en la celda

4.3 Colores de Columna

Propiedad / EventoTipoDescripción y Uso
bClrStdBloqueColor de la columna. Sobrescribe el bClrStd del browse. {{nTxt, nFondo}}
bClrEditBloqueColor al editar la celda. Ej: oBrw:aCols\[1\]:bClrEdit := oBrw:bClrStd
oBrushObjetoBrush (fondo imagen/color) propio de la columna
bExtendBloqueExtensión de celda (merge): {oIf(cond, nCols, 0)}. Simula fusión de celdas

4.4 Imágenes y Bitmaps en Columna

Propiedad / EventoTipoDescripción y Uso
AddBmpFile(cFile)MétodoAgrega un bitmap desde archivo a la lista de imágenes de la columna
AddResource(cRes)MétodoAgrega un bitmap desde recurso. Ej: :AddResource("GREEN")
AddBitmap(aNames)MétodoAgrega array de bitmaps. Ej: :AddBitmap({"OPEN","CLOSE","GREEN"})
nHeadBmpNoNumérico/BloqueNúmero del bitmap a mostrar en el encabezado. 1-based. Puede ser bloque
nHeadBmpAlignNuméricoAlineación del bitmap en el encabezado. AL\_LEFT / AL\_RIGHT / AL\_CENTER
nGrpBmpNoNuméricoNúmero de bitmap para el encabezado de grupo
bBmpDataBloqueBloque que devuelve el número de bitmap a mostrar en datos. {If(cond,1,2)}
nDataBmpAlignNuméricoAlineación del bitmap en datos. AL\_LEFT / AL\_CENTER / AL\_RIGHT
lBmpStretchLógicoSi .T., estira el bitmap para llenar la celda
lBmpTransparentLógicoSi .T., renderiza el bitmap con transparencia alfa
bStrImageBloqueBloque que devuelve la ruta de imagen para la celda. {oCol,oBrwoBrw:aRow\[2\]}
bAlphaLevelBloqueNivel de transparencia alfa (0-255). {oo:oBrw:aRow\[3\]}
nFootBmpNoNuméricoNúmero del bitmap a mostrar en el pie de la columna

📌 NOTA: Para imágenes transparentes tipo PNG con canal alfa, usar lBmpTransparent := .T. combinado con bAlphaLevel.

4.5 Pie de Columna (Footer)

Propiedad / EventoTipoDescripción y Uso
cFooterCadenaTexto estático del pie. Ej: oCols\[1\]:cFooter := "TOTAL"
bFooterBloqueBloque dinámico para el pie. {LTrim(Str(oTxt:RecCount()))}
nFooterTypeNuméricoTipo de agregado automático. Ver AGGR\_\* (sección 7\)
bSumConditionBloqueCondición para incluir fila en el total. {u,oo:oBrw:aRow\[1\]\>0}
bLClickFooterBloqueClic izquierdo en el pie. {r,c,f,oFooterAggrClick(r,c,f,o)}
bRClickFooterBloqueClic derecho en el pie
RefreshFooter()MétodoRefresca el pie de la columna
lTotalLógicoActiva el total automático en el pie. Ej: oCol:lTotal := .T.
nTotalNuméricoValor del total acumulado (lectura/escritura)

4.6 Edición de Columna

Propiedad / EventoTipoDescripción y Uso
nEditTypeNuméricoTipo de editor. Ver EDIT\_\* (sección 7). Ej: := EDIT\_GET
bOnPostEditBloqueSe ejecuta tras confirmar la edición. {oCol, xVal, nKey...}
bEditBlockBloqueBloque del botón de edición (EDIT\_GET\_BUTTON). {r,c,oCol...}
aEditListTxtArrayTextos del listbox de edición (EDIT\_GET\_LISTBOX)
aEditListBoundArrayValores correspondientes a los textos del listbox
cEditPictureCadenaMáscara de entrada al editar. Ej: "@K \!\!\!\!\!\!\!\!\!\!"
lAutoSaveLógicoSi .T., guarda automáticamente al salir de la celda sin necesidad de bOnPostEdit
SetCheck(aBmps, xEdit)MétodoConvierte la columna en checkbox. aBmps puede ser {"GREEN","RED"}
nBtnBmpCadenaRuta del bitmap para el botón de edición. Ej: "c:\\fwh\\bitmaps\\browse.bmp"
bPaintTextBloqueOwner-draw del texto de la celda. {oCol,hDC,cText,aCoordDrawText(...)}

4.7 Eventos de Columna

Propiedad / EventoTipoDescripción y Uso
bLClickHeaderBloqueClic izquierdo en encabezado. {r,c,f,oCol...}
bRClickHeaderBloqueClic derecho en encabezado
bLDClickDataBloqueDoble clic izquierdo en datos
blDClickDatasBloqueAlias de bLDClickData en algunos contextos
bRClickDataBloqueClic derecho en datos
bLClickFooterBloqueClic izquierdo en el pie
bRClickFooterBloqueClic derecho en el pie
bCellToolTipBloqueTooltip específico de la celda. {oColTRIM(FIELD-\>FIRST)+CRLF+TRIM(FIELD-\>LAST)}
cToolTipArrayTooltip de la columna como array {cTexto, cTítulo}. Ej: {'Col: '+cHeader,'Tip'}
bPopUpBloqueMenú contextual de la columna. {oColMenu(o)}

4.8 Anclaje (AnchorToCell)

Propiedad / EventoTipoDescripción y Uso
AnchorToCell(oDlg)MétodoPosiciona un diálogo anclado a la celda actual. Ideal para popups de búsqueda

Ejemplo

oDlg:bInit := { || oCol:AnchorToCell( oDlg ) }

5\. Métodos Principales del Browse

Propiedad / EventoTipoDescripción y Uso
CreateFromCode()MétodoFinaliza la construcción del browse. Siempre llamar al final de la configuración
SetArray(aData)MétodoEstablece o cambia el array de datos. Ej: oBrw:SetArray( aDatos )
SetRDD()MétodoConfigura el browse para usar el área RDD activa
Refresh(\[lFull\])MétodoRedibuja el browse. lFull=.T. para reconstrucción completa
SetFocus()MétodoDa el foco al browse
Edit(\[lNew\])MétodoAbre el editor para el registro actual. lNew=.T. para nuevo registro
Delete()MétodoElimina el registro actual
GoBottom()MétodoVa al último registro/fila
GoTop()MétodoVa al primer registro/fila
InsCol(nPos)MétodoInserta una columna en la posición nPos. Retorna el objeto columna
AddCol()MétodoAgrega una columna al final. Retorna el objeto columna
MoveCol(oColA, oColB)MétodoMueve la columna oColA antes de oColB
oCol(cNombre)MétodoRetorna la columna por nombre. Ej: oBrw:oCol("Salary")
GetVisibleCols()MétodoRetorna array con las columnas visibles
MakeTotals(\[oCol\])MétodoRecalcula totales de footer
DataRect()MétodoRetorna array {nTop,nLeft,nBottom,nRight} del área de datos
FontSize(nDelta)MétodoAumenta o disminuye el tamaño de fuente. Ej: :FontSize(+1)
SelFont()MétodoAbre diálogo para seleccionar fuente
SetAlign(nAlign)MétodoCambia la alineación de la columna activa
ToExcel(\[lTitle,nMode\])MétodoExporta el browse a Excel
Report(\[...\])MétodoGenera un reporte impreso o PDF del browse
SaveState()MétodoGuarda el estado del browse (columnas, orden, anchos)
RestoreState(cState)MétodoRestaura un estado previamente guardado
SelectAll()MétodoSelecciona todas las filas (modo multiselect)
SelectNone()MétodoDeselecciona todas las filas
SetBackGround(xBkg, nMode)MétodoEstablece el fondo del browse
SetGroupHeader(c, n1, n2, oF)MétodoDefine encabezado de grupo para columnas n1 a n2
SetTree(nil, aBmps)MétodoActiva modo árbol automático

6\. Acceso Dinámico a Columnas

XBrowse permite acceder a las columnas del browse por nombre directamente como propiedad del browse, gracias a la magia de \_\_GetProperty:

6.1 Por nombre (acceso directo)

oBrw:Salary:nFooterType := AGGR\_TOTAL

oBrw:Married:SetCheck( { "GREEN", "RED" }, .T. )

oBrw:HireDate:cEditPicture := 'dd mmm yyyy'

6.2 Por método oCol()

if \!Empty( oCol := oBrw:oCol( "Salary" ) )

oCol:lTotal := .T.

oCol:nEditType := EDIT\_GET

endif

6.3 Por índice en aCols

oBrw:aCols\[ 1 \]:bClrStd := {|| {CLR\_WHITE, CLR\_RED} }

oBrw:aCols\[ 2 \]:nHeadStrAlign := AL\_CENTER

6.4 Columna computada (fórmula)

// Agrega columna nueva basada en otra

oBrw:NewSalary := { || oBrw:Salary:Value \* 1.1 }

WITH OBJECT oBrw:NewSalary

:cHeader := 'NewSalary'

:nFooterType := AGGR\_TOTAL

END

6.5 ADD TO (comando)

ADD TO oBrw DATA (cAlias)-\>CITY HEADER "City" CARGO { 1, 2 }

ADD TO oBrw DATA oRs:Fields("TotSalary"):Value / oRs:Fields("Employees"):Value ;

HEADER "AVERAGE" PICTURE "999,999,999"

7\. Constantes de Referencia

7.1 MARQSTYLE\_\* — Estilo del Selector

Propiedad / EventoTipoDescripción y Uso
MARQSTYLE\_DOTCELL (1)ConstanteMarco punteado en la celda activa
MARQSTYLE\_SOLIDCELL (2)ConstanteMarco sólido en la celda activa
MARQSTYLE\_HIGHLCELL (3)ConstanteResalta la celda activa con color
MARQSTYLE\_HIGHLROWRC (4)ConstanteResalta fila y columna activa
MARQSTYLE\_HIGHLROW (5)ConstanteResalta la fila completa activa
MARQSTYLE\_HIGHLROWMS (6)ConstanteResalta fila con soporte multiselección
MARQSTYLE\_HIGHLWIN7ConstanteEstilo barra degradada Windows 7/10

7.2 LINESTYLE\_\* — Estilo de Líneas Divisorias

Propiedad / EventoTipoDescripción y Uso
LINESTYLE\_NONE (0)ConstanteSin líneas divisorias
LINESTYLE\_BLACK (1)ConstanteLíneas negras
LINESTYLE\_DARKGRAY (2)ConstanteLíneas gris oscuro
LINESTYLE\_FORECOLOR (3)ConstanteLíneas del color de texto del browse
LINESTYLE\_LIGHTGRAY (4)ConstanteLíneas gris claro
LINESTYLE\_INSET (5)ConstanteLíneas estilo inset (hundido)
LINESTYLE\_RAISED (6)ConstanteLíneas estilo raised (realzado)

7.3 STRETCHCOL\_\* — Columna Elástica

Propiedad / EventoTipoDescripción y Uso
STRETCHCOL\_NONEConstanteSin columna elástica
STRETCHCOL\_LASTConstanteLa última columna ocupa el espacio sobrante
STRETCHCOL\_WIDESTConstanteLa columna más ancha se estira
nCreationOrderNuméricoSe puede usar el nCreationOrder de una columna específica

7.4 EDIT\_\* — Tipos de Editor

Propiedad / EventoTipoDescripción y Uso
EDIT\_NONE (0)ConstanteSin edición
EDIT\_GET (1)ConstanteGET estándar (cuadro de texto)
EDIT\_LISTBOXConstanteListbox desplegable
EDIT\_GET\_LISTBOXConstanteGET combinado con listbox
EDIT\_BUTTONConstanteBotón de acción al lado del GET
EDIT\_GET\_BUTTONConstanteGET combinado con botón
TYPE\_IMAGEConstanteImagen editable en la celda

7.5 AGGR\_\* — Tipos de Agregado en Footer

Propiedad / EventoTipoDescripción y Uso
AGGR\_TOTAL / AGGR\_SUMConstanteSuma de todos los valores de la columna
AGGR\_AVERAGE / AGGR\_AVGConstantePromedio de los valores
AGGR\_MAXConstanteValor máximo
AGGR\_MINConstanteValor mínimo
AGGR\_COUNTConstanteCuenta de registros
AGGR\_STDEVConstanteDesviación estándar
AGGR\_STDEVPConstanteDesviación estándar poblacional

7.6 AL\_\* — Alineación

Propiedad / EventoTipoDescripción y Uso
AL\_LEFTConstanteAlineación izquierda
AL\_CENTERConstanteAlineación centrada
AL\_RIGHTConstanteAlineación derecha

8\. Ejemplos Completos

8.1 Browse básico sobre DBF

@ 0,0 XBROWSE oBrw OF oWnd ALIAS 'CUSTOMER' AUTOCOLS LINES CELL

oBrw:nMarqueeStyle := MARQSTYLE\_HIGHLROW

oBrw:nRowDividerStyle := LINESTYLE\_BLACK

oBrw:CreateFromCode()

oWnd:oClient := oBrw

8.2 Browse sobre Array con edición

@ 0,0 XBROWSE oBrw OF oDlg ARRAY aData AUTOCOLS LINES CELL FASTEDIT

oBrw:lHScroll := .F.

oBrw:lVScroll := .F.

AEval( oBrw:aCols, {|o| o:nEditType := EDIT\_GET} )

oBrw:CreateFromCode()

8.3 Browse con colores dinámicos

oBrw:bClrStd := {|| {CLR\_WHITE, iif(Field-\>Married, CLR\_RED, CLR\_GREEN)} }

oBrw:aCols\[10\]:bClrStd := {|| {iif(Field-\>Salary\>100000, CLR\_HRED, CLR\_HBLUE), CLR\_GRAY}}

8.4 Browse con footer y totales

WITH OBJECT oBrw:Salary

:nFooterType := AGGR\_TOTAL

:bLClickFooter := {|r,c,f,o| FooterAggrClick(r,c,f,o)}

END

oBrw:lFooter := .T.

oBrw:MakeTotals()

oBrw:CreateFromCode()

8.5 Multiselección con Windows 7 style

oBrw:nMarqueeStyle := MARQSTYLE\_HIGHLWIN7

WITH OBJECT oBrw:InsCol( 1 )

:cHeader := " "

:bEditValue := {|| If(AScan(oBrw:aSelected, oBrw:BookMark)\>0, .T., nil)}

:SetCheck()

:nHeadBmpNo := {|| If(Len(oBrw:aSelected)==oBrw:nLen, 1, 2)}

:bLClickHeader := {|r,c,f,oCol| If(Len(oBrw:aSelected)==oBrw:nLen, ;

                   oBrw:SelectNone(), oBrw:SelectAll())}

END

8.6 Group Headers con fuente bold

DEFINE FONT oBold NAME 'VERDANA' SIZE 0,-14 BOLD

oBrw:SetGroupHeader( 'EMPLOYEE' \+ CRLF \+ 'NAME', 1, 2, oBold )

oBrw:SetGroupHeader( 'ADDRESS', 3, 6, oBold )

oBrw:SetGroupHeader( 'OTHER', 8, 9, oBold )

oBrw:lAllowColReGroup := .T.

8.7 Imagen con transparencia alfa

oBrw:aCols\[ 1 \]:nEditType := TYPE\_IMAGE

oBrw:aCols\[ 1 \]:lBmpStretch := .F.

oBrw:aCols\[ 1 \]:lBmpTransparent := .T.

oBrw:aCols\[ 1 \]:bStrImage := {|oCol, oBrw| oBrw:aRow\[ 2 \]}

oBrw:aCols\[ 1 \]:nDataBmpAlign := AL\_CENTER

oBrw:aCols\[ 1 \]:bAlphaLevel := {|o| o:oBrw:aRow\[ 3 \]}

8.8 Tooltip de celda

oBrw:bCellToolTips := {|oCol| {oCol:Value, oCol:cHeader}}

oBrw:aCols\[1\]:bCellToolTip := {|oCol| TRIM(FIELD-\>FIRST) \+ CRLF \+ TRIM(FIELD-\>LAST)}

8.9 Merge de celdas (bExtend)

WITH OBJECT :aCols\[ 1 \]

:bExtend := {|o| If(o:oBrw:aRow\[1\]==0, 2, 0)} // extiende 2 cols si es subtotal

:bDataStrAlign := {|o| If(o:oBrw:aRow\[1\]==0, AL\_CENTER, AL\_LEFT)}

:bClrStd := {|| {CLR\_BLACK, If(oBrw:aRow\[1\]==0, CLR\_YELLOW, CLR\_WHITE)}}

END

8.10 Browse con fondo degradado

local aGrad := {{ .5, CLR\_BLUE, RGB(202,202,255) }, { .5, RGB(202,202,255), CLR\_BLUE }}

@ 10,10 XBROWSE oBrw ... BACKGROUND aGrad VERTICAL

// O dinámicamente:

oBrw:SetBackGround( aGrad, .T. ) // .T. \= vertical

oBrw:SetBackGround( "PAPER" ) // Recurso de imagen

oBrw:SetBackGround() // Limpiar el fondo

9\. Patrón de Uso Recomendado

El siguiente es el orden típico para configurar un XBrowse de forma completa y correcta:

  1. Definir la fuente de datos (ALIAS, ARRAY, RECSET, OBJECT)

  2. Crear el browse con @ X,Y XBROWSE ... o TXBrowse():New()

  3. Configurar propiedades del browse (estilos, colores, marquee, scroll)

  4. Configurar propiedades de columnas (encabezados, edición, colores, bitmaps)

  5. Definir group headers si aplica (SetGroupHeader)

  6. Llamar MakeTotals() si hay footers con agregados

  7. Llamar CreateFromCode() — SIEMPRE el último paso

  8. Asignar oWnd:oClient := oBrw (para ventanas)

  9. Activar la ventana o diálogo con ON INIT oBrw:SetFocus()

📌 NOTA: CreateFromCode() SIEMPRE debe ser la última llamada de configuración. Cualquier propiedad o método de configuración llamado después de CreateFromCode() puede no tener efecto.

10\. Record Selector Avanzado

El selector de registro (la columna izquierda gris) puede personalizarse completamente para mostrar números de fila, nombres, iconos y menús contextuales.

10.1 Propiedades del Record Selector

Propiedad / EventoTipoDescripción y Uso
bRecSelDataBloqueContenido de la celda del selector. {brwbrw:KeyNo} muestra número secuencial
bRecSelHeaderCadena/BloqueEncabezado del selector. Ej: oBrw:bRecSelHeader := "SlNo"
bRecSelFooterCadena/BloquePie del selector. {brwbrw:nLen} muestra total de registros
nRecSelWidthNumérico/CadenaAncho del selector. Si es cadena, se usa como modelo: "9999" → ancho para 4 dígitos
nRecSelHeadBmpNoNumérico/Cadena/ArrayBitmap en el encabezado del selector. Puede ser ruta, número o descriptor de líneas
bRecSelClickBloqueClic en el encabezado del selector. {RecSelPopUp(oBrw)}
SetRecSelBmp(xBmp)MétodoEstablece bitmap personalizado en el selector. nil=default, 0=sin bitmap, cFile=imagen
lRecordSelectorLógicoMuestra/oculta el selector completo. Default: .T.
RecSelShowRecNo()MétodoMuestra el número de registro en el selector (atajo conveniente)
SetChecks()MétodoConfigura todos los campos lógicos como checkboxes en el browse
SetMultiSelectCol()MétodoAgrega columna checkbox de multiselección automática
oMultiSelColObjetoReferencia a la columna de multiselección creada por SetMultiSelectCol()
SelectRow()MétodoRetorna .T. si la fila actual está seleccionada (para uso con bSumCondition)
oRightColObjetoColumna congelada por la DERECHA. Ej: oBrw:oRightCol := oBrw:Salary
KeyNoNuméricoNúmero secuencial de la fila actual (como propiedad de lectura del browse)

Ejemplo: Selector con número de fila

WITH OBJECT oBrw

:bRecSelData := { |brw| brw:KeyNo }

:bRecSelFooter := { |brw| brw:nLen }

:bRecSelHeader := "SlNo"

:nRecSelWidth := "9999"

END

Ejemplo: Selector con nombre de región (Array)

WITH OBJECT oBrw

:bRecSelData := { |brw| brw:aRow\[ 1 \] } // nombre en col 1

:bRecSelFooter := "Totals"

:bRecSelHeader := "Region"

:nRecSelWidth := "Americas" // ancho para el texto más largo

END

Ejemplo: Multiselección con totales condicionales

WITH OBJECT oBrw

:SetMultiSelectCol()

:bClrStd := { || { CLR\_BLACK, If( oBrw:SelectRow(), 0x88EDFB, CLR\_WHITE ) } }

:Salary:bSumCondition := { || oBrw:SelectRow() }

:Salary:nFooterType := AGGR\_SUM

:MakeTotals()

END

11\. Barra de Gets (GetBar) y Filtro Incremental

11.1 GetBar — Gets bajo encabezados

Permite mostrar campos de entrada directamente debajo de los encabezados de columna para filtrar el browse en tiempo real.

Propiedad / EventoTipoDescripción y Uso
lGetBarLógicoActiva/desactiva la barra de GETs bajo los encabezados. Ej: oBrw:lGetBar := .T.
uBarGetValVariosValor inicial del GET de la columna. Ej: oCol:uBarGetVal := Space(10)
cBarGetPicCadenaMáscara del GET en la barra. Ej: oCol:cBarGetPic := NumPict(FieldLen(n), FieldDec(n))
bClrEditsBloqueColor de los GETs de la barra. {{CLR\_BLACK, CLR\_YELLOW}}
oHeaderFontsObjetoFuente para los encabezados. Ej: oBrw:oHeaderFonts := oBold
AutoFit()MétodoAjusta automáticamente el ancho de columnas al contenido

Ejemplo

for n := 1 to Len( oBrw:aCols )

WITH OBJECT oBrw:aCols\[ n \]

  if FieldType( n ) \!= 'L'

     :uBarGetVal := uValBlank( fieldGet( n ) )

     if FieldType( n ) \== 'N'

        :cBarGetPic := NumPict( FieldLen(n), FieldDec(n) )

     endif

  END

END

next

oBrw:lGetBar := .T.

oBrw:bClrEdits := { || { CLR\_BLACK, CLR\_YELLOW } }

11.2 Filtro Incremental

Permite filtrar el browse mientras el usuario escribe, sin necesidad de presionar Enter.

Propiedad / EventoTipoDescripción y Uso
lIncrFilterLógicoActiva filtro incremental en tiempo real. Ej: oBrw:lIncrFilter := .T.
lSeekWildLógicoSi .T., filtra por contenido (contiene). Si .F., filtra por inicio (empieza con)
cFilterFldCadenaNombre del campo a filtrar. Ej: oBrw:cFilterFld := "FIRST"
cSeekCadenaTexto de búsqueda actual (lectura)
oSeekObjeto SAYControl SAY que muestra el texto de búsqueda actual
Seek(cText)MétodoEjecuta la búsqueda. Ej: oBrw:Seek("") para limpiar

Ejemplo

WITH OBJECT oBrw

:lIncrFilter := .T.

:lSeekWild := .T. // contiene (wildcard)

:cFilterFld := "FIRST"

END

// Combo para cambiar campo de filtro:

@ 10,10 COMBOBOX oBrw:cFilterFld ITEMS aHeaders ;

ON CHANGE ( oBrw:Seek(""), oBrw:SetFocus() )

12\. Barra de Botones Interna (nTopBarHeight / bOnAdjust)

XBrowse permite incorporar una barra de botones directamente dentro del propio browse, encima de los datos, sin necesidad de un ButtonBar externo.

Propiedad / EventoTipoDescripción y Uso
nTopBarHeightNuméricoAltura en píxeles de la barra interna. Ej: oBrw:nTopBarHeight := 30
bOnAdjustBloqueBloque ejecutado al crear/ajustar el browse. Aquí se crean los botones internos
EditSource(\[lNew\])MétodoAbre editor para el registro actual. lNew=.T. para nuevo. Alias de Edit()

Ejemplo completo

oBrw:nTopBarHeight := 30

oBrw:bOnAdjust := \<||

local oBtn

@ 05,05 BTNBMP oBtn FILE "new.bmp" SIZE 30,20 PIXEL OF oBrw NOBORDER ;

  ACTION oBrw:EditSource( .T. ) TOOLTIP "Agregar"

@ 05,45 BTNBMP oBtn FILE "edit.bmp" SIZE 30,20 PIXEL OF oBrw NOBORDER ;

  ACTION oBrw:EditSource() TOOLTIP "Editar"

@ 05,85 BTNBMP oBtn FILE "delete.bmp" SIZE 30,20 PIXEL OF oBrw NOBORDER ;

  ACTION oBrw:Delete() TOOLTIP "Eliminar"

return nil

\>

13\. Fuentes y Colores por Línea (Multiline avanzado)

XBrowse permite asignar fuentes y colores distintos a cada línea de texto dentro de una celda multilinea, usando las propiedades aDataFont y aClrText de la columna.

Propiedad / EventoTipoDescripción y Uso
aDataFontArray de fontsArray de objetos font por línea. La línea N usa aDataFont\[N\]. Ej: oCols\[1\]:aDataFont := {oFont1, oFont2}
aClrTextArray de coloresArray de colores de texto por línea. nil \= color default. Ej: {CLR\_HBLUE, CLR\_HRED}
nDataStrAlignNuméricoPuede combinar AL\_CENTER \+ AL\_BOTTOM para alineación vertical+horizontal
aImgRectArrayRecorte de imagen en celda: {nTop%, nLeft%, nBottom%, nRight%, cShape}. Shape: "CIRCLE", nil=rect
lFitGridHeightLógicoAjusta la altura del browse para que quepa exactamente N filas completas
nHeadStrAlignsNuméricoAlineación de TODOS los encabezados a la vez (propiedad del browse)
nWidthsArray/NuméricoSi es array, establece anchos individuales: {200, 250, 150}. Si es numérico, ancho uniforme

Ejemplo: distintas fuentes y colores por línea

DEFINE FONT aFont1\[ 1 \] NAME "VIVALDI" SIZE 0,-30

DEFINE FONT aFont1\[ 2 \] NAME "Verdana" SIZE 0,-20

WITH OBJECT oBrw:Name

:aDataFont := aFont1

:aClrText := { CLR\_HBLUE, CLR\_HRED }

END

Ejemplo: imagen recortada en forma circular

WITH OBJECT oBrw:aCols\[ 3 \]

:bStrImage := { || "c:\\images\\foto.jpg" }

:aImgRect := { nil, 0.3, 0.7, nil, "CIRCLE" }

// nil \= borde completo, 0.3 \= 30% desde izquierda, 0.7 \= 70% desde izquierda

END

14\. Herencia — Clases Derivadas de TXBrowse

Es posible crear clases derivadas de TXBrowse y TXBrwColumn para personalizar el comportamiento a nivel de clase, aplicando los cambios a todos los browses del mismo tipo automáticamente.

14.1 Crear una clase derivada de TXBrowse

CLASS MyBrowse FROM TXBrowse

CLASSDATA lRegistered AS LOGICAL // OBLIGATORIO

DATA bColClass INIT { || MyXBrCol() } // clase columna propia

METHOD New( oWnd ) CONSTRUCTOR

ENDCLASS

METHOD New( oWnd ) CLASS MyBrowse

Super:New( oWnd )

::nMarqueeStyle := MARQSTYLE\_HIGHLROWMS

::bClrStd := { || { CLR\_BLACK, RGB(255,255,206) } }

::bClrSelFocus := { || { CLR\_WHITE, CLR\_BLUE } }

::bClrRowFocus := { || { CLR\_BLACK, RGB(150,231,255) } }

return Self

14.2 Clase derivada de TXBrwColumn

CLASS MyXbrCol FROM TXBrwColumn

METHOD Adjust()

ENDCLASS

METHOD Adjust() CLASS MyXBrCol

if ::cDataType \== 'D'

  ::cEditPicture := 'dd-mmm-yyyy'

endif

Super:Adjust()

return Self

14.3 Activar la clase derivada

Propiedad / EventoTipoDescripción y Uso
CLASS MyBrowse() en comandoCláusula@ X,Y XBROWSE oBrw CLASS MyBrowse() ... — sólo ese browse usa la clase
SET XBROWSE TO MyBrowse()ComandoTodos los browses creados a partir de aquí usan MyBrowse()
SET XBROWSE TO bXBrComandoRestaura la clase anterior (guardada con SAVE PREVIOUS TO bXBr)
SetXBrowse({MyBrowse() })FunciónEquivalente programático a SET XBROWSE TO
lRegisteredCLASSDATAOBLIGATORIO en clases derivadas. Declara la clase como registrada en FWH
bColClassDATABloque que instancia la clase columna. INIT {TXBrwColumn() } por default
cDataTypeCadenaTipo de datos de la columna: 'C', 'N', 'D', 'L', 'M', 'F' (imagen)

Ejemplo con SAVE PREVIOUS

SET XBROWSE TO MyBrowse() SAVE PREVIOUS TO bXBr

// ... crear browses que usarán MyBrowse ...

SET XBROWSE TO bXBr // restaurar clase original

15\. Totales de Grupo (SetGroupTotal)

Además de los encabezados de grupo, es posible mostrar totales automáticos por grupo y un gran total consolidado.

Propiedad / EventoTipoDescripción y Uso
SetGroupTotal(cGrp, cHdr)MétodoAgrega fila de total para un grupo. cGrp \= nombre del grupo, cHdr \= texto encabezado
SetGroupTotal(aGrps, cHdr, nAggr, oFont)MétodoGrand total de múltiples grupos. nAggr \= AGGR\_SUM (default)
nFooterTypesNuméricoTipo de agregado para TODAS las columnas numéricas de una vez
RefreshFooters()MétodoRefresca todos los pies de columna
bOnChangeBloqueEvento de columna al cambiar valor. {oBrw:MakeTotals(), oBrw:RefreshFooters()}
lAllowColReGroupLógicoPermite mover columnas entre grupos. Ej: := .T.

Ejemplo completo

:SetGroupHeader( 'Americas', 2, 3, oBold )

:SetGroupHeader( 'Europe', 4, 5, oBold )

:lAllowColReGroup := .T.

:SetGroupTotal( 'Americas', 'Total' )

:SetGroupTotal( 'Europe', 'Total' )

:SetGroupTotal( { 'Americas\_Total', 'Europe\_Total' }, 'Grand' \+ CRLF \+ 'Total', AGGR\_SUM, oBold )

AEval( oBrw:aCols, { |o| o:bOnChange := { || oBrw:MakeTotals(), oBrw:RefreshFooters() } }, 2 )

16\. Control de Movimiento entre Celdas (nMoveType)

La propiedad nMoveType controla cómo se desplaza el cursor al presionar Enter o Tab después de editar una celda.

Propiedad / EventoTipoDescripción y Uso
nMoveTypeNuméricoDirección del movimiento al confirmar edición. Ver constantes MOVE\_\*
MOVE\_RIGHTConstanteMueve al cursor a la celda de la derecha (default para edición)
MOVE\_LEFTConstanteMueve al cursor a la celda de la izquierda
MOVE\_UPConstanteMueve al cursor a la celda de arriba
MOVE\_DOWNConstanteMueve al cursor a la celda de abajo
MOVE\_NONEConstanteNo mueve el cursor al confirmar
MOVE\_FAST\_RIGHTConstanteIgual que MOVE\_RIGHT pero con comportamiento rápido
MOVE\_FAST\_LEFTConstanteIgual que MOVE\_LEFT pero con comportamiento rápido
nRowSelNuméricoNúmero de fila actualmente seleccionada (visible)
nColSelNuméricoNúmero de columna actualmente seleccionada
lColChangeNotifyLógicoSi .T., dispara bChange también al cambiar de columna
lRelyOnKeyNoLógicoSi .F., usa posición física en lugar de KeyNo para posicionamiento

Ejemplo

oBrw:nMoveType := MOVE\_RIGHT // al confirmar, ir a la derecha

// o dinamicamente:

oBrw:nMoveType := MOVE\_DOWN // comportamiento tipo planilla de cálculo

17\. Propiedades Adicionales y Técnicas Avanzadas

17.1 Resaltado de Texto (FW\_SayTextHilite)

Permite resaltar palabras específicas dentro del texto de una celda con fuente y color diferente, usando bPaintText.

oBrw:aCols\[ 2 \]:bPaintText := {|oCol, hDC, cText, aRect, aColors|

FW\_SayTextHilite( hDC, cText, aRect, oFont, aColors\[1\], aColors\[2\],

  { { AllTrim(cWordHL), oBold, CLR\_HRED, CLR\_HGREEN } } )

return nil

}

📌 NOTA: FW\_SayTextHilite toma un array de {cPalabra, oFont, nClrTexto, nClrFondo} por cada palabra a resaltar.

17.2 DropFile — Arrastrar archivos sobre el browse

Propiedad / EventoTipoDescripción y Uso
DropFile(nRow, nCol, cFile)MétodoManeja el drop de un archivo sobre una celda específica del browse
lCanPasteLógicoHabilita la opción de pegar (Ctrl+V) en el browse. Ej: oBrw:lCanPaste := .T.

Ejemplo

if oBrw:DropFile( nRow, nCol, cFile )

// archivo aceptado

else

MsgInfo( 'Archivo no válido' )

endif

17.3 Generación de Código Fuente (PrgCode)

Propiedad / EventoTipoDescripción y Uso
PrgCode()MétodoGenera el código fuente PRG del browse actual. Retorna array de 4 elementos con distintos estilos
RptCode()Método (columna)Genera código de reporte para la columna
cColCadenaNombre de campo original de la columna (para generación de código)

Ejemplo

aCode := oBrw:PrgCode()

// aCode\[1\] \= estilo ListBox

// aCode\[2\] \= estilo Command

// aCode\[3\] \= estilo Oops

// aCode\[4\] \= código de reporte

17.4 Métodos de Navegación (bGoTop, bGoBottom, bSkip)

En clases derivadas o browses con fuentes de datos personalizadas, se pueden sobreescribir los bloques de navegación:

Propiedad / EventoTipoDescripción y Uso
bGoTopBloqueBloque para ir al primer registro. Ej: := {::nArrayAt := 1 }
bGoBottomBloqueBloque para ir al último registro
bSkipBloqueBloque para saltar N registros. Recibe nSkip, devuelve registros realmente saltados
bEofBloqueBloque que indica fin de datos. Ej: := {::nArrayAt \> ::nRows }
bKeyCountBloqueBloque que devuelve el total de registros. Ej: := {::nRows }
bKeyNoBloqueBloque para obtener/establecer la posición actual (KeyNo)
bBookMarkBloqueBloque para obtener/establecer el bookmark del registro actual
GoLeft(), GoRight()MétodosNavegar a columna izquierda/derecha. Sobreescribibles en clases derivadas
GoUp(\[n\]), GoDown(\[n\])MétodosNavegar filas arriba/abajo. Sobreescribibles
GetDisplayColsWidth()MétodoRetorna el ancho total de las columnas visibles
BrwWidth()MétodoRetorna el ancho del área de datos del browse
aCellCoor()MétodoRetorna coordenadas {nTop,nLeft,nBottom,nRight} de la celda activa
SetHScroll(l)MétodoMuestra/oculta scrollbar horizontal. Devuelve el nuevo estado
lAdjustedLógicoIndica si CreateFromCode() ya fue llamado (.T. después de crear)

17.5 Vista Grid/List (TXbrGrid)

Es posible crear un browse que alterne entre vista de lista (una columna por fila) y vista de cuadrícula (múltiples columnas por fila), similar a un explorador de archivos.

Propiedad / EventoTipoDescripción y Uso
lListViewLógico (ASSIGN)Activa vista de lista. Ej: oBrw:lListView := .T.
lGridViewLógico (ASSIGN)Activa vista de cuadrícula. Ej: oBrw:lGridView := .T.
CLASS TXbrGrid()ClaseClase derivada de ejemplo que implementa la vista grid. Ver xblstgrd.prg
lHeader, lFooterLógicoOcultar encabezado/pie: oBrw:lHeader := oBrw:lFooter := .F.
cDataType := 'F'CadenaTipo 'F' en la columna indica que el dato es una ruta de archivo de imagen

17.6 Arrays Irregulares (Ragged Arrays)

XBrowse puede navegar arrays irregulares (donde las filas tienen distinto número de columnas o tipos mezclados) usando XBROWSER directamente:

aData := { { 'Ragged Array' },

       { 123, Date(), 'Texto', 349.23 },

       345.26,

       date()-20 }

XBROWSER aData TITLE 'XBrowse Ragged Arrays' FASTEDIT

17.7 Clase TXBrCode — Browse de Código

Propiedad / EventoTipoDescripción y Uso
SET XBROWSE TO TXBrCode()ComandoActiva un browse especial con capacidad de generar código. Usado en el XBrowse Designer

18\. Barras de Progreso en Celdas (SetProgBar)

XBrowse permite mostrar una barra de progreso directamente dentro de una celda numérica, visualizando el valor como porcentaje de un máximo dado. Ideal para dashboards y procesos en cola.

Propiedad / EventoTipoDescripción y Uso
SetProgBar(uMax, nHeight, bClr)Método (col)Activa barra de progreso. uMax=valor máximo (número o bloque), nHeight=alto barra (nil=auto), bClr=bloque {nFill, nBack}
uMax como bloqueBloque{nMaxSal } — el máximo se recalcula dinámicamente en cada pintado
uMax como númeroNumérico100 — porcentaje fijo, el valor de la celda se toma como % de 100
RefreshCurrent()MétodoRefresca sólo la fila actual. Mucho más eficiente que Refresh() en procesos en curso
CargoVariosPropiedad de propósito general. Ej: oBrw:Cargo := .F. para señal de cancelación
bClrSelBloqueColor fila seleccionada sin foco. := :bClrSelFocus para igualar ambos estados
lDisplayZerosLógicoSi .F., celdas con valor 0 se muestran vacías. Útil al inicio de barras de progreso

Ejemplo — barra proporcional al máximo del campo

DBEval( { || nMaxSal := Max( nMaxSal, FIELD-\>Salary ) } )

WITH OBJECT oBrw:Salary

:SetProgBar( { nMaxSal },, { { RGB(161,224,255), CLR\_WHITE } } )

:nDataStrAlign := AL\_RIGHT

END

Ejemplo — barras dinámicas (proceso en cola con cancelación)

WITH OBJECT oBrw:aCols\[ 2 \]

:SetProgBar( 100,, { || { nProgClr, CLR\_WHITE } } )

:cEditPicture := "999.99 %"

:bClrSel := :bClrSelFocus := oBrw:bClrStd // sin resaltar fila activa

END

oBrw:lDisplayZeros := .F.

// Dentro del proceso:

oBrw:nArrayAt := n

oBrw:aArrayData\[ n, 2 \] := i / nTotal \* 100

oBrw:RefreshCurrent()

if oBrw:Cargo \== .F. ; EXIT ; endif // chequeo de cancelación

19\. Painting Avanzado (bPaintRow / bPaintHeader / bPaintFooter)

XBrowse expone hooks de pintado a nivel de fila, encabezado y pie completos, permitiendo control total del aspecto visual usando GDI.

19.1 bPaintRow — Pintar filas completas

Propiedad / EventoTipoDescripción y Uso
bPaintRowBloque{brw,nRow,nCol,nHt,lHL,lSel,nRowPos,nColSel,oColSel... } — hook de pintado por fila
Retorno .T.LógicoSi retorna .T., XBrowse no pinta esa fila (la pintó el bloque completamente)
Retorno .F.LógicoSi retorna .F., XBrowse pintará normalmente encima del trabajo del bloque
GetDC()MétodoObtiene el Device Context del browse para dibujar con GDI
ReleaseDC()MétodoLibera el DC obtenido con GetDC()
SayText(cText,aRect,cAlign,oFont,nClr)MétodoDibuja texto con el DC interno del browse
ReadImage(cFile)MétodoCarga imagen desde archivo. Retorna handle
DrawImage(aBmp, aRect)MétodoDibuja una imagen en el rectángulo indicado
lDrawBorderLógicoSi .T., dibuja borde alrededor del browse
nColorBoxNuméricoColor del borde del marquee. Ej: oBrw:nColorBox := CLR\_HRED
lHoverSelectLógicoSi .T., selecciona el registro al pasar el mouse (sin clic)
AddVar(cName, xVal)MétodoAgrega variable accesible dentro de los bloques como ::cName. Ej: oBrw:AddVar("oFntLarge", oLarge)
oFontObjetoFuente del browse. Accesible desde TDataRow: oRow:oBrw:oFont

Ejemplo bPaintRow

oBrw:bPaintRow := { |brw,nRow,nCol,nHt,lHL,lSel,nRowPos,nColSel,oColSel|

PaintRow( brw, nRow, nCol, nHt, lHL, lSel, nRowPos, nColSel, oColSel ) }

static function PaintRow( Self, nRow, nCol, nHeight, lHighLite, lSelect, nRowPos, nColSel, oColSel )

if Empty( ::aRow\[ 1 \] ) ; return .T. ; endif // fila vacía: no pintar

hDC := ::GetDC()

FillRectEx( hDC, { nRow,nCol,nRow+nHeight, ::aCols\[6\]:nDisplayCol }, aColor )

::SayText( ::aRow\[3\]+" : bPaintRow", { nRow,nCol,nRow+nHeight, ::BrwWidth() },

          nil, ::aCols\[1\]:oHeaderFont )

::aCols\[6\]:PaintData( nRow, nil, nHeight, lHighLite, lSelect, 6, nRowPos )

::ReleaseDC()

return .F.

19.2 bPaintHeader y bPaintFooter

Propiedad / EventoTipoDescripción y Uso
bPaintHeaderBloque{brw,hDC,aCols,nLast,hWPen,hGPen,hCPenBrwHeader(...) } — pinta el encabezado completo
bPaintFooterBloque{brw,hDC,aCols,nLast,nGridW,nBrwH,hWPen,hGPenBrwFooter(...) } — pinta el pie completo
oCol:PaintHeader(nRow,nil,nHt,lSel,hDC)Método (col)Pinta encabezado estándar de UNA columna dentro del hook personalizado
oCol:PaintFooter(nRow,nil,nHt)Método (col)Pinta pie estándar de UNA columna
oCol:PaintData(nRow,nil,nHt,lHL,lSel,n,nPos)Método (col)Pinta celda de datos de UNA columna — útil en bPaintRow
oCol:nDisplayColNuméricoPosición X izquierda de la columna (px desde borde izquierdo del browse)
oCol:oHeaderFontObjetoFuente del encabezado de la columna
oCol:oFooterFontObjetoFuente del pie de la columna
nHeadStrAlignsArrayAlineaciones del encabezado por columna: { AL\_RIGHT, AL\_LEFT, AL\_RIGHT }
nFootStrAlignsArrayAlineaciones del pie por columna
lAllowColSwappingLógicoSi .F., impide reordenar columnas arrastrando el encabezado

Ejemplo bPaintHeader con imagen y texto

oBrw:bPaintHeader := { |brw,hDC,aCols,nLast,...| BrwHeader( brw, hDC, aCols, nLast ) }

function BrwHeader( Self, hDC, aCols, nLast )

FillRect( hDC, { 1,1,::nHeaderHeight,::BrwWidth() }, ::oHeadBrush:hBrush )

::SayText( "MI EMPRESA", aRect, "T", ::oFntLarge, CLR\_HRED )

aBmp := ::ReadImage( "logo.png" ) ; ::DrawImage( aBmp, { 1,1,::nHeaderHeight-24,64 } ) ; PalBmpFree( aBmp )

for n := 1 to Len( ::aCols )

  ::aCols\[n\]:PaintHeader( ::nHeaderHeight-30, nil, 26, .F., hDC )  // enc. estándar abajo

next

return nil

19.3 bPaintText — Pintado individual de celda con GDI

oBrw:aCols\[1\]:bPaintText := { |oCol, hDC, cText, aCoors, aColors, lHighlight|

if lHighlight

  GradientFill( hDC, aCoors\[1\]+10, aCoors\[2\]+10, aCoors\[3\]-10, aCoors\[4\]-10,

     { {2/5, RGB(253,212,168), RGB(251,178,99)}, {3/5, RGB(250,157,52), RGB(252,234,163)} } )

else

  GradientFill( hDC, aCoors\[1\]+10, aCoors\[2\]+10, aCoors\[3\]-10, aCoors\[4\]-10,

     { {2/5, RGB(223,236,255), RGB(197,222,255)}, {3/5, RGB(173,209,255), RGB(189,217,255)} } )

endif

RoundBox( hDC, aCoors\[2\]+10, aCoors\[1\]+10, aCoors\[4\]-10, aCoors\[3\]-10, 20, 20, CLR\_BLUE )

DrawBitmap( hDC, hBmp, aCoors\[1\]+18, aCoors\[2\]+23 )

DrawText( hDC, cText, aCoors )

return nil }

📌 NOTA: En bPaintText: aColors\[1\]=color texto, aColors\[2\]=color fondo. lHighlight indica celda seleccionada con foco.

20\. Edición Avanzada

20.1 EDIT\_BUTTON — Editor popup personalizado

Propiedad / EventoTipoDescripción y Uso
nEditType := EDIT\_BUTTONConstante (col)Muestra botón en la celda al entrar en modo edición
bEditBlockBloque (col){nRow, nCol, oColEditFunc(nRow,nCol,oCol) } — invocado al hacer clic en el botón
bOnPostEditBloque (col){oCol, xVal, nKey... } — post-edit. xVal=nuevo valor, nKey=tecla de confirmación
EDIT\_LISTBOXConstante (col)Muestra un listbox desplegable como editor de celda
aEditListTxtArray (col)Ítems para EDIT\_LISTBOX: { {1,'uno',10}, {2,'dos',20} }
nLbxAtNumérico (col)Índice de la selección actual en aEditListTxt (sólo lectura útil en bOnChange)
bClrEditBloque (col)Color de edición para UNA columna: {{ CLR\_BLACK, CLR\_YELLOW } }
bClrEditsBloque (browse)Color de edición para TODAS las columnas del browse
bOnChangeBloque (col)Ejecutado al cambiar el valor durante la edición de esa columna
bOnChangesBloque (browse)Ejecutado cuando cualquier columna cambia. Global para todo el browse

Ejemplo EDIT\_BUTTON con popup memo

oCol:nEditType := EDIT\_BUTTON

oCol:bEditBlock := { |nRow, nCol, oCol| EditNotes( nRow, nCol, oCol ) }

oCol:bOnPostEdit := { |oCol, cVal| If( cVal \!= nil, FIELD-\>Notes := cVal, ) }

static function EditNotes( nRow, nCol, oCol )

aPoint := ClientToScreen( oCol:oBrw:hWnd, { nRow, nCol } )

DEFINE DIALOG oDlg FROM aPoint\[1\], aPoint\[2\] TO aPoint\[1\]+230, aPoint\[2\]+200 PIXEL STYLE WS\_POPUP

@ 2,2 GET oGet VAR cNotes TEXT SIZE 96,96 PIXEL OF oDlg

ACTIVATE DIALOG oDlg

return If( lEdited, cNotes, nil )

Ejemplo EDIT\_LISTBOX con nLbxAt

WITH OBJECT oBrw:Item

:nEditType := EDIT\_LISTBOX

:aEditListTxt := { {1,'uno',10}, {2,'dos',20}, {3,'tres',30} }

:bOnChange := { |oCol| oBrw:aRow\[2\] := aList\[ oCol:nLbxAt, 3 \] }

END

20.2 CurrentRow() — Edición en diálogo independiente

Propiedad / EventoTipoDescripción y Uso
CurrentRow()MétodoRetorna objeto TDataRow de la fila actual. Campos accesibles como oRow:First, oRow:Salary
oRow:Modified()MétodoRetorna .T. si hay cambios no guardados
oRow:Save()MétodoGuarda cambios del TDataRow en la fuente original
oRow:Undo()MétodoDescarta los cambios pendientes
oRow:oBrwObjetoReferencia al browse padre. Ej: oRow:oBrw:oFont, oRow:oBrw:Age:cEditPicture
ON DBLCLICKCláusula@ X,Y XBROWSE ... ON DBLCLICK RowEdit( oBrw:CurrentRow() )
DATABASE oObjComandoCrea un objeto TDatabase desde el área activa
ROWS {10,5,30}Cláusula@ X,Y XBROWSE oBrw OBJECT oDbf ROWS {10,5,30} — sólo muestra esos recnos
SelectCol(nPos, lCenter)MétodoMueve foco a columna. lCenter=.T. centra la columna en vista
ColPos(oCol)MétodoRetorna posición visible de una columna
SelectedCol()MétodoRetorna el objeto columna actualmente con foco
nCreationOrderNumérico (col)Orden de creación de la columna (no cambia al reordenar en pantalla)

Ejemplo CurrentRow con diálogo no-modal

@ 10,10 XBROWSE oBrw ... ON DBLCLICK RowEdit( oBrw:CurrentRow() )

static function RowEdit( oRow )

DEFINE DIALOG oDlg SIZE 300,200 PIXEL FONT oRow:oBrw:oFont

@ 10,60 GET oRow:First SIZE 80,12 PIXEL OF oDlg UPDATE

@ 40,60 GET oRow:Age SIZE 40,12 PIXEL OF oDlg PICTURE oRow:oBrw:Age:cEditPicture UPDATE

@ 80,78 BUTTON 'Save' WHEN oRow:Modified() ACTION oRow:Save()

@ 80,10 BUTTON 'Undo' WHEN oRow:Modified() ACTION ( oRow:Undo(), oDlg:Update() )

ACTIVATE DIALOG oDlg CENTERED NOMODAL

return nil

21\. Combo de Ordenamiento (oSortCbx / cSortOrder)

XBrowse puede vincularse automáticamente a un ComboBox externo para que el usuario cambie el orden de visualización sin código adicional.

Propiedad / EventoTipoDescripción y Uso
oSortCbxObjetoReferencia a un ComboBox externo. XBrowse lo rellena con los índices disponibles y cambia el orden al seleccionar
cSortOrderCadenaNombre del orden activo. Vinculable al VAR del ComboBox
AUTOSORTCláusulaHabilita ordenamiento por clic en encabezado de columna
nColorBoxNuméricoColor del recuadro de selección (estilo MARQSTYLE\_SOLIDCELL). Ej: CLR\_HRED
cHeadersArrayArray de encabezados asignado vía código. Ej: oBrw:cHeaders := { "Nombre", "Apellido" }
lExcelCellWiseLógicoSi .T., la exportación a Excel mantiene estructura celda a celda

Uso 1 — Combo integrado (más simple)

oBrw:nMarqueeStyle := MARQSTYLE\_SOLIDCELL

oBrw:nColorBox := CLR\_HRED

oBrw:CreateFromCode()

@ 10,20 COMBOBOX oBrw:oSortCbx VAR oBrw:cSortOrder SIZE 100,400 PIXEL OF oDlg

Uso 2 — Combo externo vinculado después

@ 10,20 COMBOBOX oCbx VAR cOrder SIZE 100,400 PIXEL OF oDlg

WITH OBJECT oBrw

:oSortCbx := oCbx

:CreateFromCode()

END

22\. Intercambiar Browses (Swap), SetRDD y TPages

22.1 Mostrar/ocultar browses alternativos

Propiedad / EventoTipoDescripción y Uso
Hide()MétodoOculta el browse sin destruirlo
Show()MétodoMuestra un browse oculto
Enable()MétodoHabilita el browse para interacción del usuario
Disable()MétodoDeshabilita el browse (sin respuesta a teclado/mouse)
SetRDD()MétodoCambia la fuente de datos RDD al área activa en ese momento. Ej: Customer-\>( oBrw:SetRDD() )
REDEFINE XBROWSEComandoVincula browse de un resource a un objeto. Ej: REDEFINE XBROWSE oBrw ID 10 OF oDlg

Ejemplo swap con tabs

aBrw\[1\]:CreateFromCode() ; aBrw\[2\]:CreateFromCode()

aBrw\[2\]:Hide() // ocultar al inicio

static function ChangeBrw( nNew, nOld, aBrw )

aBrw\[ nOld \]:Hide()

aBrw\[ nNew \]:oWnd:oClient := aBrw\[ nNew \]

aBrw\[ nNew \]:Enable() ; aBrw\[ nNew \]:Show() ; aBrw\[ nNew \]:oWnd:Resize()

return nil

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual TXBrowse
Posted: Mon Mar 09, 2026 04:12 PM

Ejemplo SetRDD desde botones

REDEFINE XBROWSE oBrw ID 10 OF oDlg AUTOCOLS ALIAS "Customer"

REDEFINE BUTTON ID 20 OF oDlg ACTION Customer-\>( oBrw:SetRDD() )

REDEFINE BUTTON ID 30 OF oDlg ACTION Clients-\>( oBrw:SetRDD() )

22.2 Browse como selector de páginas (TPages)

@ 10,20 XBROWSE oXBrw DATASOURCE { "First", "Second" } AUTOCOLS HEADERS "Page" NOBORDER

oXBrw:bChange := { || oPages:SetOption( oXBrw:BookMark ) }

oXBrw:CreateFromCode()

oPages := TPages():New( 10, 200, 380, 580, oDlg )

23\. Degradado en Filas Seleccionadas

XBrowse admite arrays de degradado en los bloques de color de selección, creando efectos visuales estilo Office/Vista.

Propiedad / EventoTipoDescripción y Uso
bClrSelFocus con aGradBloque{{ CLR\_BLACK, aSelGrad } } — degradado en la fila con foco
bClrRowFocus con aGradBloque{{ CLR\_BLACK, aRowGrad } } — degradado en filas visibles sin foco
aRowGrad (ejemplo)Array{ {.5, RGB(232,241,252), RGB(232,241,252)}, {.5, RGB(210,225,244), RGB(235,243,253)} }
aSelGrad (ejemplo)Array{ {.5, RGB(255,255,251), RGB(255,237,178)}, {.5, RGB(255,218,103), RGB(255,233,162)} }
AddBitmap(cFile)Método (col)Agrega bitmap para uso con bBmpData. Admite bitmaps alpha y no-alpha
bBmpData := {1 }Bloque (col)Índice del bitmap a mostrar. 0 \= sin bitmap
nDataBmpAlignNumérico (col)Alineación del bitmap: AL\_CENTER, AL\_LEFT, AL\_RIGHT, AL\_TOP, AL\_BOTTOM

Ejemplo completo

aRowGrad := { {.5, RGB(232,241,252), RGB(232,241,252)}, {.5, RGB(210,225,244), RGB(235,243,253)} }

aSelGrad := { {.5, RGB(255,255,251), RGB(255,237,178)}, {.5, RGB(255,218,103), RGB(255,233,162)} }

oBrw:nMarqueeStyle := MARQSTYLE\_HIGHLROWRC

oBrw:bClrSelFocus := { || { CLR\_BLACK, aSelGrad } }

oBrw:bClrRowFocus := { || { CLR\_BLACK, aRowGrad } }

// Bitmap en columna:

WITH OBJECT oBrw:HireDate

:AddBitmap( '\\fwh\\bitmaps\\Alphabmp\\task.bmp' )

:bBmpData := { || 1 }

END

24\. Imágenes Avanzadas — Imagen \+ Texto y SetColsAsRows

24.1 Imagen y texto en la misma celda

Propiedad / EventoTipoDescripción y Uso
aImgRect := { nil,nil,-40,nil }ArrayReserva 40px en la parte inferior para el texto. La imagen ocupa el resto
aImgRect := "CIRCLE"CadenaImagen en forma circular centrada en la celda
aImgRect := "ELLIPSE"CadenaImagen en forma elíptica
nCellHeightNumérico (col)Altura dedicada a esta celda dentro de una fila multilinea (con SetColsAsRows)
SetColsAsRows(aColNos)Método (col)Apila columnas en la misma celda visual en filas separadas. Ej: :SetColsAsRows( {2,3} )
oDataFontObjeto (col)Fuente del texto de datos de una columna individual
nDataStrAlign := AL\_TOPNuméricoAlinea el texto arriba de la celda
nDataStrAlign := AL\_BOTTOMNuméricoAlinea el texto abajo (útil con imagen arriba)

Ejemplo imagen memo \+ nombre abajo

WITH OBJECT oBrw:aCols\[ 1 \]

:bStrImage := { || FIELD-\>IMAGE }

:oDataFont := oBold

:nDataStrAlign := AL\_CENTER \+ AL\_BOTTOM

:nDataBmpAlign := AL\_CENTER

:aImgRect := { nil, nil, \-40, nil } // 40px reservados abajo para el texto

END

Ejemplo SetColsAsRows — nombre de archivo \+ ruta apilados

WITH OBJECT oBrw:aCols\[ 2 \]

:bEditValue := { || cFileNoPath( oBrw:aRow ) }

:oDataFont := oBold

:nCellHeight := 35

:SetColsAsRows( { 2, 3 } ) // col 2 y col 3 apiladas en la misma celda

END

WITH OBJECT oBrw:aCols\[ 3 \]

:bEditValue := { || cFilePath( oBrw:aRow ) }

:nCellHeight := 25

:nDataStrAlign := AL\_TOP

:bClrStd := { || { CLR\_GRAY, CLR\_WHITE } }

END

25\. Misceláneas: UTF-8, Excel, Estructura DBF Sincronizada

25.1 Soporte UTF-8

Propiedad / EventoTipoDescripción y Uso
UTF8TOUTF16(campo)ExpresiónConvierte texto UTF-8 a UTF-16 para mostrar correctamente en XBrowse
lExcelCellWiseLógicooBrw:lExcelCellWise := .T. — exportación a Excel celda por celda
SetFont(oFont)MétodoAsigna fuente Unicode al browse. Ej: "Segoe UI" para idiomas Unicode
nWidths como númeroNuméricooBrw:nWidths := 230 — mismo ancho para todas las columnas

Ejemplo XBROWSER con UTF-8 y grupos

XBROWSER "UTF8\_01.DBF" ;

COLUMNS "English", "Hindi", "UTF8TOUTF16(HINDI)" ;

SETUP ( oBrw:SetFont( oFont ), oBrw:nWidths := 230, ;

       oBrw:cHeaders := { "English", "Hindi (UTF-8)", "Hindi (UTF-16)" }, ;

       oBrw:lExcelCellWise := .T., ;

       oBrw:SetGroupHeader( "Indian Languages", 2, 5 ) )

25.2 Browse de hoja Excel (oRange)

oRange := GetExcelRange( cFile, "Customers", "A1:E12" )

@ 0,0 XBROWSE oBrw OF oWnd AUTOCOLS DATASOURCE oRange CELL LINES FOOTERS

oBrw:Sales:bFooter := { || oRange:Application:WorkSheetFunction:Sum( oRange:Columns(5) ) }

25.3 Dos browses sincronizados — Datos y Estructura DBF

Patrón avanzado: un browse muestra los datos y otro muestra la estructura del DBF. Al moverse en el browse de datos, el de estructura resalta el campo correspondiente y viceversa.

Propiedad / EventoTipoDescripción y Uso
bOnChanges (browse)BloqueEjecutado cuando cualquier dato cambia en el browse (post-edit global)
lColChangeNotifyLógicoSi .T., bChange se dispara también al cambiar de columna (no sólo de fila)
RefreshCurrent()MétodoRefresca sólo la fila activa — más eficiente que Refresh() completo
SelectCol(nPos, lCenter)MétodoMueve foco a columna indicada, centrándola opcionalmente

oData:bChange := { || oStru:nArrayAt := oData:SelectedCol():nCreationOrder, oStru:Refresh() }

oData:bOnChanges := { || oStru:Refresh() }

oData:lColChangeNotify := .T.

oStru:bChange := { || oData:SelectCol( oData:ColPos( oData:oCol( oStru:nArrayAt ) ), .T. ) }

// Columna 5 de oStru muestra y permite editar el valor actual del campo:

WITH OBJECT oStru:aCols\[ 5 \]

:nEditType := EDIT\_GET

:bEditValue := { |x| If( x==nil, CUST-\>( FieldGet( oStru:nArrayAt ) ), ;

                     CUST-\>( If( DbRLock(), ( FieldPut( oStru:nArrayAt, x ), DbRUnLock() ), nil ) ) ) }

:bOnChange := { || oData:RefreshCurrent() }

END

26\. Subtotales Dinámicos Insertables (Array)

Es posible insertar filas de subtotal dentro de un browse de array, calculadas mediante bloques que se reevalúan automáticamente cuando el usuario edita los datos. Las filas de subtotal se distinguen de los datos normales por un valor centinela en una columna (por ejemplo, la columna 1 vacía).

Propiedad / EventoTipoDescripción y Uso
IsTotalRow(aRow)Función propiaDetecta si la fila actual es subtotal. Ej: Empty( aRow\[1\] ) → es fila de total
bEditWhenBloque (col){\!IsTotalRow( oBrw:aRow ) } — deshabilita edición en filas de subtotal
bSumConditionBloque (col){n,o\!IsTotalRow( o:oBrw:aRow ) } — excluye filas de subtotal del total del pie
bEditValue con bloqueBloque (col)Si el valor del array es un bloque ('{...}'), se evalúa con XEval(). Útil para subtotales dinámicos
MakeSumBlock(aData,nCol,nFrom,nLast)Función propiaCrea un bloque {FW\_ArrSum(...) } que suma el rango indicado dinámicamente
AIns(aData, nPos, xVal, .T.)FunciónInserta un elemento en la posición nPos del array, desplazando los demás
FW\_ArrSum(aData, nCol)Función FWHSuma los valores de la columna nCol del array aData
bChange del browseBloque{oDlg:AEvalWhen() } — refresca las condiciones WHEN de los botones al moverse
Delete()MétodoElimina la fila actual del browse (en arrays, elimina el elemento del array)

Patrón completo de subtotales

// Marcar fila de subtotal: aRow\[1\] vacío \= es subtotal

oBrw:bClrStd := { || { CLR\_BLACK, If( IsTotalRow(oBrw:aRow), CLR\_YELLOW, CLR\_WHITE ) } }

// Columnas numéricas: no editar en subtotales, excluir del pie

WITH OBJECT oBrw:aCols\[ nCol \]

:bEditValue := { |x,o| If( x==nil .or. ValType(oBrw:aRow\[o:nArrayCol\])=='B',

                           XEval( oBrw:aRow\[o:nArrayCol\] ),

                           oBrw:aRow\[o:nArrayCol\] := x ) }

:bEditWhen := { || \!IsTotalRow( oBrw:aRow ) }

:bOnChange := { || oBrw:Refresh() }

:nFooterType := AGGR\_SUM

:bSumCondition:= { |n,o| \!IsTotalRow( o:oBrw:aRow ) }

END

// Insertar subtotal después de la fila actual:

AIns( oBrw:aArrayData, nRowLast+1, Array(Len(oBrw:aRow)), .T. )

oBrw:aArrayData\[ nRowLast+1, 2 \] := "Sum: " \+ cValToChar(nRowFrom) \+ "-" \+ cValToChar(nRowLast)

oBrw:aArrayData\[ nRowLast+1, nCol \] := MakeSumBlock( oBrw:aArrayData, nCol, nRowFrom, nRowLast )

// MakeSumBlock devuelve: { || FW\_ArrSum( aRows, nCol ) }

27\. Marca de Agua Transparente (bPaintBack / lTransparent)

XBrowse permite dibujar una imagen como marca de agua semitransparente detrás de los datos, usando el hook bPaintBack y la propiedad lTransparent.

Propiedad / EventoTipoDescripción y Uso
lTransparentLógicoSi .T., el browse usa fondo transparente (el hook bPaintBack controla el fondo)
bPaintBackBloque{Self... } — hook de pintado del fondo. Self \= el browse. Debe pintar el fondo y mantener lTransparent := .T.
hDCHandle (interno)::hDC — Device Context del browse accesible dentro de bPaintBack
oBrush:hBrushHandle::oBrush:hBrush — handle del pincel de fondo del browse
DataRect():aRectArrayRectángulo del área de datos del browse. Útil para escalar la imagen marca de agua
DrawImage(aImage, aRect, nil, nil, nAlpha)MétodoDibuja imagen con nivel de transparencia alpha (0=opaco, 255=invisible). Ej: nAlpha=64
oWnd:ReadImage(cFile,,lAlpha)MétodoLee imagen con soporte alpha (.T.). Retorna handle para usar con DrawImage
PalBmpFree(aImage)FunciónLibera memoria de imagen cargada con ReadImage

Ejemplo completo

aImage := oWnd:ReadImage( "c:\\fwh\\bitmaps\\pngs\\logo.png",, .T. ) // .T. \= con alpha

@ 20,20 XBROWSE oBrw ... DATASOURCE "CUSTOMER" AUTOCOLS NOBORDER CELL LINES

WITH OBJECT oBrw

:lTransparent := .T.

:bPaintBack := { |Self|

  FillRect( ::hDC, GetClientRect(::hWnd), ::oBrush:hBrush )  // fondo normal

  ::DrawImage( aImage, ::DataRect():aRect, nil, nil, 64 )     // 64 \= 25% opaco

  ::lTransparent := .T.  // mantener transparencia para el próximo repaint

  return nil }

:CreateFromCode()

END

// Al terminar:

PalBmpFree( aImage )

28\. XBROWSER Avanzado: CALC, Botones Internos y SETUP

El comando XBROWSER tiene opciones adicionales para mostrar browses de inicio rápido con funcionalidades extendidas.

Propiedad / EventoTipoDescripción y Uso
XBROWSER aData CALCComandoAbre XBROWSER con una hoja de cálculo editable incorporada (modo calculadora)
XBROWSER aData COLUMNS {1,2,3}ComandoMuestra sólo las columnas indicadas
XBROWSER ... SETUP codeblockComandoEjecuta un bloque de código después de crear el browse. oBrw disponible como variable
BUTTON\_PRINTConstanteÍndice del botón de impresión en la barra del XBROWSER. oBrw:oWnd:aControls\[BUTTON\_PRINT\]
BUTTON\_SHEETConstanteÍndice del botón de hoja/export en la barra del XBROWSER
oBtn:cCaptionCadenaCambia el texto del botón. Ej: oBrw:oWnd:aControls\[BUTTON\_PRINT\]:cCaption := "Mi Texto"
oBtn:bActionBloqueCambia la acción del botón. Ej: := {MsgInfo("Mi acción") }
nArrayAt()MétodoAlternativa funcional (con paréntesis) para obtener la posición actual en el array

Ejemplo XBROWSER con SETUP personalizado

XBROWSER aArray TITLE "Mi Browse" COLUMNS { 1,2,3,4,5 } ;

SETUP fSetUp( oBrw, aHeaders )

function fSetUp( oBrw, aHead )

local nI

for nI := 1 to Len( aHead )

  oBrw:aCols\[ nI \]:cHeader := aHead\[ nI \]

next

oBrw:bClrStd := { || { CLR\_BLACK, If( oBrw:nArrayAt() % 2 \== 0, CLR\_HCYAN, CLR\_LIGHTGRAY ) } }

oBrw:oWnd:aControls\[ BUTTON\_PRINT \]:cCaption := "Imprimir"

oBrw:oWnd:aControls\[ BUTTON\_PRINT \]:bAction := { || MsgInfo( "Acción propia" ) }

oBrw:oWnd:aControls\[ BUTTON\_SHEET \]:cCaption := "Exportar"

return nil

29\. Exportar Registros Seleccionados a Excel (aSelected)

XBrowse permite exportar a Excel sólo un subconjunto de registros especificando un array de números de registro en la propiedad aSelected antes de llamar ToExcel().

Propiedad / EventoTipoDescripción y Uso
aSelectedArrayArray de RecNos a exportar. Ej: oBrw:aSelected := { 10, 25, 30 }. Después de ToExcel() debe limpiarse
ToExcel()MétodoExporta los datos del browse a Excel. Si aSelected no está vacío, exporta sólo esos registros
DBEVAL(bBlock, bCond)FunciónRecorre los registros del área activa evaluando bBlock sólo cuando bCond es .T.
cAliasCadenaAlias de la fuente de datos RDD del browse. Ej: (oBrw:cAlias)-\>( DBEVAL(...) )

Ejemplo: exportar sólo registros del Estado X

// 1\. Construir array de RecNos que cumplen la condición

local aRecNo := {}

local bCondi := { || (oBrw:cAlias)-\>STATE \== cState }

(oBrw:cAlias)-\>( DBEVAL( { || AAdd( aRecNo, RECNO() ) }, bCondi ) )

// 2\. Posicionar en el primer registro que cumple y refrescar

(oBrw:cAlias)-\>( DBSeek( cState, .T. ) )

oBrw:Refresh()

// 3\. Asignar, exportar y limpiar

oBrw:aSelected := aRecNo

oBrw:ToExcel()

oBrw:aSelected := {}

Apéndice A — Ejemplos Completos de Referencia

Esta sección contiene ejemplos completos y funcionales extraídos directamente de los archivos de ejemplo de FiveWin/XBrowse. Son la referencia más directa para implementar cada característica.

A.1 Browse básico con RDD, edición y pie (testxbr3.prg)

\#include 'fivewin.ch'

\#include 'xbrowse.ch'

USE CUSTOMER NEW ALIAS CUST SHARED VIA "DBFCDX"

SET ORDER TO TAG FIRST

GO TOP

DEFINE FONT oFont NAME "TAHOMA" SIZE 0,-12

DEFINE DIALOG oDlg SIZE 700,400 PIXEL FONT oFont TITLE "XBrowse RDD"

@ 10,10 XBROWSE oBrw SIZE \-10,-10 PIXEL OF oDlg ;

DATASOURCE "CUST" AUTOCOLS AUTOSORT ;

CELL LINES NOBORDER FOOTERS

WITH OBJECT oBrw

:nEditTypes := EDIT\_GET

:Married:SetCheck()

:Salary:nFooterType := AGGR\_SUM

:nStretchCol := STRETCHCOL\_WIDEST

:MakeTotals()

:CreateFromCode()

END

ACTIVATE DIALOG oDlg CENTERED ON INIT ( CUST-\>(dbGoTop()), oBrw:SetFocus(), .F. )

A.2 Browse de Array con barra de botones interna (xbrbar.prg)

@ 20,20 XBROWSE oBrw SIZE \-20,200 PIXEL OF oDlg ;

DATASOURCE "STATES" AUTOCOLS CELL LINES NOBORDER

oBrw:nTopBarHeight := 30

oBrw:bOnAdjust := \<||

@ 05,05 BTNBMP oBtn FILE "new.bmp" SIZE 30,20 PIXEL OF oBrw NOBORDER ;

  ACTION oBrw:EditSource( .T. ) TOOLTIP "Agregar"

@ 05,45 BTNBMP oBtn FILE "edit.bmp" SIZE 30,20 PIXEL OF oBrw NOBORDER ;

  ACTION oBrw:EditSource() TOOLTIP "Editar"

@ 05,85 BTNBMP oBtn FILE "delete.bmp" SIZE 30,20 PIXEL OF oBrw NOBORDER ;

  ACTION oBrw:Delete() TOOLTIP "Eliminar"

return nil

\>

oBrw:nEditTypes := EDIT\_GET

oBrw:CreateFromCode()

A.3 Record Selector con número de fila y popup (xbrecsel.prg)

WITH OBJECT oBrw

:nHeaderHeight := 60

:SetChecks()

:bRecSelData := { |brw| brw:KeyNo }

:bRecSelFooter := { |brw| brw:nLen }

:bRecSelHeader := "SlNo"

:bRecSelClick := { || RecSelPopUp( oBrw ) }

:nRecSelWidth := "9999"

:AutoFit()

:CreateFromCode()

END

A.4 Totales de grupo con grand total (xbgrpsum.prg)

WITH OBJECT oBrw

:SetGroupHeader( 'Americas', 2, 3, oBold )

:SetGroupHeader( 'Europe', 4, 5, oBold )

:lAllowColReGroup := .T.

:SetGroupTotal( 'Americas', 'Total' )

:SetGroupTotal( 'Europe', 'Total' )

:SetGroupTotal( { 'Americas\_Total', 'Europe\_Total' }, 'Grand' \+ CRLF \+ 'Total', AGGR\_SUM, oBold )

END

AEval( oBrw:aCols, { |o|

o:cEditPicture := '99,999.99'

o:nFooterType := AGGR\_SUM

o:bOnChange := { || oBrw:MakeTotals(), oBrw:RefreshFooters() } }, 2 )

oBrw:MakeTotals()

oBrw:CreateFromCode()

A.5 Filtro incremental con wildcard (xbincflt.prg)

WITH OBJECT oBrw

:lIncrFilter := .T.

:lSeekWild := .T. // buscar 'contiene'

:cFilterFld := "FIRST"

:nStretchCol := STRETCHCOL\_WIDEST

END

@ 10,10 COMBOBOX oBrw:cFilterFld ITEMS aHdrs ;

ON CHANGE ( oBrw:Seek(""), oBrw:SetFocus() )

@ 10,70 COMBOBOX nWild ITEMS { "Empieza con", "Contiene" } ;

ON CHANGE ( oBrw:lSeekWild := (nWild==2), oBrw:Seek(""), oBrw:SetFocus() )

@ 11,160 SAY oBrw:oSeek PROMPT oBrw:cSeek SIZE 200,10 PIXEL COLOR CLR\_BLACK,CLR\_YELLOW

oBrw:CreateFromCode()

A.6 Barras de progreso dinámicas con cancelación (xbrprogd.prg)

WITH OBJECT oBrw:aCols\[ 2 \]

:SetProgBar( 100,, { || { RGB(161,224,255), CLR\_WHITE } } )

:cEditPicture := "999.99 %"

:bClrSel := :bClrSelFocus := oBrw:bClrStd

END

oBrw:lDisplayZeros := .F.

oBrw:nStretchCol := 2

oBrw:CreateFromCode()

// Proceso con actualización fila a fila:

for n := 1 to oBrw:nLen

nTotal := HB\_RandomInt( 100, 2000 )

oBrw:aArrayData\[ n, 4 \] := nTotal

oBrw:RefreshCurrent()

for i := 1 to nTotal STEP 10

  SysWait( 0.01 )

  oBrw:nArrayAt           := n

  oBrw:aArrayData\[ n, 2 \] := i / nTotal \* 100

  oBrw:aArrayData\[ n, 3 \] := i

  oBrw:RefreshCurrent()

  if oBrw:Cargo \== .F. ; EXIT ; endif

next

if oBrw:Cargo \== .F. ; EXIT ; endif

next

A.7 Marca de agua con imagen PNG alpha (xbwaterm.prg)

aImage := oWnd:ReadImage( "c:\\fwh\\bitmaps\\pngs\\logo.png",, .T. )

@ 20,20 XBROWSE oBrw ... DATASOURCE "CUSTOMER" AUTOCOLS NOBORDER CELL LINES

WITH OBJECT oBrw

:lTransparent := .T.

:bPaintBack := { |Self|

  FillRect( ::hDC, GetClientRect(::hWnd), ::oBrush:hBrush )

  ::DrawImage( aImage, ::DataRect():aRect, nil, nil, 64 )

  ::lTransparent := .T.

  return nil }

:CreateFromCode()

END

A.8 Subtotales dinámicos en array (xbsubtot.prg)

// Filas de subtotal: aRow\[1\] vacío \= es subtotal

oBrw:bClrStd := { || { CLR\_BLACK, If( Empty(oBrw:aRow\[1\]), CLR\_YELLOW, CLR\_WHITE ) } }

WITH OBJECT oBrw:aCols\[ nCol \]

:bEditValue := { |x,o| If( x==nil .or. ValType(oBrw:aRow\[o:nArrayCol\])=='B',

                           XEval( oBrw:aRow\[o:nArrayCol\] ),

                           oBrw:aRow\[o:nArrayCol\] := x ) }

:bEditWhen := { || \!Empty( oBrw:aRow\[1\] ) }

:bSumCondition := { |n,o| \!Empty( o:oBrw:aRow\[1\] ) }

:bOnChange := { || oBrw:Refresh() }

:nFooterType := AGGR\_SUM

END

// Insertar la fila de subtotal:

AIns( oBrw:aArrayData, nRowLast+1, Array(Len(oBrw:aRow)), .T. )

oBrw:aArrayData\[nRowLast+1, nCol\] := { || FW\_ArrSum( aRows, nCol ) } // bloque dinámico

A.9 Exportar registros filtrados a Excel (xbxlsel.prg)

// Construir array de RecNos que cumplen la condición

local aRecNo := {}

(oBrw:cAlias)-\>( DBEVAL( { AAdd( aRecNo, RECNO() ) }, { STATE \== cState } ) )

(oBrw:cAlias)-\>( DBSeek( cState, .T. ) )

oBrw:Refresh()

// Exportar sólo esos registros

oBrw:aSelected := aRecNo

oBrw:ToExcel()

oBrw:aSelected := {}

A.10 Clase derivada con columna y comportamiento propio (xbrchild.prg)

CLASS MyBrowse FROM TXBrowse

CLASSDATA lRegistered AS LOGICAL

DATA bColClass INIT { || MyXBrCol() }

METHOD New( oWnd ) CONSTRUCTOR

ENDCLASS

METHOD New( oWnd ) CLASS MyBrowse

Super:New( oWnd )

::nMarqueeStyle := MARQSTYLE\_HIGHLROWMS

::bClrStd := { || { CLR\_BLACK, RGB(255,255,206) } }

::bClrSelFocus := { || { CLR\_WHITE, CLR\_BLUE } }

::bClrRowFocus := { || { CLR\_BLACK, RGB(150,231,255) } }

return Self

CLASS MyXbrCol FROM TXBrwColumn

METHOD Adjust()

ENDCLASS

METHOD Adjust() CLASS MyXBrCol

if ::cDataType \== 'D'

  ::cEditPicture := 'dd-mmm-yyyy'

endif

Super:Adjust()

return Self

// Uso puntual:

@ 10,10 XBROWSE oBrw CLASS MyBrowse() OF oDlg ALIAS 'SALES' AUTOCOLS SIZE 200,70 PIXEL

// Uso global:

SET XBROWSE TO MyBrowse() SAVE PREVIOUS TO bXBr

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1710
Joined: Tue Oct 28, 2008 06:26 PM
Re: Manual TXBrowse
Posted: Tue Mar 10, 2026 10:40 AM

Excelente!!

Muchas gracias Antonio y Carlos

Saludos,



Adhemar C.
Posts: 3358
Joined: Fri Oct 07, 2005 08:20 PM
Re: Manual TXBrowse
Posted: Wed Mar 11, 2026 03:38 AM

Amigos:

Ardua labora la de Carlos.

Muchas gracias por el aporte

SOI, s.a. de c.v.
estbucarm@gmail.com
http://www.soisa.mex.tl/
http://sqlcmd.blogspot.com/
Tel. (722) 174 44 45
Carpe diem quam minimum credula postero
Posts: 163
Joined: Thu Mar 16, 2017 04:08 PM
Re: Manual TXBrowse
Posted: Thu Mar 12, 2026 04:45 PM

Estimados:

Gran Aporte, Xbrowse es muy potente.

Estoy probando el "Filtro Incremental", me funciona bien y rápido, tengo dos consultas.

  1. ¿Como hgo para que aparezca debajo de la cabecera del xBrowse un ombo para selección o un Get para escribir el filtro?
  2. ¿Como se resetea el filtro, ya intento con esto al dar click en la cabecera y no me funciona: oBrw:Seek(IF( oBrw:lSeekWild, oBrw:cSeek, "" )?

Saludos

Fernando Espinoza

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual TXBrowse
Posted: Thu Mar 12, 2026 05:11 PM

Estimado Fernando,

Pregunta 1: Mostrar un Combo o Get debajo de la cabecera del XBrowse

XBrowse tiene dos propiedades integradas para esto: lSeekBar y lGetBar. Ambas muestran una barra debajo de la cabecera del browse para escribir el filtro/búsqueda.

  • lSeekBar (nBarHdr == 2): Muestra un SAY que refleja lo que se va escribiendo (búsqueda incremental por teclado). 0-cite-0

  • lGetBar (nBarHdr == 1): Muestra un GET editable debajo de la cabecera donde se puede escribir directamente el filtro. 0-cite-1

Ejemplo con lSeekBar:

Code (prg): Select all Collapse
oBrw:lSeekBar      := .t.
oBrw:lSeekWild     := .t.
oBrw:bClrEdits     := { || { CLR_HRED, CLR_YELLOW } }
``` [0-cite-2](#0-cite-2) 

Ejemplo con `lGetBar` (el GET se conecta automáticamente al Seek del browse via `Check_oSeek()`): [0-cite-3](#0-cite-3) 

Si prefieres un enfoque manual con combos (como hace `xbrowser.prg` con `lAutoSort`), puedes colocar controles encima del browse ajustando `oBrw:nTop`:

```prg
nRow        := oBrw:nTop
oBrw:nTop   += 14

@ nRow - 1, 10 COMBOBOX nFilter SIZE 60, 14 PIXEL OF oDlg ;
   ITEMS { "Search For", "Show Only" } ;
   ON CHANGE ( oBrw:Seek( "" ), oBrw:lIncrFilter := nFilter > 1, oBrw:SetFocus() )

@ nRow, 135 SAY oBrw:oSeek PROMPT oBrw:cSeek ;
   SIZE 100, 10 PIXEL UPDATE OF oDlg ;
   COLOR CLR_RED, CLR_YELLOW
``` [0-cite-4](#0-cite-4) 

## Pregunta 2: Resetear el filtro incremental

La forma correcta de resetear el filtro incremental es:

```prg
oBrw:Seek( "" )

No uses oBrw:Seek( IF( oBrw:lSeekWild, oBrw:cSeek, "" ) ) porque eso solo pasa "" cuando lSeekWild es .f., y cuando es .t. pasa el valor actual de cSeek (que no resetea nada).

Cuando se llama oBrw:Seek(""), internamente el método BrwSeek de la fuente de datos detecta que cSeek está vacío y limpia el filtro con SetFilter(""): 0-cite-5 0-cite-6 0-cite-7

Los samples oficiales confirman este patrón. Por ejemplo, al presionar ESC o al cerrar el diálogo:

Code (prg): Select all Collapse
:bKeyChar := { |nKey| If( nKey == VK_ESCAPE, ( oBrw:Seek( "" ), oDlg:End() ), nil ) }
``` [0-cite-8](#0-cite-8) 

Y en el `VALID` del diálogo:
```prg
VALID ( oBrw:Seek( "" ), .t. )
``` [0-cite-9](#0-cite-9) 

Si `oBrw:Seek("")` no te funciona al hacer click en la cabecera, verifica que:

1. El `bSeek` del browse esté correctamente asignado (se asigna automáticamente con `SetRDD()`, `SetArray()`, etc.)
2. Después del `Seek("")` llames `oBrw:Refresh()` y `oBrw:SetFocus()` para asegurar que se repinte:

```prg
oBrw:Seek( "" )
oBrw:Refresh()
oBrw:SetFocus()

Si estás usando el click en la cabecera para resetear, un ejemplo sería:

Code (prg): Select all Collapse
oBrw:bLClickHeader := { |nRow, nCol, nFlags, oCol| oBrw:Seek( "" ), oBrw:Refresh(), oBrw:SetFocus() }
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual TXBrowse
Posted: Thu Mar 12, 2026 05:14 PM

Aqui van dos ejemplos completos, uno con lSeekBar y otro con lGetBar, ambos con filtro incremental y reset al hacer click en la cabecera.

Ejemplo 1: Con lSeekBar (SAY que muestra lo que se escribe)

Code (prg): Select all Collapse
#include "FiveWin.ch"
#include "XBrowse.ch"

REQUEST DBFCDX

function Main()

   local oDlg, oBrw

   USE CUSTOMER NEW SHARED VIA "DBFCDX"
   SET ORDER TO TAG FIRST
   GO TOP

   DEFINE DIALOG oDlg SIZE 600, 400 PIXEL TITLE "Ejemplo lSeekBar + Filtro Incremental"

   @ 10, 10 XBROWSE oBrw SIZE -10, -10 PIXEL OF oDlg ;
      DATASOURCE Alias() ;
      COLUMNS "First", "Last", "City" ;
      AUTOSORT CELL LINES

   WITH OBJECT oBrw
      :lSeekBar      := .t.                              // SAY debajo de la cabecera
      :lIncrFilter   := .t.                              // Filtro incremental (no solo buscar, sino filtrar)
      :lSeekWild     := .t.                              // Busqueda tipo "Containing" (*texto*)
      :bClrEdits     := { || { CLR_HRED, CLR_YELLOW } }  // Colores del SeekBar

      // Reset del filtro al hacer click en la cabecera
      :bLClickHeader := { |nRow, nCol, nFlags, oCol| ;
                           oBrw:Seek( "" ), ;
                           oBrw:Refresh(), ;
                           oBrw:SetFocus() }

      // Reset del filtro al presionar ESC
      :bKeyChar      := { |nKey| If( nKey == VK_ESCAPE, ;
                           ( oBrw:Seek( "" ), oBrw:Refresh() ), nil ) }

      :CreateFromCode()
   END

   ACTIVATE DIALOG oDlg CENTERED ;
      ON INIT oBrw:SetFocus() ;
      VALID ( oBrw:Seek( "" ), .t. )                     // Reset al cerrar

return nil

lSeekBar muestra un SAY debajo de la cabecera que refleja lo que el usuario va tecleando directamente sobre el browse. No necesita un control externo: simplemente se escribe sobre el browse y el SAY muestra el texto de búsqueda. 1-cite-0


---

Ejemplo 2: Con lGetBar (GET editable debajo de la cabecera)

Code (prg): Select all Collapse
#include "FiveWin.ch"
#include "XBrowse.ch"

REQUEST DBFCDX

function Main()

   local oDlg, oBrw

   USE CUSTOMER NEW SHARED VIA "DBFCDX"
   SET ORDER TO TAG FIRST
   GO TOP

   DEFINE DIALOG oDlg SIZE 600, 400 PIXEL TITLE "Ejemplo lGetBar + Filtro Incremental"

   @ 10, 10 XBROWSE oBrw SIZE -10, -10 PIXEL OF oDlg ;
      DATASOURCE Alias() ;
      COLUMNS "First", "Last", "City" ;
      AUTOSORT CELL LINES

   WITH OBJECT oBrw
      :lGetBar       := .t.                              // GET editable debajo de la cabecera
      :lIncrFilter   := .t.                              // Filtro incremental
      :lSeekWild     := .t.                              // Busqueda tipo "Containing"
      :bClrEdits     := { || { CLR_HRED, CLR_YELLOW } }

      // Reset del filtro al hacer click en la cabecera
      :bLClickHeader := { |nRow, nCol, nFlags, oCol| ;
                           oBrw:Seek( "" ), ;
                           oBrw:Refresh(), ;
                           oBrw:SetFocus() }

      // Reset del filtro al presionar ESC
      :bKeyChar      := { |nKey| If( nKey == VK_ESCAPE, ;
                           ( oBrw:Seek( "" ), oBrw:Refresh() ), nil ) }

      :CreateFromCode()
   END

   ACTIVATE DIALOG oDlg CENTERED ;
      ON INIT oBrw:SetFocus() ;
      VALID ( oBrw:Seek( "" ), .t. )

return nil

lGetBar muestra un GET editable debajo de la cabecera de la columna activa. El usuario puede hacer click en el GET y escribir directamente. El método Check_oSeek() conecta automáticamente el GET con oBrw:Seek() via bChange: 1-cite-1 1-cite-2


---

Diferencias clave

CaracterísticalSeekBarlGetBar
nBarHdr21
Tipo de controlSAY (solo muestra)GET (editable, con cursor)
InteracciónSe escribe directamente sobre el browseSe hace click en el GET y se escribe ahí
Conexión automáticaMuestra cSeekCheck_oSeek() conecta bChange del GET con Seek()

En ambos casos, el reset se hace con oBrw:Seek(""), que internamente limpia el filtro llamando SetFilter("") en la fuente de datos. 1-cite-3 1-cite-4

Los samples oficiales yunus.prg y mariainv.prg usan exactamente este patrón de oBrw:Seek("") para resetear al cerrar el diálogo o al presionar ESC. 1-cite-5 1-cite-6

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 163
Joined: Thu Mar 16, 2017 04:08 PM
Re: Manual TXBrowse
Posted: Thu Mar 12, 2026 06:22 PM

Estimado Antonio:

Realice la prueba con lSeekBar; a su código le realice un agregado:

:cFilterFld := "FIELD" // para que filtre por el campo FIELD

Y funciona la mayoría, incluyendo el reseteo (va muy bien); lo único que no me funciona es que se muestre el texto que se va escribiendo en el SAY que se inserta, no se si será problema de este metodo o algo más:

:bClrEdits := { || { CLR_HRED, CLR_YELLOW } }

No funciona; estoy con FW2512.

Saludos

Fernando Espinoza A.

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual TXBrowse
Posted: Thu Mar 12, 2026 07:28 PM

A ver si esto te sirve:

El problema que describes -- el filtro funciona pero el texto no se muestra en el SeekBar -- es un tema conocido relacionado con cómo se pinta la barra debajo de la cabecera.

Causa probable

Cuando lSeekBar está activo (nBarHdr == 2), el XBrowse pinta una barra debajo de la cabecera donde muestra el texto de cSeek. El método PaintHeader primero rellena toda el área del header (incluyendo la barra) con el brush del header: 2-cite-0

Luego, para cada columna, llama a oCol:CheckBarGet(.t.) que es el responsable de crear/mostrar el control SAY en la barra con los colores de bClrEdits: 2-cite-1

El estilo l2007 (que está activo por defecto con n2KStyle == 2007) aplica un gradiente sobre el header que puede cubrir el área de la barra, haciendo que el texto sea invisible aunque bClrEdits esté configurado. 2-cite-2 2-cite-3

Soluciones a probar

Opción 1: Cambiar a estilo flat, que no aplica gradiente sobre la barra:

Code (prg): Select all Collapse
oBrw:lFlatStyle := .t.
``` [2-cite-4](#2-cite-4) 

**Opción 2**: Usar `lGetBar` en lugar de `lSeekBar`. El `lGetBar` crea un GET editable real (no un SAY pintado), que tiene su propia ventana y no se ve afectado por el gradiente del header:

```prg
oBrw:lGetBar       := .t.    // en lugar de lSeekBar
oBrw:bClrEdits     := { || { CLR_HRED, CLR_YELLOW } }
``` [2-cite-5](#2-cite-5) 

**Opción 3**: Crear un SAY externo asignado a `oBrw:oSeek` (como hacen los samples oficiales `yunus.prg`, `mariainv.prg` y `xbrowser.prg`). Este enfoque no depende de la barra interna:

```prg
// Después de CreateFromCode():
@ nRow, nCol SAY oBrw:oSeek PROMPT oBrw:cSeek PICTURE "@!" ;
   SIZE 200, 14 PIXEL OF oDlg ;
   COLOR CLR_HRED, CLR_YELLOW
``` [2-cite-6](#2-cite-6) [2-cite-7](#2-cite-7) 

**Opción 4**: Si quieres mantener `lSeekBar` con estilo 2007, prueba desactivar el gradiente completo:

```prg
oBrw:lGradientComplete := .f.
oBrw:lSeekBar          := .t.
oBrw:bClrEdits         := { || { CLR_HRED, CLR_YELLOW } }
``` [2-cite-2](#2-cite-2) 

## Ejemplo completo con Opción 3 (SAY externo, más confiable)

```prg
#include "FiveWin.ch"
#include "XBrowse.ch"

REQUEST DBFCDX

function Main()

   local oDlg, oBrw

   USE CUSTOMER NEW SHARED VIA "DBFCDX"
   SET ORDER TO TAG FIRST
   GO TOP

   DEFINE DIALOG oDlg SIZE 600, 400 PIXEL TITLE "lSeekBar + Filtro Incremental"

   @ 30, 10 XBROWSE oBrw SIZE -10, -10 PIXEL OF oDlg ;
      DATASOURCE Alias() ;
      COLUMNS "First", "Last", "City" ;
      AUTOSORT CELL LINES

   WITH OBJECT oBrw
      :lIncrFilter   := .t.
      :lSeekWild     := .t.
      :cFilterFld    := "FIRST"

      :bLClickHeader := { |nRow, nCol, nFlags, oCol| ;
                           oBrw:Seek( "" ), ;
                           oBrw:Refresh(), ;
                           oBrw:SetFocus() }

      :bKeyChar      := { |nKey| If( nKey == VK_ESCAPE, ;
                           ( oBrw:Seek( "" ), oBrw:Refresh() ), nil ) }

      :CreateFromCode()
   END

   // SAY externo para mostrar el texto de búsqueda
   @ 14, 10 SAY oBrw:oSeek PROMPT oBrw:cSeek PICTURE "@!" ;
      SIZE 200, 14 PIXEL OF oDlg UPDATE ;
      COLOR CLR_HRED, CLR_YELLOW

   ACTIVATE DIALOG oDlg CENTERED ;
      ON INIT oBrw:SetFocus() ;
      VALID ( oBrw:Seek( "" ), .t. )

return nil

El sample oficial fwfuncs.prg usa lSeekBar con bClrEdits y funciona, pero ese sample usa SetArray() como datasource, no RDD con cFilterFld. Es posible que haya una diferencia de comportamiento específica con FW2512 cuando se combina lSeekBar + lIncrFilter + cFilterFld con RDD. 2-cite-8

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 163
Joined: Thu Mar 16, 2017 04:08 PM
Re: Manual TXBrowse
Posted: Fri Mar 13, 2026 03:29 AM

Estimado Antonio:

Hice la prueba con la opción FLAT, el problema se mantiene.

saludos

Fernando Espinoza A.

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Manual TXBrowse
Posted: Mon Mar 23, 2026 07:11 AM

Estimado Fernando,

Arreglado y funcionando bien! :) Incluido en el próximo FWH 26.03

* Fix: XBrowse lSeekBar + lIncrFilter: Multiple fixes for incremental filter
with SeekBar display:

  • Fix: oFilterCol was never assigned, so the SeekBar SAY was never painted
    when lIncrFilter was active. Now auto-assigned from SelectedCol() on first Seek.

  • Fix: SeekBar text was painted on screen DC instead of the double-buffer DC,
    causing it to be overwritten by DispEnd(). ShowSeek() now uses the active
    hDC (buffer or screen) and draws text with DrawText/SetTextColor directly.

  • Fix: Seek(nil) called from GoUp/GoDown/navigation methods was clearing
    cSeek even during active incremental filtering, erasing the search text.
    Now preserves cSeek when lIncrFilter is active.

  • Fix: Recursive Seek("") calls during Refresh()/Change() were clearing the
    search state. Added static recursion guard in Seek() for lIncrFilter mode.

  • Fix: Search icon (zoom2 bitmap) was not drawn with transparent background.
    Changed from DrawBitmap to DrawTransparent for proper alpha rendering.

  • New sample: samples/ui/xbrowse/xbrseek.prg - Demonstrates lSeekBar with
    incremental filtering (lIncrFilter + lSeekWild) using a DBF data source.
    Shows colored SeekBar (bClrEdit), ESC to reset filter, and AUTOSORT.

- bClrEdit es propiedad de cada columna, no del browse

  • ShowSeek necesitaba pintar en el buffer DC, no en pantalla
  • Seek(nil) desde la navegación borraba el filtro activo
  • Recursión de Seek("") durante Refresh() limpiaba el estado
  • lFastEdit capturaba las teclas antes de llegar al SeekBar
  • oFilterCol nunca se inicializaba para lIncrFilter
regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion