PowerBuilder

PowerBuilder 2025 R2: inspección dinámica de DataWindows con GetDWObject

AutorLuis Avilan
Publicado
PowerBuilder 2025 R2: inspección dinámica de DataWindows con GetDWObject, artículo de Luis Avilan
PB

0 visualizaciones

Cómo construir un inspector de metadatos en tiempo de ejecución combinando Describe, GetDWObject, DWObject, Modify, un DataStore compartido y exportación JSON UTF-8.

Detalles

Introducción

Un DataWindow no es únicamente un contenedor de filas. Su definición contiene un árbol de objetos visuales y funcionales: columnas, textos, campos calculados, botones y otros controles. Cada elemento posee identidad, tipo, banda, geometría, visibilidad y propiedades especializadas. Normalmente esa información queda implícita en el Painter; sin embargo, PowerBuilder también permite consultarla y modificarla durante la ejecución.

El proyecto Demo_GetDWObject convierte esa capacidad en una herramienta visible. El usuario puede enumerar los objetos de un DataWindow, filtrarlos por tipo, seleccionar uno por nombre, recuperar sus propiedades, localizarlo mediante un resaltado temporal, exportar el resultado a JSON y ejecutar una validación básica del diseño. Todo funciona con datos locales, sin una conexión a base de datos.

El valor técnico del demo no reside en una sola llamada. Presenta un flujo completo: Describe("DataWindow.Objects") descubre los nombres, GetDWObject obtiene el objeto tipado, el NVO normaliza sus propiedades, Modify aplica un cambio visual reversible y FileWriteEx entrega un artefacto JSON en UTF-8.

Ventana principal del demo: inventario de objetos, DataWindow de origen, propiedades normalizadas y representación JSON del objeto seleccionado.

El problema: inspeccionar una definición que solo se conoce en ejecución

Una referencia directa como dw_origen.Object.saldo funciona cuando el desarrollador conoce el nombre durante la compilación. No resuelve herramientas genéricas de soporte, documentación o auditoría donde el objeto llega como texto, cambia entre DataWindows o debe ser seleccionado por el usuario.

El demo separa el problema en tres operaciones complementarias:

Ventaja del enfoque dinámico

1. Enfoque acoplado: referencias codificadas en cada ventana

Una implementación convencional podría escribir condiciones específicas para saldo, nombre_cliente o total_saldos. Esa solución sirve para un único DataWindow, pero obliga a recompilar cuando cambia el diseño y duplica lógica en los eventos visuales. También dificulta construir utilidades transversales.

2. Enfoque reutilizable: nombre, tipo y propiedades en tiempo de ejecución

El NVO nvo_inspector_dw recibe un control DataWindow y un nombre. La ventana no necesita conocer cómo se inspecciona una columna frente a un campo calculado. El NVO determina el tipo y devuelve filas normalizadas con tres atributos: propiedad, valor y categoría.

3. Comparación arquitectónica

Arquitectura del demo

El proyecto mantiene las responsabilidades bien delimitadas:

La ventana conserva dos referencias de instancia: inv_inspector para el servicio y ids_propiedades para el DataStore. Además guarda el nombre resaltado, su color anterior y el modo de fondo anterior, necesarios para una restauración segura.

Flujo de ejecución completo

• El evento open crea nvo_inspector_dw y un DataStore.

• Asigna d_origen_inspeccion al control origen y d_propiedades_objeto a la cuadrícula de resultados y al DataStore.

• ShareData conecta el DataStore con dw_propiedades.

• La ventana inserta tres clientes de demostración, incluyendo nombres con acentos.

• El NVO enumera los objetos y aplica el filtro seleccionado.

• Una selección o clic dentro del DataWindow entrega el nombre a of_inspeccionar_objeto.

• El NVO carga propiedades; la ventana actualiza la tabla, el JSON y la línea de estado.

• El usuario puede resaltar, exportar o validar el diseño.

• El evento close restaura cualquier resaltado y destruye los objetos creados.

Paso 1: inicializar un demo independiente de la base de datos

La ventana construye la infraestructura en memoria. El DataWindow de origen posee definición de columnas, pero las filas se insertan localmente; el DataStore de resultados utiliza un DataWindow externo como esquema.

1event open;
2inv_inspector = Create nvo_inspector_dw
3ids_propiedades = Create DataStore
4
5dw_origen.DataObject = "d_origen_inspeccion"
6dw_propiedades.DataObject = "d_propiedades_objeto"
7ids_propiedades.DataObject = "d_propiedades_objeto"
8ids_propiedades.ShareData(dw_propiedades)
9
10ddlb_tipo_objeto.SelectItem(1)
11of_cargar_datos_demostracion()
12of_actualizar_lista_objetos()
13of_inspeccionar_objeto()
14end event

Paso 2: descubrir los objetos de la definición activa

DataWindow.Objects devuelve nombres separados por tabuladores. of_obtener_nombres recorre esa cadena sin asumir cuántos objetos contiene, obtiene cada DWObject, consulta su tipo y agrega únicamente los que coinciden con el filtro.

1ls_objetos = adw_origen.Describe("DataWindow.Objects")
2
3Do While Len(ls_objetos) > 0
4 ll_posicion = Pos(ls_objetos, "~t")
5 If ll_posicion > 0 Then
6 ls_nombre = Left(ls_objetos, ll_posicion - 1)
7 ls_objetos = Mid(ls_objetos, ll_posicion + 1)
8 Else
9 ls_nombre = ls_objetos
10 ls_objetos = ""
11 End If
12
13 ldwo_objeto = of_obtener_objeto(adw_origen, ls_nombre)
14 If Not IsNull(ldwo_objeto) Then
15 If of_coincide_tipo(of_obtener_tipo(ldwo_objeto), as_filtro) Then
16 ll_total++
17 as_nombres[ll_total] = ls_nombre
18 End If
19 Destroy ldwo_objeto
20 End If
21Loop

Los filtros de interfaz en español se traducen a tipos técnicos: columnas a column, campos calculados a compute y textos a text. La opción controles recoge cualquier tipo que no pertenezca a esos tres grupos; en el DataWindow de muestra incluye boton_actualizar.

Paso 3: obtener y validar el DWObject

El acceso se concentra en of_obtener_objeto. Antes de llamar a GetDWObject, valida el control, elimina espacios del nombre y rechaza cadenas vacías. El llamador siempre comprueba IsNull antes de leer propiedades.

1public function dwobject of_obtener_objeto (datawindow adw_origen, string as_nombre);
2DWObject ldwo_objeto
3String ls_nombre
4
5If Not IsValid(adw_origen) Then Return ldwo_objeto
6ls_nombre = Trim(as_nombre)
7If Len(ls_nombre) = 0 Then Return ldwo_objeto
8
9ldwo_objeto = adw_origen.GetDWObject(ls_nombre)
10Return ldwo_objeto
11end function

Paso 4: normalizar propiedades comunes y específicas

Todos los objetos aportan un núcleo común: Name, Type, Band, X, Y, Width, Height y Visible. Después, el tipo determina qué propiedades adicionales son válidas.

1ls_tipo = of_obtener_tipo(ldwo_objeto)
2ll_total = of_agregar_propiedad(ads_propiedades, "Name", String(ldwo_objeto.Name), "Identidad")
3ll_total = of_agregar_propiedad(ads_propiedades, "Type", String(ldwo_objeto.Type), "Identidad")
4ll_total = of_agregar_propiedad(ads_propiedades, "X", String(ldwo_objeto.X), "Geometría")
5ll_total = of_agregar_propiedad(ads_propiedades, "Visible", String(ldwo_objeto.Visible), "Visual")
6
7Choose Case ls_tipo
8 Case "column"
9 ll_total = of_agregar_propiedad(ads_propiedades, "ColType", String(ldwo_objeto.ColType), "Datos")
10 ll_total = of_agregar_propiedad(ads_propiedades, "Format", String(ldwo_objeto.Format), "Datos")
11 Case "compute"
12 ll_total = of_agregar_propiedad(ads_propiedades, "Expression", String(ldwo_objeto.Expression), "Expresión")
13 Case "text"
14 ll_total = of_agregar_propiedad(ads_propiedades, "Text", String(ldwo_objeto.Text), "Contenido")
15End Choose

El DataWindow d_origen_inspeccion ofrece casos verificables: saldo expone formato monetario, total_saldos contiene la expresión sum(saldo for all), etiqueta_total aporta texto y alineación, y boton_actualizar muestra texto y acción.

Paso 5: seleccionar objetos directamente sobre el DataWindow

Además de la lista lateral, el usuario puede pulsar sobre un elemento visible. El evento clicked recibe dwo, valida que no sea nulo, copia dwo.Name al campo de búsqueda y ejecuta la misma ruta de inspección.

1event clicked;
2If Not IsNull(dwo) Then
3 sle_nombre_objeto.Text = String(dwo.Name)
4 of_inspeccionar_objeto()
5End If
6end event

Este patrón mantiene una única implementación funcional: escribir un nombre, elegirlo en la lista, hacer doble clic o pulsar en el DataWindow termina invocando of_inspeccionar_objeto.

Paso 6: aplicar y revertir un resaltado dinámico

Antes de cambiar el fondo, el NVO obtiene el color y modo actuales con Describe. Después usa Modify con el nombre dinámico. Una cadena vacía como resultado de Modify se interpreta como éxito; un texto devuelto se trata como error.

1as_color_anterior = adw_origen.Describe(as_nombre + ".Background.Color")
2as_modo_anterior = adw_origen.Describe(as_nombre + ".Background.Mode")
3
4ls_resultado = adw_origen.Modify( &
5 as_nombre + ".Background.Mode='2' " + &
6 as_nombre + ".Background.Color='16022560'")
7
8If Len(ls_resultado) > 0 Then Return -1
9Return 1

La ventana restaura el objeto antes de resaltar otro y también durante close. Esta simetría es importante: una herramienta de diagnóstico debe dejar el DataWindow en el mismo estado visual en que lo encontró.

Paso 7: construir JSON válido y guardarlo en UTF-8

of_exportar_json recorre el DataStore y genera una matriz properties. El código escapa barras invertidas, comillas, retornos de carro, saltos de línea y tabuladores antes de insertar valores en el documento.

1{
2 "properties": [
3 {"name": "Name", "value": "total_saldos", "category": "Identidad"},
4 {"name": "Type", "value": "compute", "category": "Identidad"},
5 {"name": "Expression", "value": "sum(saldo for all)", "category": "Expresión"}
6 ]
7}

Al guardar, la cadena se convierte explícitamente con EncodingUTF8!. Esto protege valores como “Comercial Águila”, “Distribuidora del Pacífico” y “Tecnología y Diseño México”.

1lblb_contenido = Blob(as_contenido, EncodingUTF8!)
2ll_archivo = FileOpen(as_ruta, StreamMode!, Write!, LockWrite!, Replace!)
3If ll_archivo < 1 Then Return -1
4
5ll_escritos = FileWriteEx(ll_archivo, lblb_contenido)
6FileClose(ll_archivo)

Paso 8: validar el diseño usando el mismo inventario

La opción Validar diseño demuestra que la inspección puede convertirse en una regla automatizada. Para cada nombre, el NVO lee Visible, X, Y, Width y Height y registra:

• objetos ocultos;

• posiciones con coordenadas negativas;

• anchos o altos menores o iguales que cero.

d_origen_inspeccion contiene deliberadamente columna_oculta con visible="0". Por eso el validador tiene al menos un caso controlado que puede detectar sin base de datos ni preparación adicional.

1If String(ldwo_objeto.Visible) = "0" Then
2 ll_hallazgos++
3 ls_informe += "[Oculto] " + ls_nombre + "~r~n"
4End If
5If ll_x < 0 Or ll_y < 0 Then
6 ll_hallazgos++
7 ls_informe += "[Posición] " + ls_nombre + " está fuera del origen visible.~r~n"
8End If
9If ll_ancho <= 0 Or ll_alto <= 0 Then
10 ll_hallazgos++
11 ls_informe += "[Dimensión] " + ls_nombre + " tiene ancho o alto no válido.~r~n"
12End If

Gestión del ciclo de vida

Las referencias DWObject se destruyen después de cada inspección. La ventana también libera el DataStore y el NVO durante el cierre. Antes de destruirlos, restaura el resaltado pendiente.

1event close;
2of_restaurar_resaltado_actual()
3If IsValid(ids_propiedades) Then Destroy ids_propiedades
4If IsValid(inv_inspector) Then Destroy inv_inspector
5end event

Este orden evita abandonar una modificación visual temporal y deja explícita la propiedad de los objetos creados con Create.

Aplicaciones prácticas

• Soporte: inspectores internos que muestren nombre, tipo, banda y geometría sin abrir el Painter.

• Documentación: inventarios JSON para catálogos técnicos o documentación automática.

• Control de calidad: reglas para objetos ocultos, formatos inconsistentes, tamaños inválidos o secuencias de tabulación.

• Migración: comparación de propiedades antes y después de convertir una aplicación.

• Pruebas: búsqueda de objetos por tipo o nombre sin acoplar el código de prueba a referencias estáticas.

• Herramientas de desarrollador: resaltado, navegación y diagnóstico de DataWindows complejos durante la ejecución.

Mejoras recomendadas para una herramienta de producción

• Agregar manejo de errores de Describe y validar respuestas que comiencen con !.

• Ampliar la matriz de propiedades para gráficos, líneas, óvalos, reportes anidados y otros tipos utilizados por la aplicación.

• Separar la serialización JSON en un componente especializado o usar un generador JSON cuando el contrato crezca.

• Incluir nombre del DataObject, versión, marca de tiempo y contexto de ejecución en la exportación.

• Permitir reglas configurables en lugar de codificar todas las validaciones dentro del NVO.

• Procesar inventarios grandes sin congelar la interfaz y añadir un registro técnico de errores.

• No exportar expresiones o metadatos sensibles sin aplicar controles de acceso y políticas de diagnóstico.

• Crear pruebas para objetos inexistentes, nombres con caracteres especiales y tipos no contemplados.

Conclusión

GetDWObject se vuelve especialmente útil cuando forma parte de una arquitectura de introspección completa. Describe descubre la definición activa, GetDWObject permite interpretar cada elemento según su tipo, un NVO transforma propiedades heterogéneas en filas uniformes y Modify aporta una interacción visual reversible.

Demo_GetDWObject lleva ese patrón desde la inicialización hasta un resultado reutilizable: funciona sin base de datos, ofrece varias rutas de selección, conserva correctamente caracteres en UTF-8, exporta JSON y demuestra cómo las mismas propiedades pueden alimentar una validación automática. Es una base compacta, pero técnicamente sólida, para construir inspectores, documentadores y auditores de DataWindows en PowerBuilder 2025 R2.

PowerBuilder 2025 R2: inspección dinámica de DataWindows con… | Luis Avilan