PBToolboxAI v5 ← Site

form — u_pbt_form #

← Referencia de componentes · Índice de la guía

form es una pantalla de entrada de datos entera en un solo control: campos tipados — texto, número, lista, botones de opción, fecha, hora, color, casilla, contraseña, correo, texto largo, imagen, etiquetas, estrellas — dispuestos en columnas y en secciones que se pliegan, cada uno con su etiqueta que sube a su borde. Las reglas de cada campo (obligatorio, límites, patrón, máscara…) y sus errores de negocio se comprueban juntos; of_is_dirty dice qué ha cambiado desde la carga, of_load_row / of_save_row leen y escriben una fila de un DataWindow. Su propio botón Aceptar llama a of_validate.

▶ Verlo en vivo — Aplicación de demostración, mosaico Form: la vista previa, el código que lo genera y esta página, uno al lado del otro.


De un vistazo #

Userobjectu_pbt_form
Clases de itemsn_pbt_form_section (sección) → n_pbt_form_field (campo)
Sirve paraSustituir una pantalla de entrada hecha de decenas de controles colocados a mano por un formulario con tema que se maqueta solo y comprueba lo que se escribe: ficha de cliente, pedido, ajustes, un paso de un asistente
ValoresSiempre texto, uno a uno (of_field(dirección).is_value); todos a la vez y tipados con of_values; una fila de DataWindow con of_load_row / of_save_row
Opciones opt-inib_veto_changes

Inicio rápido #

// Two columns ; a name at the top, then a section
uo_form.ii_columns = 2
uo_form.of_add_field(/*keys*/ "name", /*label*/ "Name", /*type*/ u_pbt_form.TYPE_STRING, /*value*/ "")
uo_form.of_field(/*keys*/ "name").ib_required = true
uo_form.of_add_section(/*key*/ "order", /*title*/ "Order")
uo_form.of_add_field(/*keys*/ "order/qty", /*label*/ "Quantity", /*type*/ u_pbt_form.TYPE_NUMBER, /*value*/ "1")
uo_form.of_add_field(/*keys*/ "order/due", /*label*/ "Due on", /*type*/ u_pbt_form.TYPE_DATE, /*value*/ "")
// event clicked of cb_ok
// Local variables
n_pbt_json lnv_json

// Every error shown at once, the keyboard on the first
if uo_form.of_validate() > 0 then return

// Valid : read the values, typed, along the same addresses
lnv_json.of_load(/*json*/ uo_form.of_values())
of_save_order(lnv_json.of_get_string(/*path*/ "name"), lnv_json.of_get_number(/*path*/ "order/qty"))

Una dirección por campo #

Un campo se designa por su dirección: su clave sola en el primer nivel ("name"), la sección y luego el campo unidos por / dentro de una sección ("order/qty") — un nivel, nunca más. Una sección se designa por su clave sola. En el primer nivel, una clave nombra una sola cosa: una sección y un campo de arriba no pueden compartirla. Los eventos llevan la misma dirección, la que usted escribió; la clave de un campo (su último nivel) es también el nombre de la columna que leen of_load_row y of_save_row.

// A section by its key, a field by its address
uo_form.of_section(/*key*/ "order").ib_collapsed = true
uo_form.of_field(/*keys*/ "order/qty").is_value = "3"

Los tipos de campo #

El tipo se elige una vez, en of_add_field, y da el control. El valor, en cambio, es siempre texto — dado, leído, entregado por los eventos — escrito igual sea cual sea el idioma del equipo. Una lista, una fecha y un color se abren en las ventanas nativas compartidas por toda la biblioteca, con los colores del tema.

ConstanteControlEl valor, como texto
TYPE_STRINGCuadro de texto; una máscara (is_mask, sintaxis de un EditMask) muestra sus literales mientras se teclea; un botón «…» (ib_ellipsis) abre su búsquedaEl texto tal cual; enmascarado, sin sus literales (5551234567)
TYPE_TEXTAREATexto en varias líneas; Intro salta de líneaEl texto, saltos de línea incluidos
TYPE_NUMBERCuadro de texto que lee un número en el idioma de visualización: su separador decimal o un punto, su agrupación de miles bien formada (1,000 vale mil en inglés, 1.500 mil quinientos en alemán, 1 234,5 en francés). Una entrada ambigua (1,5 en inglés, 1 2) o que no es un número se queda en pantalla con el mensaje Not a number traducido, y el valor vigente no se mueve; Arriba / Abajo lo suben o lo bajan en su paso (id_step)Con punto: "12.5"; "" = vacío. Conservado cifra a cifra; 12.50 retecleado por 12.5 no es un cambio
TYPE_BOOLEANUn interruptor (por defecto) o una casilla (is_boolean_style), con su etiqueta al lado"true" · "false"
TYPE_ENUMBotón que abre la lista compartida; sus opciones se añaden una a una (of_add_option)El valor de la opción elegida, nunca su texto; un valor que ninguna opción ofrece se conserva y se muestra tal cual, como un DataWindow sin tabla de códigos
TYPE_RADIOUn botón de opción por opción, uno al lado del otro; las flechas pasan de uno a otroEl valor de la opción elegida
TYPE_DATEBotón que abre el calendario compartido; la fecha se muestra en el formato del idioma de visualización"yyyy-mm-dd"
TYPE_TIMECuadro de texto: 9:5 pasa a 09:05; lo que no es una hora de 24 h se queda en pantalla con su mensaje"hh:mm"
TYPE_COLORBotón con la muestra del color, que abre el selector compartidoEl long RGB escrito como texto: String(RGB(255, 0, 0)) = "255"
TYPE_PASSWORDCuadro de texto ocultoEl texto tal cual
TYPE_EMAILCuadro de texto que comprueba la forma de una direcciónEl texto tal cual
TYPE_IMAGEUna imagen, no una entrada: nunca validada, nunca en of_values; con ii_rowspan cubre varias filas (la foto de una ficha)Su origen: un archivo, o mono: / tint: seguido de una ruta (con los colores del tema); una dirección http:// se rechaza
TYPE_TAGSEtiquetas en píldoras dentro de un cuadro: se teclea una palabra y luego Intro, una coma o Tab; Retroceso en la entrada vacía quita la última, cada píldora tiene su cruz, Izquierda / Derecha las recorren. Las opciones del campo (of_add_option) son sugerencias filtradas al teclear; ib_options_only las vuelve las únicas opciones, ii_max_tags limita su númeroLas etiquetas unidas por una coma y un espacio: "vip, b2b" (el valor de la opción para una sugerencia elegida). La coma es el separador: nunca dentro de una etiqueta; escrita por el código separa, los espacios alrededor desaparecen, la misma etiqueta dos veces (sin distinguir mayúsculas) se guarda una vez
TYPE_RATINGEstrellas para hacer clic, ii_star_count (5 por defecto), medias estrellas con ib_half_stars. Pasar el ratón previsualiza sin mover nada; un clic en el valor actual lo borra. Con el teclado: flechas, Inicio / Fin, un dígito, 0 o Supr. Una estrella llena toma el acento del componenteEl número de estrellas: "4", "3.5"; "" = sin valoración (0 también). Escrito por el código, redondeado al paso y mantenido dentro de las estrellas; un texto o un negativo se rechaza

La validación #

Cada campo lleva sus reglas: ib_required (una casilla obligatoria debe estar marcada), is_min / is_max (un número, una fecha, una hora), ii_max_length, is_pattern (una expresión regular que el valor entero debe seguir), is_mask (una máscara rellena), is_match (igual a otro campo), y la forma de un correo, de un número o de una hora. Su aplicación añade sus errores de negocio (is_error: ya existe), que se quedan hasta que el usuario modifica el campo. Nada se muestra en rojo mientras el usuario no ha salido de un campo: un formulario nuevo no está en rojo. of_is_valid y of_invalid_keys responden sin mostrar nada; of_validate lo muestra todo a la vez y pone el teclado en el primer campo con error. Un campo oculto, desactivado o de solo lectura no se juzga por sus reglas: el usuario no podría corregirlo.

// A customer code : two capital letters, then four digits
uo_form.of_field(/*keys*/ "code").is_pattern = "[A-Z]{2}[0-9]{4}"
uo_form.of_field(/*keys*/ "code").is_pattern_message = "Two capital letters, then four digits"

// A rule only your database knows
if of_email_taken(uo_form.of_field(/*keys*/ "email").is_value) then
    uo_form.of_field(/*keys*/ "email").is_error = "Already used by another customer"
end if

Propiedades #

PropiedadTipoPredeterminadoFunción
ii_columnsinteger1Número de columnas, de 1 a 12 (un valor fuera de rango se devuelve dentro). Cada sección sigue. Un campo abarca más con ii_colspan, empieza en una columna dada con ii_col, cubre varias filas con ii_rowspan; los demás campos rellenan las celdas libres
ib_readonlybooleanfalseEl formulario entero de solo lectura: cada valor se lee, ninguno se cambia — una ficha que se consulta. Su código puede seguir escribiendo los valores
ib_veto_changesbooleanfalseOpt-in: preguntar antes de escribir un valor del usuario (lanza ue_value_changing, que puede rechazarlo: el campo vuelve a mostrar el valor anterior). Un valor escrito por su código nunca pregunta
is_theme_stylestring""Estilo visual del componente (constantes THEME_STYLE_*); vacío = el de la aplicación, seguido en cada cambio
is_theme_modestring""Variante clara u oscura (constantes THEME_MODE_*); vacío = la de la aplicación, seguida en cada cambio
il_theme_accentlong-1Color de acento de este componente (-1 = acento de la aplicación, o el del tema)
is_tooltipstring""Tooltip simple mostrado al pasar el ratón por el componente
is_super_tooltip_titlestring""Título del tooltip enriquecido (prevalece sobre is_tooltip)
is_super_tooltip_textstring""Texto del tooltip enriquecido (se acepta el marcado enriquecido)
is_super_tooltip_imagestring""Imagen del tooltip enriquecido

Propiedades de una sección — n_pbt_form_section #

PropiedadTipoPredeterminadoFunción
is_titlestring""Título del encabezado. Acepta el texto enriquecido con etiquetas. Vacío, el encabezado muestra la clave
ib_collapsedbooleanfalsetrue pliega la sección: su encabezado se queda, sus campos se ocultan — conservan sus valores y siguen juzgados. Sigue ue_section_toggled, como con un clic en el encabezado. Leída en directo
ib_visiblebooleantruefalse quita la sección, y todos sus campos, del formulario; conservan sus valores (of_values los sigue devolviendo) y no se juzgan
is_tooltipstring""Tooltip simple mostrado al pasar el ratón por el item
is_super_tooltip_titlestring""Título del tooltip enriquecido del item (prevalece sobre is_tooltip)
is_super_tooltip_textstring""Texto del tooltip enriquecido del item (se acepta el marcado enriquecido)
is_super_tooltip_imagestring""Imagen del tooltip enriquecido del item
il_accentlong-1Acento del encabezado de la sección: la línea bajo su título lo toma (-1 = el del componente). Sus campos conservan el suyo
il_back_colorlong-1Fondo del encabezado de la sección, en RGB(r,g,b) (-1 = lo que da el tema)
il_text_colorlong-1Color del texto del encabezado de la sección (-1 = lo que da el tema)
il_back_color_hoverlong-1Fondo del encabezado de la sección bajo el puntero (-1 = lo que da el tema)
il_text_color_hoverlong-1Color del texto del encabezado de la sección bajo el puntero (-1 = lo que da el tema)

Propiedades de un campo — n_pbt_form_field #

PropiedadTipoPredeterminadoFunción
is_valuestring""El valor, como texto (ver los tipos). Escribirlo no lanza nada, como SetItem; leerlo da el valor actual, quienquiera que lo haya escrito — lo que se está tecleando cuenta. Un valor que el tipo no puede contener (una fecha que no existe, un texto en un número) se rechaza: el anterior se queda y of_get_last_error dice por qué
is_labelstring""La etiqueta, dentro del campo: sube al borde cuando el campo tiene un valor o el teclado. Acepta el texto enriquecido con etiquetas. Una estrella sigue a la etiqueta de un campo obligatorio. Vacía, la clave
is_placeholderstring""Una indicación dentro de un campo de texto vacío mientras tiene el teclado
is_helpstring""Una línea de ayuda bajo el campo (texto enriquecido con etiquetas); se oculta mientras un error ocupa su lugar
is_errorstring""Un error de negocio de su aplicación (ya existe): mostrado enseguida, el formulario no válido, hasta que el usuario modifica el campo o of_clear_errors; "" lo borra. Leído, da el mensaje que el campo muestra — o mostrará cuando el usuario salga de él: el error de negocio, si no el de sus reglas, "" si es válido. En un campo que el usuario no puede alcanzar (oculto, desactivado) se conserva y se relee, pero no cuenta: el formulario sigue siendo válido hasta que el campo vuelve
is_minstring""El valor más pequeño aceptado para un número (con punto), una fecha (yyyy-mm-dd) o una hora (hh:mm); el mensaje dice Minimum y el límite. Un límite que el tipo no lee se ignora; "" = ninguno — para un número, el extremo inferior de la SpinRange de su columna de DataWindow (of_load_row). Leído, da el límite vigente
is_maxstring""El valor más grande aceptado, leído como is_min; el mensaje dice Maximum y el límite
is_patternstring""Una expresión regular (sintaxis JavaScript) que el valor entero debe seguir: [A-Z]{2}[0-9]{4} acepta AB1234, no xAB1234. Un valor vacío se deja a ib_required
is_pattern_messagestring""El mensaje cuando is_pattern falla; vacío: Invalid format, traducido
is_maskstring""Una máscara de entrada. En un TYPE_STRING, la sintaxis de un EditMask de PowerBuilder: # un dígito, ! cualquier carácter en mayúscula, ^ cualquier carácter en minúscula, a una letra o un dígito, X cualquier carácter, el resto escrito para el usuario mientras teclea ((###) ###-####); el cursor se queda tras lo tecleado. El valor solo conserva lo tecleado en las casillas, como un EditMask (5551234567, listo para una columna de dígitos); un valor del código es el valor en bruto, sin sus literales (312345678 bajo +33 # ## ## ## ##), los literales iniciales se escriben en cuanto el campo toma el teclado, un valor que no rellena todas las casillas es Incomplete, y poner una máscara después de un valor no cambia nada de lo que hizo el usuario. Tras of_load_row, una máscara vacía toma el EditMask de la columna. En un TYPE_NUMBER: una máscara numérica en la sintaxis de PowerBuilder (EditMask, formato DataWindow): # un dígito opcional, 0 un dígito obligatorio, , la agrupación, . el separador decimal, % el número mostrado por 100, literales ($, un texto entre comillas, \x) y secciones positivo;negativo ("$#,##0;($#,##0)"). La coma y el punto designan los separadores del idioma de visualización. Al teclear, la agrupación aparece a medida que llegan los dígitos, los decimales se limitan a la máscara, las letras se rechazan, el cursor conserva su lugar entre los dígitos y un número pegado se lee en el idioma de visualización; al salir del campo la máscara completa (12 pasa a 12,00). El valor conserva el número tal como lo escribe el código ("1234.5"), sin agrupación ni símbolo. Un límite inferior (is_min) de 0 o más rechaza el signo menos; los límites se aplican al salir del campo, y su mensaje los muestra a través de la máscara; vacío en un TYPE_NUMBER (por defecto): el campo se teclea según su is_format sin sus literales ($#,##0.00 se teclea #,##0.00, #,##0 "px" se teclea #,##0; un % se queda, es una escala)
is_formatstring""Un TYPE_NUMBER: su formato de visualización, cómo se muestra el número mientras el campo no tiene el teclado — la misma sintaxis que is_mask, con sus literales: #,##0 "px" muestra 575 px, $#,##0.00;($#,##0.00) un precio, 0.0% una tasa (0,125 muestra 12,5%). Cuando el campo toma el teclado su texto pasa a ser el de is_mask (575), el cursor conserva su lugar entre los dígitos; al salir vuelve el texto de visualización. El cambio nunca modifica el valor y no lanza nada: ue_value_changed solo informa de lo que el usuario tecleó, el número desnudo. Vacío: el campo muestra su is_mask
is_boolean_stylestring"switch"Un TYPE_BOOLEAN: cómo se dibuja. BOOLEAN_SWITCH (por defecto): un interruptor, una pista y un círculo que se desliza hacia el final, en el color de acento del componente, cuando está encendido — un ajuste de encendido / apagado. BOOLEAN_CHECKBOX: la casilla clásica — una aceptación, una elección entre opciones. Ambos responden a Espacio y a un clic en su etiqueta, dan el mismo valor y los mismos eventos; desactivados o de solo lectura siguen siendo legibles. El círculo se desliza en un cuarto de segundo, nada cuando Windows pide menos animaciones; en un idioma escrito de derecha a izquierda se desliza hacia la izquierda
ii_max_tagsinteger0Un TYPE_TAGS: el número máximo de etiquetas (0 = sin límite). Una más tecleada por el usuario se rechaza: se queda en la entrada con el mensaje At most N; una lista más larga escrita por el código se conserva, y el campo no es válido (of_is_valid, is_error) — se muestra una vez tocado el campo o tras of_validate, nunca de entrada
ib_options_onlybooleanfalseUn TYPE_TAGS: true limita sus etiquetas a las opciones del campo (of_add_option) — un texto que no nombra ninguna (por su texto o su valor, sin mayúsculas ni acentos) se rechaza con Not in the list. false: todo texto es una etiqueta, las opciones solo son sugerencias
ii_star_countinteger5Un TYPE_RATING: el número de estrellas, de 1 a 10 (un número fuera se devuelve dentro). El valor actual se mantiene dentro, sin evento: poner el número antes de un valor por encima de cinco
ib_half_starsbooleanfalseUn TYPE_RATING: true permite las medias estrellas — la primera mitad de la estrella bajo el puntero (en el sentido de lectura), Izquierda / Derecha de media estrella — y el valor se escribe "3.5". Desactivado, el valor actual se redondea a una estrella entera, sin evento: activarlo antes de un medio valor
is_matchstring""La dirección de otro campo al que este debe ser igual — repetir una contraseña, un correo. El mensaje sigue cada cambio de uno u otro
is_prefixstring""Un texto corto dentro del campo, antes del valor (un símbolo de moneda); no forma parte del valor
is_suffixstring""Un texto corto dentro del campo, después del valor (una unidad); no forma parte del valor
ii_max_lengthinteger0El texto más largo aceptado (0 = sin límite) por un TYPE_STRING, TYPE_TEXTAREA, TYPE_PASSWORD o TYPE_EMAIL — un número o una hora no tiene longitud, se ignora; 0 toma el Limit de su columna de DataWindow (of_load_row), leído da la longitud vigente: el tecleo se detiene ahí, un contador aparece bajo un campo de texto; un valor más largo escrito por el código es Too long. En un TYPE_TAGS: la etiqueta más larga — un pegado de varias etiquetas nunca se corta, cada una se juzga
ii_colspaninteger1Cuántas columnas abarca el campo (como mucho ii_columns); -1 = todo el ancho
ii_rowspaninteger1Cuántas filas cubre el campo — una imagen junto a varios campos; los siguientes rellenan las celdas libres a su lado
ii_colinteger0La columna donde el campo empieza, desde 1; 0 = donde lo pone el flujo. La tecla Tab sigue el orden de la pantalla — primero los campos de arriba, luego cada sección —, sea cual sea la columna
ib_requiredbooleanfalseEl campo debe rellenarse — una casilla debe estar marcada (una casilla Acepto), un campo de etiquetas contener una, una valoración tener una estrella. Una estrella sigue a su etiqueta; el error Required aparece cuando el usuario sale del campo o con of_validate. Una columna cuyo estilo de edición dice Required lo vuelve obligatorio también (of_load_row); leído, da el estado vigente
ib_readonlybooleanfalseMuestra el valor sin dejar cambiarlo: un texto aún puede seleccionarse y copiarse; una lista, una fecha, un color, un interruptor o botones de opción conservan el teclado (Tab los alcanza, un lector de pantalla dice solo lectura) pero ni se abren ni cambian. No se juzga por sus reglas; su código puede seguir escribiendo is_value
ib_enabledbooleantruefalse atenúa el campo: sin teclado (sale del orden de tabulación), sin clic, no se juzga por sus reglas
ib_visiblebooleantruefalse quita el campo del formulario; conserva su dirección y su valor (of_values lo sigue devolviendo) y no se juzga
is_columnstring""La columna del DataWindow que of_load_row y of_save_row leen y escriben para este campo; "" (por defecto) = su clave, el último nivel de su dirección. Dos secciones pueden así llevar la misma clave (billing/town, shipping/town) ligada a dos columnas, y un campo conservar una clave legible para una columna llamada cust_nm
id_stepdouble0Un TYPE_NUMBER: lo que Arriba / Abajo (y sus botones, ib_spin) añaden o quitan, mantenido entre is_min e is_max; 0 (por defecto) = el SpinIncr de su columna de DataWindow (of_load_row), si no 1. Un paso es un gesto del usuario: se lanza ue_value_changed (y antes se plantea ue_value_changing con ib_veto_changes). Un campo vacío parte de is_min, si no de 0; un paso negativo se rechaza
ib_ellipsisbooleanfalseUn campo de texto (TYPE_STRING, TYPE_NUMBER, TYPE_EMAIL…): true muestra un botón … en su extremo — su búsqueda, un cliente, un artículo. Un clic en él, o Ctrl+Intro en el campo, lanza ue_ellipsis_clicked; queda fuera del orden de Tab. Funciona también en un campo de solo lectura (un valor elegido solo por la búsqueda), nunca en uno desactivado
ib_spinbooleanfalseUn TYPE_NUMBER: true muestra dos pequeños botones en su extremo, arriba y abajo, que añaden o quitan id_step entre is_min e is_max — un gesto del usuario (ue_value_changed). La rueda del ratón también lo hace mientras el campo tiene el teclado; los botones quedan fuera del orden de Tab (Arriba / Abajo en el campo hacen lo mismo) y se ocultan en un campo de solo lectura. Una columna cuyo EditMask dice Spin los da con of_load_row, con su SpinIncr como paso y su SpinRange como límites. Ignorado por los demás tipos; leído, da el estado vigente
of_type ( ) → string——El tipo dado a of_add_field (una constante TYPE_*). Solo lectura: un tipo se elige una vez
of_is_dirty ( ) → boolean——TRUE cuando este campo difiere de su referencia (ver of_is_dirty del formulario)
of_is_valid ( ) → boolean——TRUE cuando este campo no tiene ningún error; is_error dice cuál
is_tooltipstring""Tooltip simple mostrado al pasar el ratón por el item
is_super_tooltip_titlestring""Título del tooltip enriquecido del item (prevalece sobre is_tooltip)
is_super_tooltip_textstring""Texto del tooltip enriquecido del item (se acepta el marcado enriquecido)
is_super_tooltip_imagestring""Imagen del tooltip enriquecido del item
il_accentlong-1Acento del campo: su borde lo toma cuando tiene el teclado, y su etiqueta una vez subida (-1 = el del componente)
il_back_colorlong-1Fondo del campo, en RGB(r,g,b) (-1 = lo que da el tema)
il_text_colorlong-1Color del texto del campo (-1 = lo que da el tema)
il_back_color_hoverlong-1Fondo del campo bajo el puntero (-1 = lo que da el tema)
il_text_color_hoverlong-1Color del texto del campo bajo el puntero (-1 = lo que da el tema)

Métodos #

En el componente #

MétodoFunción
of_add_section (string as_key, string as_title) → longAñade una sección al final: un encabezado que se pliega, subrayado con el acento, bajo el cual se añaden luego los campos "clave/nombre". Un título vacío muestra la clave. Devuelve 0 una vez añadida, -5 para una clave vacía, que contiene / o `, o ya tomada por una sección o un campo de arriba, -2` si el componente no está creado
of_add_field (string as_keys, string as_label, string as_type, string as_value) → longAñade un campo en su dirección, al final de lo que lo contiene: su sección o la parte de arriba del formulario. as_type es una constante TYPE_*, as_value su valor como texto (el origen de una imagen). Devuelve 0 una vez añadido, -5 para una dirección ya tomada (un campo, o una sección en el primer nivel), un nivel vacío, más de dos niveles, una sección nunca añadida, una clave que contiene `, un tipo desconocido o un valor que el tipo no puede contener (of_get_last_error dice por qué), -2` si el componente no está creado
of_add_option (string as_keys, string as_value, string as_text) → longAñade una opción, al final, a una lista TYPE_ENUM, a botones TYPE_RADIO o a las sugerencias de un TYPE_TAGS: as_value es lo que contiene el campo (y lo que da ue_value_changed), as_text lo que lee el usuario, como texto plano ("" muestra el valor). Devuelve 0 una vez añadida, -5 para una dirección que no es una lista, botones de opción ni etiquetas, un valor ya ofrecido, un valor que contiene ` o / (of_keys_at lo une a la dirección del campo) — y para etiquetas un valor vacío o con una coma —, -2` si el componente no está creado
of_remove_option (string as_keys, string as_value) → longQuita una opción de una lista, de botones de opción o de etiquetas, por su valor; el valor del campo se queda como está. Devuelve 0 una vez quitada, -5 para una dirección que no es una lista, botones de opción ni etiquetas, o un valor que no ofrece, -2 si el componente no está creado
of_clear_options (string as_keys) → longQuita todas las opciones de una lista, de botones de opción o de etiquetas, para cargar otra lista sin perder el campo, su handle ni su estado; su valor se queda como está. Devuelve 0 una vez vaciada, -5 para una dirección que no es una lista, botones de opción ni etiquetas, -2 si el componente no está creado
of_remove_field (string as_keys) → longQuita un campo por su dirección, con su handle; el resto del formulario conserva su estado. Devuelve 0 una vez quitado, -5 para una dirección nunca añadida, -2 si el componente no está creado
of_remove_section (string as_key) → longQuita una sección y todos sus campos, con sus handles. Devuelve 0 una vez quitada, -5 para una sección nunca añadida, -2 si el componente no está creado
of_clear ( ) → longVacía el formulario: secciones, campos y sus handles. A diferencia de of_reset, las columnas, la solo lectura y la pregunta se quedan. Devuelve 0 una vez vaciado, -2 si el componente no está creado
of_section (string as_key) → n_pbt_form_sectionEl handle de una sección, por su clave: título, pliegue, visibilidad, colores y tooltip. Creado en el primer acceso
of_field (string as_keys) → n_pbt_form_fieldEl handle de un campo, por su dirección: of_field("order/qty").is_value = "3". Creado en el primer acceso
of_values ( ) → stringTodos los valores a la vez, leídos en directo, en un objeto JSON: los campos de arriba, luego cada sección como un objeto propio — ocultos incluidos, imágenes excluidas; lo que el usuario aún teclea se valida primero, como hace AcceptText: ue_value_changing planteado con ib_veto_changes, luego ue_value_changed lanzado — ambos antes de la lectura, nunca en medio: su Aceptar lee lo que está en pantalla. Tipados: un número es un número (escrito cifra a cifra, null si está vacío), un booleano un booleano, un color su long RGB, el resto texto. Se lee con n_pbt_json, por la misma dirección: lnv_json.of_get_number("order/qty")
of_load (string as_json) → longCarga un registro: un objeto JSON de valores por dirección — los campos de arriba, luego un objeto por sección, o claves "order/qty" —, la forma que devuelve of_values; números y booleanos pueden ir tipados — un número de más de 15 dígitos se lee cifra a cifra, un booleano vale true, false, 1 o 0 (cualquier otro valor se rechaza). Los valores se escriben en silencio y pasan a ser la referencia de of_is_dirty; los errores mostrados y los errores de negocio se borran. Un campo que el objeto no nombra conserva su valor. Devuelve 0 una vez cargado, -5 para un texto que no es un objeto JSON, o un valor que su campo no puede contener (abc para un número) — entonces no se escribe nada, of_get_last_error nombra los campos —, -2 si el componente no está creado
of_load_row (powerobject apo_data, long al_row) → longCarga una fila de un DataWindow, un DataStore o un DataWindowChild: cada campo toma el valor de su columna — la que nombra is_column, si no su clave (el último nivel de su dirección) —, tipado — una fecha yyyy-mm-dd, una hora hh:mm, un número con punto, un booleano leído por el valor On de una columna casilla (si no, 1, Y, true…). Lo que el DataWindow dice de la columna llega con ella, releído en cada carga: un campo sin etiqueta muestra el texto de encabezado de su columna, un campo sin máscara ni formato toma su EditMask y su Format (un número $#,##0), una lista o botones de opción sin opciones propias ofrecen su tabla de códigos o las filas de su DropDownDataWindow; un campo sin reglas propias pasa a ser obligatorio cuando el estilo de edición dice Required y limitado a su Limit, y un número cuyo EditMask dice Spin recibe sus botones (ib_spin), su paso (SpinIncr) y sus límites (SpinRange). La fila siguiente del mismo DataWindow no vuelve a dibujar ningún campo: el que tiene el teclado lo conserva. Como con of_load, los valores pasan a ser la referencia. Devuelve 0 una vez cargada, -5 para un objeto que no es ni un DataWindow, ni un DataStore, ni un DataWindowChild, una fila que no existe, o un valor que su campo no puede contener (entonces nada cambia: ni un valor ni lo que dice el DataWindow), -2 si el componente no está creado
of_save_row (powerobject apo_data, long al_row) → longEscribe los valores que el usuario ha cambiado (desde la referencia: of_load_row, of_reset_update) en una fila de un DataWindow, un DataStore o un DataWindowChild: cada campo cambiado en su columna (is_column, si no su clave), con SetItem, tipado para la columna (fecha, datetime, hora, número, texto; un booleano como valor On / Off de una columna casilla, 1 / 0 en una columna numérica, true / false en una columna de texto; un campo vacío escribe un NULL — un texto vacío, un NULL donde su columna dice NilIsNull, una cadena vacía si no, como el DataWindow). Un campo que el usuario no ha cambiado no se escribe: la fila conserva su estado, su Update solo envía lo que cambió. Un TYPE_DATE conserva la hora de una columna datetime, un TYPE_TIME su fecha. Lo que el usuario aún teclea se valida primero, y se lanza ue_value_changed, antes de cualquier lectura; el Update sigue siendo suyo. Devuelve 0 una vez escrita, -5 para un objeto que no es ni un DataWindow, ni un DataStore, ni un DataWindowChild, o una fila que no existe, -4 cuando una columna se quedó como estaba — un SetItem rechazado, un campo cuyo tipo la columna no puede contener (un TYPE_STRING en una columna de fecha), o un valor con decimales para una columna entera (3.5 estrellas en un long: SetItem lo redondearía): of_get_last_error nombra la primera, los demás campos cambiados se escriben igualmente —, -2 si el componente no está creado
of_is_valid ( ) → booleanTRUE cuando ningún campo mostrado tiene un error: cada campo obligatorio relleno (una casilla obligatoria marcada), cada regla seguida, ningún error de negocio. Leído en directo, sin mostrar nada; un error de negocio solo cuenta en un campo que el usuario puede alcanzar (mostrado y activo)
of_invalid_keys (ref string as_keys[]) → longRellena as_keys[] con las direcciones de los campos con error, en el orden de la pantalla, sin mostrar nada, y devuelve su número (0 = el formulario es válido)
of_validate ( ) → longMuestra todos los errores a la vez y pone el teclado en el primer campo con error (ue_focus_changed) — lo que llama su botón Aceptar antes de guardar. El teclado deja el botón por el formulario: la tecla siguiente va al campo; lo que el usuario aún teclea se valida primero, y un lector de pantalla lee los mensajes enseguida. Devuelve el número de campos con error (0 = el formulario es válido, nada se mueve), -2 si el componente no está creado
of_clear_errors ( ) → longBorra todos los errores de negocio (is_error escrito por su código); los errores de las reglas se quedan. Devuelve 0 una vez aplicado, -2 si el componente no está creado
of_is_dirty ( ) → booleanTRUE cuando al menos un campo difiere de su referencia: el valor dado al añadirlo, o lo que hicieron de él of_load, of_load_row u of_reset_update; lo que el usuario aún teclea se valida primero. Un número escrito de otra forma (12.50 por 12.5) es el mismo valor
of_dirty_keys (ref string as_keys[]) → longRellena as_keys[] con las direcciones de los campos modificados desde la referencia, en el orden de la pantalla, y devuelve su número
of_reset_update ( ) → longLos valores en pantalla pasan a ser la referencia: of_is_dirty vuelve a responder FALSE. Llámelo una vez que su Update haya funcionado, como ResetUpdate de un DataWindow. Devuelve 0 una vez aplicado, -2 si el componente no está creado
of_revert ( ) → longDevuelve cada campo a su valor de referencia, en silencio, y oculta los errores — el Cancelar del usuario. Devuelve 0 una vez aplicado, -2 si el componente no está creado
of_focus_field (string as_keys) → longPone el teclado en un campo, como un clic: su sección plegada se abre primero (ue_section_toggled), sigue ue_focus_changed. Devuelve 0 una vez hecho, -5 para una dirección nunca añadida o un campo que el usuario no puede alcanzar (oculto, desactivado — incluido el que tenía el teclado —, una imagen, más allá del límite de la demo), -2 si el componente no está creado
of_current_key ( ) → stringDirección del campo que tiene el teclado — o lo tuvo el último, mientras el teclado está en otra parte de la ventana: "order/qty", "" si aún ninguno. Siempre la actual, leída del componente; también lo que da ue_focus_changed
of_get_last_error ( ) → stringPor qué se rechazó la última escritura: un valor que el tipo no puede contener (is_value no tiene código de retorno; una imagen http://, un número infinito como 1e400), un is_pattern que el formulario no sabe leer, un valor que of_load rechazó, una columna que of_save_row dejó. Vacío cuando ha pasado; si no, el último error de la biblioteca, como en todo componente
of_reset ( ) → longVacía el formulario y luego devuelve cada propiedad del componente a su valor por defecto: una columna, ni solo lectura ni pregunta. Devuelve 0 una vez aplicado, -2 si el componente no está creado

En una sección y en un campo #

Una sección contiene campos; una lista o unos botones de opción contienen sus opciones, designadas por su valor. Los tres métodos los recorren.

MétodoFunción
of_count ( ) → longNúmero de campos de la sección, ocultos incluidos; número de opciones de una lista, de botones de opción o de etiquetas (0 para los demás tipos)
of_keys_at (long al_index) → stringDirección del campo de rango al_index (desde 1) — "order/qty", reutilizable tal cual en of_field; en una lista, la dirección seguida del valor de la opción. "" fuera de rango
of_has (string as_key) → boolean¿Contiene la sección este campo? ¿Ofrece la lista este valor?

Eventos #

EventoSe activa cuando
ue_value_changed (string as_keys, string as_value)El usuario ha cambiado un valor: una entrada validada al salir del campo o con Intro, una casilla marcada, un botón de opción, una opción, una fecha o un color elegidos (Arriba / Abajo recorren una lista, Supr vacía una lista, una fecha o un color). as_keys es la dirección del campo, as_value su nuevo valor como texto; ya está escrito. Un valor escrito por su código no lanza nada, como SetItem. Lo que el usuario aún teclea cuando su código lee el formulario (of_values, of_save_row, is_value…) se valida en ese momento, como hace AcceptText: el evento se lanza ahí, antes de la lectura
ue_value_changing (string as_keys, string as_value) → booleanCancelable, planteado antes de escribir un valor del usuario, y solo cuando ib_veto_changes = true (desactivado por defecto). Devolver FALSE lo rechaza: el campo vuelve a mostrar el valor anterior, que of_field(as_keys).is_value sigue devolviendo durante el evento. Devuelva TRUE para aceptar. Cuando su código lee el formulario (of_values, of_validate, is_value…) mientras el usuario aún teclea en un campo, la pregunta se plantea enseguida, antes de la lectura: la lectura devuelve el valor que usted aceptó
ue_focus_changed (string as_from_keys, string as_keys)El campo que tiene el teclado ha cambiado: un clic, la tecla Tab, su código (of_focus_field, of_validate — una orden también lo lanza, como SetColumn), o el campo se ha ido: quitado, ocultado, vaciado (as_keys vale entonces ""). El teclado que deja el formulario por otro control de la ventana no lanza nada
ue_section_toggled (string as_key, boolean ab_collapsed)Una sección se ha plegado (ab_collapsed = TRUE) o desplegado: un clic en su encabezado (Intro o Espacio cuando tiene el teclado), su ib_collapsed fijado por su código, u of_focus_field sobre un campo de una sección plegada
ue_ellipsis_clicked (string as_keys)Se ha hecho clic en el botón … de un campo (ib_ellipsis), o se ha pulsado Ctrl+Intro en el campo: abra su búsqueda (un cliente, un artículo) y escriba lo que eligió el usuario en of_field(as_keys).is_value. Lo tecleado ya está validado (ue_value_changing planteado antes con ib_veto_changes, ue_value_changed lanzado). Nunca se lanza para un campo desactivado
ue_ready ( )El componente ha terminado de cargarse; todo lo enviado antes se ha reproducido
ue_runtime_missing ( )El runtime WebView2 está ausente: el componente permanece vacío
ue_bg_color (long al_color)El componente ha calculado el color de fondo de su tema; el userobject ya lo ha adoptado (backcolor)

Teclado y ratón #

GestoEfecto
Tab, Mayús+TabPasan de un campo a otro, en el orden de la pantalla: los campos de arriba, luego cada sección, primero su encabezado; un campo desactivado se salta; los botones de opción son una parada, las flechas eligen
Intro en un campo de una líneaValida la entrada y luego pasa a la ventana: su botón por defecto (su Aceptar) lee un formulario al día. Con ib_veto_changes, la página no pregunta nada: la entrada queda pendiente, y la lectura del botón por defecto la valida (ue_value_changing se plantea entonces) — sin botón por defecto, la valida la salida del campo
EscEn un campo que el usuario ha modificado, devuelve su valor; si no, pasa a la ventana: su botón Cancelar
Lista, fecha, color: Intro, Espacio, F4, Alt+AbajoAbren la ventana compartida (lista, calendario, selector de color); Intro se queda ahí, no pasa al botón por defecto de la ventana — tampoco en un encabezado de sección, que pliega o despliega
Lista: Arriba / AbajoOpción anterior / siguiente, sin abrir la lista
Lista, fecha, color: Supr, RetrocesoVacían el valor
Etiquetas: Intro, coma, TabAñaden lo tecleado (Tab mantiene el teclado en el campo); Retroceso en la entrada vacía quita la última etiqueta; Izquierda / Derecha recorren las etiquetas, Supr quita la marcada; Abajo abre las sugerencias, Esc las cierra
Estrellas: flechas, Inicio / Fin, dígitos, SuprDerecha / Arriba una estrella más (media con ib_half_stars), Izquierda / Abajo una menos — invertidas en un idioma escrito de derecha a izquierda; Inicio una estrella, Fin todas; un dígito tantas estrellas; 0, Supr o Retroceso borran la valoración
Número: Arriba / AbajoAñaden o quitan el paso del campo (id_step, 1 por defecto), mantenido entre is_min e is_max: sigue ue_value_changed. Con ib_spin, sus dos botones hacen lo mismo, y la rueda mientras tiene el teclado
Ctrl+Intro, clic en …Un campo con ib_ellipsis: valida lo tecleado y luego lanza ue_ellipsis_clicked — su búsqueda
Clic en un encabezado de secciónPliega o despliega la sección (ue_section_toggled)

Ejemplos #

Una ficha de cliente #

// The whole form in one paint, two columns
uo_form.of_set_redraw(/*on*/ false)
uo_form.ii_columns = 2

// Identity : two required names, an e-mail, a phone with its mask
uo_form.of_add_section(/*key*/ "identity", /*title*/ "Identity")
uo_form.of_add_field(/*keys*/ "identity/first_name", /*label*/ "First name", /*type*/ u_pbt_form.TYPE_STRING, /*value*/ "")
uo_form.of_field(/*keys*/ "identity/first_name").ib_required = true
uo_form.of_add_field(/*keys*/ "identity/last_name", /*label*/ "Last name", /*type*/ u_pbt_form.TYPE_STRING, /*value*/ "")
uo_form.of_field(/*keys*/ "identity/last_name").ib_required = true
uo_form.of_add_field(/*keys*/ "identity/email", /*label*/ "E-mail", /*type*/ u_pbt_form.TYPE_EMAIL, /*value*/ "")
uo_form.of_add_field(/*keys*/ "identity/phone", /*label*/ "Phone", /*type*/ u_pbt_form.TYPE_STRING, /*value*/ "")
uo_form.of_field(/*keys*/ "identity/phone").is_mask = "(###) ###-####"

// Account : a credit limit with a bound, shown as a price and typed without the
// dollar sign ; a list ; a switch
uo_form.of_add_section(/*key*/ "account", /*title*/ "Account")
uo_form.of_add_field(/*keys*/ "account/credit_limit", /*label*/ "Credit limit", /*type*/ u_pbt_form.TYPE_NUMBER, /*value*/ "2500")
uo_form.of_field(/*keys*/ "account/credit_limit").is_format = "$#,##0.00"
uo_form.of_field(/*keys*/ "account/credit_limit").is_max = "50000"
uo_form.of_add_field(/*keys*/ "account/country", /*label*/ "Country", /*type*/ u_pbt_form.TYPE_ENUM, /*value*/ "gb")
uo_form.of_add_option(/*keys*/ "account/country", /*value*/ "gb", /*text*/ "United Kingdom")
uo_form.of_add_option(/*keys*/ "account/country", /*value*/ "fr", /*text*/ "France")
uo_form.of_add_field(/*keys*/ "account/vip", /*label*/ "VIP customer", /*type*/ u_pbt_form.TYPE_BOOLEAN, /*value*/ "false")

// Shown at once, the keyboard on the first field
uo_form.of_set_redraw(/*on*/ true)
uo_form.of_focus_field(/*keys*/ "identity/first_name")

Editar una fila de un DataWindow #

La clave de cada campo es el nombre de su columna: el formulario lee la fila, el usuario la modifica, el formulario la escribe.

// The current row of dw_customers fills the form ; nothing is dirty yet
uo_form.of_load_row(/*data*/ dw_customers, /*row*/ dw_customers.GetRow())
// event clicked of cb_save
if uo_form.of_validate() > 0 then return

// Write the values back into the row, then save it
if uo_form.of_save_row(/*data*/ dw_customers, /*row*/ dw_customers.GetRow()) < 0 then
    MessageBox("Save", uo_form.of_get_last_error())
    return
end if

// Saved : what is on screen is the new reference
if dw_customers.Update() = 1 then
    COMMIT USING SQLCA;
    uo_form.of_reset_update()
end if

Etiquetas y estrellas #

Una opinión sobre un producto: una nota sobre cinco con medias estrellas, y etiquetas libres con sugerencias. Los dos valores siguen siendo texto, que of_save_row escribe en una columna numérica y en una columna de texto.

// Local variables
string ls_score, ls_tags

// The score : five stars, half stars set BEFORE a half value
uo_form.of_add_field(/*keys*/ "score", /*label*/ "Overall", /*type*/ u_pbt_form.TYPE_RATING, /*value*/ "")
uo_form.of_field(/*keys*/ "score").ib_half_stars = true
uo_form.of_field(/*keys*/ "score").is_value = "3.5"

// Tags : free text, the options are suggestions, five at most
uo_form.of_add_field(/*keys*/ "tags", /*label*/ "Tags", /*type*/ u_pbt_form.TYPE_TAGS, /*value*/ "office, led")
uo_form.of_add_option(/*keys*/ "tags", /*value*/ "eco", /*text*/ "Eco-friendly")
uo_form.of_add_option(/*keys*/ "tags", /*value*/ "usb", /*text*/ "USB powered")
uo_form.of_field(/*keys*/ "tags").ii_max_tags = 5

// Read back : "3.5" and "office, led"
ls_score = uo_form.of_field(/*keys*/ "score").is_value
ls_tags = uo_form.of_field(/*keys*/ "tags").is_value

Avisar antes de perder cambios #

// event closequery of the window
if not uo_form.of_is_dirty() then return 0

// Changes since the load : ask, and put them back if the user gives up
if MessageBox("Customer", "Discard your changes ?", Question!, YesNo!) = 2 then return 1
uo_form.of_revert()
return 0

Buenas prácticas #

Heredado de la base común #

Estos miembros existen en todos los componentes visuales — no son propios de este. Se detallan una sola vez, en los capítulos transversales; esta tabla solo dice dónde leerlos.

MiembrosFunciónDetallado en
of_count · of_keys_at · of_hasRecorrer lo que el componente contiene3.2 Los items
of_resetPoner el componente a cero3.6 Poner un componente a cero: of_reset()
of_register_shortcut · of_clear_shortcutsAtajos de teclado del componente3.5 Los atajos de teclado
of_is_created · of_is_ready · of_get_last_errorSi ha nacido, si está listo, qué ha fallado3.7 Diagnóstico
of_save_as_png · of_save_as_jpgExportar el render como imagen3.8 Exportar la representación como imagen
of_set_redrawAgrupar los cambios en un solo repintado3.10 Buenas prácticas
of_preload_iconsIconos mostrados sin retardoVisualización instantánea: of_icon
of_set_translationTraducir una etiqueta del componente5.2 Adaptar una etiqueta: of_set_translation
of_focus_webviewDar el foco al componente6.4 Teclado y foco
of_print · of_print_to_pdfImprimir, o escribir un PDF6.9 Imprimir
of_set_property · of_get_property · of_component_nameControlar una propiedad por su nombre3.1 El motor de propiedades
is_accessible_name · is_accessible_descriptionEl nombre y la descripción que dice un lector de pantalla3.4 Propiedades y eventos opcionales

Dos ayudas no se heredan: of_icon y of_escape_markup viven en n_pbt_utils. Declare uno — n_pbt_utils lnv_utils, nada que crear — y llámelas sobre él.

← Referencia de componentes · Índice de la guía