Manual TXBrowse

Post Reply
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Manual TXBrowse

Post by Antonio Linares »

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, ;

Code: Select all | Expand

                   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 \]

Code: Select all | Expand

  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 ;

Code: Select all | Expand

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

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

Code: Select all | Expand

  ACTION oBrw:EditSource() TOOLTIP "Editar"

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

Code: Select all | Expand

  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'

Code: Select all | Expand

  ::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\],

Code: Select all | Expand

  { { 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' },

Code: Select all | Expand

       { 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() },

Code: Select all | Expand

          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 )

Code: Select all | Expand

  ::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

Code: Select all | Expand

  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

Code: Select all | Expand

  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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual TXBrowse

Post by Antonio Linares »

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, ;

Code: Select all | Expand

       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 ) ), ;

Code: Select all | Expand

                     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',

Code: Select all | Expand

                           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|

Code: Select all | Expand

  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 )

Code: Select all | Expand

  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 ;

Code: Select all | Expand

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

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

Code: Select all | Expand

  ACTION oBrw:EditSource() TOOLTIP "Editar"

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

Code: Select all | Expand

  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

Code: Select all | Expand

  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|

Code: Select all | Expand

  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',

Code: Select all | Expand

                           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'

Code: Select all | Expand

  ::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
User avatar
acuellar
Posts: 1713
Joined: Tue Oct 28, 2008 6:26 pm
Location: Santa Cruz-Bolivia

Re: Manual TXBrowse

Post by acuellar »

Excelente!!

Muchas gracias Antonio y Carlos

Saludos,

Adhemar Cuéllar

User avatar
Armando
Posts: 3382
Joined: Fri Oct 07, 2005 8:20 pm
Location: Toluca, México
Contact:

Re: Manual TXBrowse

Post by Armando »

Amigos:

Ardua labora la de Carlos.

Muchas gracias por el aporte

SOI, s.a. de c.v.
[email protected]
http://www.soisa.mex.tl/
http://sqlcmd.blogspot.com/
Tel. (722) 174 44 45
Carpe diem quam minimum credula postero
nanoespinoza
Posts: 163
Joined: Thu Mar 16, 2017 4:08 pm
Location: Ecuador

Re: Manual TXBrowse

Post by nanoespinoza »

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

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual TXBrowse

Post by Antonio Linares »

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: Select all | Expand

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: Select all | Expand

: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: Select all | Expand

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

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual TXBrowse

Post by Antonio Linares »

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: Select all | Expand

#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: Select all | Expand

#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
nanoespinoza
Posts: 163
Joined: Thu Mar 16, 2017 4:08 pm
Location: Ecuador

Re: Manual TXBrowse

Post by nanoespinoza »

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.

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual TXBrowse

Post by Antonio Linares »

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: Select all | Expand

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
nanoespinoza
Posts: 163
Joined: Thu Mar 16, 2017 4:08 pm
Location: Ecuador

Re: Manual TXBrowse

Post by nanoespinoza »

Estimado Antonio:

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

saludos

Fernando Espinoza A.

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: Manual TXBrowse

Post by Antonio Linares »

Estimado Fernando,

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

Image

* 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
Post Reply