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

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_dw3ids_propiedades = Create DataStore45dw_origen.DataObject = "d_origen_inspeccion"6dw_propiedades.DataObject = "d_propiedades_objeto"7ids_propiedades.DataObject = "d_propiedades_objeto"8ids_propiedades.ShareData(dw_propiedades)910ddlb_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")23Do While Len(ls_objetos) > 04 ll_posicion = Pos(ls_objetos, "~t")5 If ll_posicion > 0 Then6 ls_nombre = Left(ls_objetos, ll_posicion - 1)7 ls_objetos = Mid(ls_objetos, ll_posicion + 1)8 Else9 ls_nombre = ls_objetos10 ls_objetos = ""11 End If1213 ldwo_objeto = of_obtener_objeto(adw_origen, ls_nombre)14 If Not IsNull(ldwo_objeto) Then15 If of_coincide_tipo(of_obtener_tipo(ldwo_objeto), as_filtro) Then16 ll_total++17 as_nombres[ll_total] = ls_nombre18 End If19 Destroy ldwo_objeto20 End If21Loop
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_objeto3String ls_nombre45If Not IsValid(adw_origen) Then Return ldwo_objeto6ls_nombre = Trim(as_nombre)7If Len(ls_nombre) = 0 Then Return ldwo_objeto89ldwo_objeto = adw_origen.GetDWObject(ls_nombre)10Return ldwo_objeto11end 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")67Choose Case ls_tipo8 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) Then3 sle_nombre_objeto.Text = String(dwo.Name)4 of_inspeccionar_objeto()5End If6end 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")34ls_resultado = adw_origen.Modify( &5 as_nombre + ".Background.Mode='2' " + &6 as_nombre + ".Background.Color='16022560'")78If Len(ls_resultado) > 0 Then Return -19Return 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 -145ll_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" Then2 ll_hallazgos++3 ls_informe += "[Oculto] " + ls_nombre + "~r~n"4End If5If ll_x < 0 Or ll_y < 0 Then6 ll_hallazgos++7 ls_informe += "[Posición] " + ls_nombre + " está fuera del origen visible.~r~n"8End If9If ll_ancho <= 0 Or ll_alto <= 0 Then10 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_propiedades4If IsValid(inv_inspector) Then Destroy inv_inspector5end 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.