PBToolboxAI v5 ← Site

form — u_pbt_form #

← Référence des composants · Sommaire du guide

form est un écran de saisie entier dans un seul contrôle : des champs typés — texte, nombre, liste, boutons radio, date, heure, couleur, case à cocher, mot de passe, e-mail, texte long, image, étiquettes, étoiles — rangés en colonnes et en sections qui se replient, chacun avec son libellé qui remonte dans sa bordure. Les règles de chaque champ (obligatoire, bornes, motif, masque…) et vos erreurs métier se vérifient ensemble ; of_is_dirty dit ce qui a changé depuis le chargement, of_load_row / of_save_row lisent et écrivent une ligne de DataWindow. Votre propre bouton OK appelle of_validate.

▶ Le voir en vrai — Application de démonstration, tuile Form : l'aperçu, le code qui le produit et cette page, côte à côte.


En bref #

Userobjectu_pbt_form
Classes d'itemsn_pbt_form_section (section) → n_pbt_form_field (champ)
Sert àRemplacer un écran de saisie fait de dizaines de contrôles posés à la main par un formulaire thémé, mis en page tout seul, qui vérifie ce qu'on y tape : fiche client, commande, paramètres, assistant d'une étape
ValeursToujours du texte, une par une (of_field(adresse).is_value) ; toutes d'un coup et typées par of_values ; une ligne de DataWindow par of_load_row / of_save_row
Options opt-inib_veto_changes

Démarrage rapide #

// 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"))

Une adresse par champ #

Un champ se désigne par son adresse : sa seule clé au premier niveau ("name"), la section puis le champ joints par / dans une section ("order/qty") — un niveau, jamais plus. Une section se désigne par sa clé seule. Au premier niveau, une clé ne nomme qu'une chose : une section et un champ du haut ne peuvent pas la partager. Les events portent la même adresse, celle que vous avez écrite ; la clé d'un champ (son dernier niveau) est aussi le nom de la colonne que lisent of_load_row et 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"

Les types de champ #

Le type se choisit une fois, à of_add_field, et donne le contrôle. La valeur, elle, est toujours du texte — donnée, lue, rendue par les events — écrite de la même manière quelle que soit la langue du poste. Une liste, une date et une couleur s'ouvrent dans les fenêtres natives partagées par toute la bibliothèque, aux couleurs du thème.

ConstanteContrôleLa valeur, en texte
TYPE_STRINGZone de texte ; un masque (is_mask, syntaxe d'un EditMask) montre ses littéraux pendant la frappe ; un bouton « … » (ib_ellipsis) ouvre votre rechercheLe texte tel quel ; masqué, sans ses littéraux (5551234567)
TYPE_TEXTAREATexte sur plusieurs lignes ; Entrée va à la ligneLe texte, retours à la ligne compris
TYPE_NUMBERZone de texte qui lit un nombre dans la langue d'affichage : son séparateur décimal ou un point, son groupement des milliers bien formé (1,000 vaut mille en anglais, 1.500 mille cinq cents en allemand, 1 234,5 en français). Une saisie ambiguë (1,5 en anglais, 1 2) ou qui n'est pas un nombre reste à l'écran avec le message Not a number traduit, et la valeur en place ne bouge pas ; Haut / Bas l'augmentent ou le diminuent de son pas (id_step)Avec un point : "12.5" ; "" = vide. Gardée chiffre pour chiffre ; 12.50 retapé pour 12.5 n'est pas un changement
TYPE_BOOLEANInterrupteur (par défaut) ou case à cocher (is_boolean_style), son libellé à côté"true" · "false"
TYPE_ENUMBouton qui ouvre la liste partagée ; ses options s'ajoutent une par une (of_add_option)La valeur de l'option choisie, jamais son texte ; une valeur qu'aucune option n'offre est gardée et montrée telle quelle, comme un DataWindow sans table de codes
TYPE_RADIOUn bouton radio par option, côte à côte ; les flèches passent de l'un à l'autreLa valeur de l'option choisie
TYPE_DATEBouton qui ouvre le calendrier partagé ; la date s'affiche au format de la langue d'affichage"yyyy-mm-dd"
TYPE_TIMEZone de texte : 9:5 devient 09:05 ; ce qui n'est pas une heure de 24 h reste à l'écran avec son message"hh:mm"
TYPE_COLORBouton avec la pastille de la couleur, qui ouvre le sélecteur partagéLe long RGB écrit en texte : String(RGB(255, 0, 0)) = "255"
TYPE_PASSWORDZone de texte masquéeLe texte tel quel
TYPE_EMAILZone de texte qui vérifie la forme d'une adresseLe texte tel quel
TYPE_IMAGEUne image, pas une saisie : jamais validée, jamais dans of_values ; avec ii_rowspan, elle couvre plusieurs lignes (la photo d'une fiche)Sa source : un fichier, ou mono: / tint: suivi d'un chemin (aux couleurs du thème) ; une adresse http:// est refusée
TYPE_TAGSDes étiquettes en pastilles dans une boîte : on tape un mot puis Entrée, une virgule ou Tab ; Retour arrière dans la saisie vide retire la dernière, chaque pastille a sa croix, Gauche / Droite les parcourent. Les options du champ (of_add_option) sont des suggestions filtrées pendant la frappe ; ib_options_only les rend obligatoires, ii_max_tags borne leur nombreLes étiquettes jointes par une virgule et une espace : "vip, b2b" (la valeur de l'option pour une suggestion choisie). La virgule est le séparateur : jamais dans une étiquette ; écrite par le code, elle découpe, les espaces autour partent, la même étiquette deux fois (sans tenir compte de la casse) n'est gardée qu'une fois
TYPE_RATINGDes étoiles à cliquer, ii_star_count (5 par défaut), des demi-étoiles avec ib_half_stars. Le survol prévisualise sans rien déplacer ; un clic sur la valeur en place l'efface. Au clavier : flèches, Début / Fin, un chiffre, 0 ou Suppr. Une étoile pleine prend l'accent du composantLe nombre d'étoiles : "4", "3.5" ; "" = aucune note (0 aussi). Écrite par le code, arrondie au pas et gardée dans les étoiles ; un texte ou un négatif est refusé

La validation #

Chaque champ porte ses règles : ib_required (une case à cocher obligatoire doit être cochée), is_min / is_max (un nombre, une date, une heure), ii_max_length, is_pattern (une expression régulière que la valeur entière doit suivre), is_mask (un masque rempli), is_match (égal à un autre champ), et la forme d'un e-mail, d'un nombre ou d'une heure. Votre application y ajoute ses erreurs métier (is_error : déjà pris), qui restent jusqu'à ce que l'utilisateur modifie le champ. Rien ne s'affiche en rouge tant que l'utilisateur n'a pas quitté un champ : un formulaire neuf n'est pas rouge. of_is_valid et of_invalid_keys répondent sans rien montrer ; of_validate montre tout d'un coup et met le clavier sur le premier champ en erreur. Un champ masqué, désactivé ou en lecture seule n'est pas jugé par ses règles : l'utilisateur ne pourrait pas le corriger.

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

Propriétés #

PropriétéTypeDéfautRôle
ii_columnsinteger1Nombre de colonnes, de 1 à 12 (une valeur hors bornes y est ramenée). Chaque section suit. Un champ en couvre plus avec ii_colspan, commence dans une colonne donnée avec ii_col, couvre plusieurs lignes avec ii_rowspan ; les autres champs remplissent les cases libres
ib_readonlybooleanfalseLe formulaire entier en lecture seule : chaque valeur se lit, aucune ne se change — une fiche qu'on consulte. Votre code peut toujours écrire les valeurs
ib_veto_changesbooleanfalseOpt-in : demander avant d'écrire une valeur de l'utilisateur (déclenche ue_value_changing, qui peut refuser : le champ reprend l'ancienne valeur). Une valeur écrite par votre code ne pose jamais la question
is_theme_stylestring""Style visuel du composant (constantes THEME_STYLE_*) ; vide = celui de l'application, suivi à chaque changement
is_theme_modestring""Variante claire ou sombre (constantes THEME_MODE_*) ; vide = celle de l'application, suivie à chaque changement
il_theme_accentlong-1Couleur d'accent de ce composant (-1 = l'accent de l'application, ou celui du thème)
is_tooltipstring""Info-bulle simple affichée au survol du composant
is_super_tooltip_titlestring""Titre de l'info-bulle enrichie (prend le pas sur is_tooltip)
is_super_tooltip_textstring""Texte de l'info-bulle enrichie (balisage riche accepté)
is_super_tooltip_imagestring""Image de l'info-bulle enrichie

Propriétés d'une section — n_pbt_form_section #

PropriétéTypeDéfautRôle
is_titlestring""Titre de l'en-tête. Accepte le balisage riche. Vide, l'en-tête montre la clé
ib_collapsedbooleanfalsetrue replie la section : son en-tête reste, ses champs se cachent — ils gardent leurs valeurs et restent jugés. ue_section_toggled suit, comme pour un clic sur l'en-tête. Lu en direct
ib_visiblebooleantruefalse retire la section, et tous ses champs, du formulaire ; ils gardent leurs valeurs (of_values les rend toujours) et ne sont pas jugés
is_tooltipstring""Info-bulle simple affichée au survol de l'item
is_super_tooltip_titlestring""Titre de l'info-bulle enrichie de l'item (prend le pas sur is_tooltip)
is_super_tooltip_textstring""Texte de l'info-bulle enrichie de l'item (balisage riche accepté)
is_super_tooltip_imagestring""Image de l'info-bulle enrichie de l'item
il_accentlong-1Accent de l'en-tête de la section : le trait sous son titre le prend (-1 = celui du composant). Ses champs gardent le leur
il_back_colorlong-1Fond de l'en-tête de la section, en RGB(r,v,b) (-1 = ce que donne le thème)
il_text_colorlong-1Couleur du texte de l'en-tête de la section (-1 = ce que donne le thème)
il_back_color_hoverlong-1Fond de l'en-tête de la section sous le pointeur (-1 = ce que donne le thème)
il_text_color_hoverlong-1Couleur du texte de l'en-tête de la section sous le pointeur (-1 = ce que donne le thème)

Propriétés d'un champ — n_pbt_form_field #

PropriétéTypeDéfautRôle
is_valuestring""La valeur, en texte (voir les types). L'écrire ne lève rien, comme SetItem ; la lire rend la valeur courante, qui que ce soit qui l'ait écrite — ce qui est en cours de frappe compte. Une valeur que le type ne peut pas tenir (une date qui n'existe pas, un texte dans un nombre) est refusée : l'ancienne reste et of_get_last_error dit pourquoi
is_labelstring""Le libellé, dans le champ : il remonte dans la bordure quand le champ a une valeur ou le clavier. Accepte le balisage riche. Une étoile suit le libellé d'un champ obligatoire. Vide, la clé
is_placeholderstring""Une indication dans un champ de texte vide pendant qu'il a le clavier
is_helpstring""Une ligne d'aide sous le champ (balisage riche) ; elle se cache quand une erreur prend sa place
is_errorstring""Une erreur métier de votre application (déjà pris) : affichée tout de suite, le formulaire invalide, jusqu'à ce que l'utilisateur modifie le champ ou of_clear_errors ; "" l'efface. Lue, elle rend le message que le champ montre — ou montrera quand l'utilisateur le quittera : l'erreur métier, sinon celle de ses règles, "" s'il est valide. Sur un champ que l'utilisateur ne peut pas atteindre (masqué, désactivé), elle est gardée et se relit, mais ne compte pas : le formulaire reste valide jusqu'au retour du champ
is_minstring""La plus petite valeur acceptée d'un nombre (avec un point), d'une date (yyyy-mm-dd) ou d'une heure (hh:mm) ; le message dit Minimum et la borne. Une borne que le type ne lit pas est ignorée ; "" = aucune — pour un nombre, le bas de la SpinRange de sa colonne de DataWindow (of_load_row). Lue, elle rend la borne en vigueur
is_maxstring""La plus grande valeur acceptée, lue comme is_min ; le message dit Maximum et la borne
is_patternstring""Une expression régulière (syntaxe JavaScript) que la valeur entière doit suivre : [A-Z]{2}[0-9]{4} accepte AB1234, pas xAB1234. Une valeur vide est laissée à ib_required
is_pattern_messagestring""Le message quand is_pattern échoue ; vide : Invalid format, traduit
is_maskstring""Un masque de saisie. Sur un TYPE_STRING, la syntaxe d'un EditMask de PowerBuilder : # un chiffre, ! un caractère mis en majuscule, ^ un caractère mis en minuscule, a une lettre ou un chiffre, X n'importe quel caractère, le reste écrit pour l'utilisateur pendant qu'il tape ((###) ###-####) ; le curseur reste après ce qui a été tapé. La valeur ne garde que ce qui a été tapé dans les cases, comme un EditMask (5551234567, prête pour une colonne de chiffres) ; une valeur du code est la valeur brute, sans ses littéraux (312345678 sous +33 # ## ## ## ##), les littéraux de tête s'écrivent dès que le champ prend le clavier, une valeur qui ne remplit pas toutes les cases est Incomplete, et poser un masque après une valeur ne change rien de ce que l'utilisateur a fait. Après of_load_row, un masque vide prend l'EditMask de la colonne. Sur un TYPE_NUMBER : un masque numérique dans la syntaxe de PowerBuilder (EditMask, format DataWindow) : # un chiffre facultatif, 0 un chiffre obligatoire, , le groupement, . le séparateur décimal, % le nombre affiché multiplié par 100, des littéraux ($, un texte entre guillemets, \x) et des sections positif;négatif ("$#,##0;($#,##0)"). La virgule et le point désignent les séparateurs de la langue d'affichage. Pendant la frappe, le groupement apparaît au fil des chiffres, les décimales sont limitées au masque, les lettres sont refusées, le curseur garde sa place parmi les chiffres et un nombre collé est lu dans la langue d'affichage ; en quittant le champ, le masque complète (12 devient 12,00). La valeur garde le nombre tel que le code l'écrit ("1234.5"), sans groupement ni symbole. Une borne basse (is_min) de 0 ou plus refuse le signe moins ; les bornes s'appliquent en quittant le champ, et leur message les montre à travers le masque ; vide sur un TYPE_NUMBER (par défaut) : le champ tape son is_format dépouillé de ses littéraux ($#,##0.00 se tape #,##0.00, #,##0 "px" se tape #,##0 ; un % reste, c'est une échelle)
is_formatstring""Un TYPE_NUMBER : son masque d'affichage, la façon dont le nombre s'affiche quand le champ n'a pas le clavier — même syntaxe que is_mask, avec ses littéraux : #,##0 "px" affiche 575 px, $#,##0.00;($#,##0.00) un prix, 0.0% un taux (0,125 affiche 12,5%). Quand le champ prend le clavier, son texte devient celui de is_mask (575), le curseur gardant sa place parmi les chiffres ; en le quittant, le texte d'affichage revient. La bascule ne change jamais la valeur et ne lève rien : ue_value_changed ne rapporte que ce que l'utilisateur a tapé, le nombre nu. Vide : le champ affiche son is_mask
is_boolean_stylestring"switch"Un TYPE_BOOLEAN : son dessin. BOOLEAN_SWITCH (par défaut) : un interrupteur, une piste et un rond qui glisse vers la fin, dans l'accent du composant, quand il est allumé — un réglage marche / arrêt. BOOLEAN_CHECKBOX : la case à cocher classique — un accord, un choix parmi des options. Les deux prennent Espace et un clic sur leur libellé, donnent la même valeur et les mêmes événements ; désactivés ou en lecture seule, ils restent lisibles. Le rond glisse en un quart de seconde, pas du tout quand Windows demande moins d'animations ; dans une langue écrite de droite à gauche, il glisse vers la gauche
ii_max_tagsinteger0Un TYPE_TAGS : le nombre maximal d'étiquettes (0 = sans limite). Une de plus tapée par l'utilisateur est refusée : elle reste dans la saisie avec le message At most N ; une liste plus longue écrite par le code est gardée, et le champ est invalide (of_is_valid, is_error) — montré une fois le champ touché ou après of_validate, jamais d'emblée
ib_options_onlybooleanfalseUn TYPE_TAGS : true limite ses étiquettes aux options du champ (of_add_option) — un texte qui n'en nomme aucune (par son texte ou sa valeur, sans casse ni accent) est refusé avec Not in the list. false : tout texte est une étiquette, les options ne sont que des suggestions
ii_star_countinteger5Un TYPE_RATING : le nombre d'étoiles, de 1 à 10 (un nombre hors bornes y est ramené). La valeur en place y est gardée, sans event : poser le nombre avant une valeur au-delà de cinq
ib_half_starsbooleanfalseUn TYPE_RATING : true permet les demi-étoiles — la première moitié de l'étoile sous le pointeur (dans le sens de lecture), Gauche / Droite d'une demi-étoile — et la valeur s'écrit "3.5". Désactivé, la valeur en place est arrondie à l'étoile, sans event : l'activer avant une demi-valeur
is_matchstring""L'adresse d'un autre champ auquel celui-ci doit être égal — répéter un mot de passe, un e-mail. Le message suit chaque changement de l'un ou de l'autre
is_prefixstring""Un texte court dans le champ, avant la valeur (un symbole monétaire) ; il ne fait pas partie de la valeur
is_suffixstring""Un texte court dans le champ, après la valeur (une unité) ; il ne fait pas partie de la valeur
ii_max_lengthinteger0Le texte le plus long accepté (0 = sans limite) par un TYPE_STRING, TYPE_TEXTAREA, TYPE_PASSWORD ou TYPE_EMAIL — un nombre ou une heure n'a pas de longueur, ignoré ; 0 prend la Limit de sa colonne de DataWindow (of_load_row), lu il rend la longueur en vigueur : la frappe s'arrête là, un compteur s'affiche sous un champ de texte ; une valeur plus longue écrite par le code est Too long. Sur un TYPE_TAGS : l'étiquette la plus longue — un collage de plusieurs étiquettes n'est jamais coupé, chacune est jugée
ii_colspaninteger1Le nombre de colonnes que couvre le champ (au plus ii_columns) ; -1 = toute la largeur
ii_rowspaninteger1Le nombre de lignes que couvre le champ — une image à côté de plusieurs champs ; les suivants remplissent les cases libres à côté
ii_colinteger0La colonne où le champ commence, à partir de 1 ; 0 = là où le flux le pose. La touche Tab suit l'ordre de l'écran — les champs du haut d'abord, puis chaque section —, quelle que soit la colonne
ib_requiredbooleanfalseLe champ doit être rempli — une case à cocher doit être cochée (une case J'accepte), un champ d'étiquettes en tenir une, une note avoir une étoile. Une étoile suit son libellé ; l'erreur Required paraît quand l'utilisateur quitte le champ ou à of_validate. Une colonne dont le style d'édition dit Required le rend obligatoire aussi (of_load_row) ; lu, il rend l'état en vigueur
ib_readonlybooleanfalseMontre la valeur sans la laisser changer : un texte peut encore être sélectionné et copié ; une liste, une date, une couleur, un interrupteur ou des boutons radio gardent le clavier (Tab les atteint, un lecteur d'écran dit lecture seule) mais ne s'ouvrent ni ne changent. Pas jugé par ses règles ; votre code peut toujours écrire is_value
ib_enabledbooleantruefalse grise le champ : plus de clavier (il quitte l'ordre de tabulation), plus de clic, pas jugé par ses règles
ib_visiblebooleantruefalse retire le champ du formulaire ; il garde son adresse et sa valeur (of_values la rend toujours) et n'est pas jugé
is_columnstring""La colonne du DataWindow que of_load_row et of_save_row lisent et écrivent pour ce champ ; "" (par défaut) = sa clé, le dernier niveau de son adresse. Deux sections peuvent ainsi porter la même clé (billing/town, shipping/town) liée à deux colonnes, et un champ garder une clé lisible pour une colonne nommée cust_nm
id_stepdouble0Un TYPE_NUMBER : ce que Haut / Bas (et ses boutons, ib_spin) ajoutent ou retirent, gardé entre is_min et is_max ; 0 (par défaut) = la SpinIncr de sa colonne de DataWindow (of_load_row), sinon 1. Un pas est un geste de l'utilisateur : ue_value_changed est levé (et ue_value_changing posé d'abord avec ib_veto_changes). Un champ vide part de is_min, sinon de 0 ; un pas négatif est refusé
ib_ellipsisbooleanfalseUn champ de texte (TYPE_STRING, TYPE_NUMBER, TYPE_EMAIL…) : true montre un bouton … à son bout — votre recherche, un client, un article. Un clic dessus, ou Ctrl+Entrée dans le champ, lève ue_ellipsis_clicked ; il reste hors de l'ordre de Tab. Il marche aussi sur un champ en lecture seule (une valeur choisie par la seule recherche), jamais sur un champ désactivé
ib_spinbooleanfalseUn TYPE_NUMBER : true montre deux petits boutons à son bout, haut et bas, qui ajoutent ou retirent id_step entre is_min et is_max — un geste de l'utilisateur (ue_value_changed). La molette le fait aussi tant que le champ a le clavier ; les boutons restent hors de l'ordre de Tab (Haut / Bas dans le champ font pareil) et se cachent sur un champ en lecture seule. Une colonne dont l'EditMask dit Spin les donne par of_load_row, avec sa SpinIncr pour pas et sa SpinRange pour bornes. Ignoré par les autres types ; lu, il rend l'état en vigueur
of_type ( ) → string——Le type donné à of_add_field (une constante TYPE_*). Lecture seule : un type se choisit une fois
of_is_dirty ( ) → boolean——TRUE quand ce champ diffère de sa référence (voir of_is_dirty du formulaire)
of_is_valid ( ) → boolean——TRUE quand ce champ ne porte aucune erreur ; is_error dit laquelle
is_tooltipstring""Info-bulle simple affichée au survol de l'item
is_super_tooltip_titlestring""Titre de l'info-bulle enrichie de l'item (prend le pas sur is_tooltip)
is_super_tooltip_textstring""Texte de l'info-bulle enrichie de l'item (balisage riche accepté)
is_super_tooltip_imagestring""Image de l'info-bulle enrichie de l'item
il_accentlong-1Accent du champ : sa bordure le prend quand il a le clavier, et son libellé une fois remonté (-1 = celui du composant)
il_back_colorlong-1Fond du champ, en RGB(r,v,b) (-1 = ce que donne le thème)
il_text_colorlong-1Couleur du texte du champ (-1 = ce que donne le thème)
il_back_color_hoverlong-1Fond du champ sous le pointeur (-1 = ce que donne le thème)
il_text_color_hoverlong-1Couleur du texte du champ sous le pointeur (-1 = ce que donne le thème)

Méthodes #

Sur le composant #

MéthodeRôle
of_add_section (string as_key, string as_title) → longAjoute une section à la fin : un en-tête qui se replie, souligné de l'accent, sous lequel s'ajoutent ensuite les champs "clé/nom". Un titre vide montre la clé. Rend 0 une fois ajoutée, -5 pour une clé vide, qui contient / ou `, ou déjà prise par une section ou un champ du haut, -2` si le composant n'est pas créé
of_add_field (string as_keys, string as_label, string as_type, string as_value) → longAjoute un champ à son adresse, à la fin de ce qui le contient : sa section ou le haut du formulaire. as_type est une constante TYPE_*, as_value sa valeur en texte (la source d'une image). Rend 0 une fois ajouté, -5 pour une adresse déjà prise (un champ, ou une section au premier niveau), un niveau vide, plus de deux niveaux, une section jamais ajoutée, une clé qui contient `, un type inconnu ou une valeur que le type ne peut pas tenir (of_get_last_error dit pourquoi), -2` si le composant n'est pas créé
of_add_option (string as_keys, string as_value, string as_text) → longAjoute une option, à la fin, à une liste TYPE_ENUM, à des boutons TYPE_RADIO ou aux suggestions d'un TYPE_TAGS : as_value est ce que le champ contient (et ce que donne ue_value_changed), as_text ce que l'utilisateur lit, en texte simple ("" montre la valeur). Rend 0 une fois ajoutée, -5 pour une adresse qui n'est pas une liste, des boutons radio ou des étiquettes, une valeur déjà offerte, une valeur qui contient ` ou / (of_keys_at la joint à l'adresse du champ) — et pour des étiquettes une valeur vide ou qui contient une virgule —, -2` si le composant n'est pas créé
of_remove_option (string as_keys, string as_value) → longRetire une option d'une liste, de boutons radio ou d'étiquettes, par sa valeur ; la valeur du champ reste telle quelle. Rend 0 une fois retirée, -5 pour une adresse qui n'est pas une liste, des boutons radio ou des étiquettes, ou une valeur qu'elle n'offre pas, -2 si le composant n'est pas créé
of_clear_options (string as_keys) → longRetire toutes les options d'une liste, de boutons radio ou d'étiquettes, pour charger une autre liste sans perdre le champ, son handle ni son état ; sa valeur reste telle quelle. Rend 0 une fois vidée, -5 pour une adresse qui n'est pas une liste, des boutons radio ou des étiquettes, -2 si le composant n'est pas créé
of_remove_field (string as_keys) → longRetire un champ par son adresse, avec son handle ; le reste du formulaire garde son état. Rend 0 une fois retiré, -5 pour une adresse jamais ajoutée, -2 si le composant n'est pas créé
of_remove_section (string as_key) → longRetire une section et tous ses champs, avec leurs handles. Rend 0 une fois retirée, -5 pour une section jamais ajoutée, -2 si le composant n'est pas créé
of_clear ( ) → longVide le formulaire : sections, champs et leurs handles. Contrairement à of_reset, les colonnes, la lecture seule et la question restent. Rend 0 une fois vidé, -2 si le composant n'est pas créé
of_section (string as_key) → n_pbt_form_sectionLe handle d'une section, par sa clé : titre, pli, visibilité, couleurs et info-bulle. Créé au premier accès
of_field (string as_keys) → n_pbt_form_fieldLe handle d'un champ, par son adresse : of_field("order/qty").is_value = "3". Créé au premier accès
of_values ( ) → stringToutes les valeurs d'un coup, lues en direct, en un objet JSON : les champs du haut, puis chaque section comme un objet à elle — masqués compris, images exclues ; ce que l'utilisateur tape encore est validé d'abord, comme le fait AcceptText : ue_value_changing posé avec ib_veto_changes, puis ue_value_changed levé — l'un et l'autre avant la lecture, jamais au milieu : votre OK lit ce qui est à l'écran. Typées : un nombre est un nombre (écrit chiffre pour chiffre, null s'il est vide), un booléen un booléen, une couleur son long RGB, le reste du texte. Se lit avec n_pbt_json, par la même adresse : lnv_json.of_get_number("order/qty")
of_load (string as_json) → longCharge un enregistrement : un objet JSON de valeurs par adresse — les champs du haut, puis un objet par section, ou des clés "order/qty" —, la forme que rend of_values ; nombres et booléens peuvent être typés — un nombre de plus de 15 chiffres est lu chiffre pour chiffre, un booléen vaut true, false, 1 ou 0 (toute autre valeur est refusée). Les valeurs s'écrivent en silence et deviennent la référence de of_is_dirty ; les erreurs affichées et les erreurs métier s'effacent. Un champ que l'objet ne nomme pas garde sa valeur. Rend 0 une fois chargé, -5 pour un texte qui n'est pas un objet JSON, ou une valeur que son champ ne peut pas tenir (abc pour un nombre) — rien n'est écrit alors, of_get_last_error nomme les champs —, -2 si le composant n'est pas créé
of_load_row (powerobject apo_data, long al_row) → longCharge une ligne d'un DataWindow, d'un DataStore ou d'un DataWindowChild : chaque champ prend la valeur de sa colonne — celle que nomme is_column, sinon sa clé (le dernier niveau de son adresse) —, typée — une date yyyy-mm-dd, une heure hh:mm, un nombre avec un point, un booléen lu par la valeur On d'une colonne case à cocher (sinon 1, Y, true…). Ce que le DataWindow dit de la colonne vient avec, relu à chaque chargement : un champ sans libellé montre le texte d'en-tête de sa colonne, un champ sans masque ni format prend son EditMask et son Format (un nombre $#,##0), une liste ou des boutons radio sans option à eux offrent sa table de codes ou les lignes de sa DropDownDataWindow ; un champ sans règle à lui devient obligatoire quand le style d'édition dit Required et limité à sa Limit, et un nombre dont l'EditMask dit Spin reçoit ses boutons (ib_spin), son pas (SpinIncr) et ses bornes (SpinRange). La ligne suivante du même DataWindow ne redessine aucun champ : celui qui a le clavier le garde. Comme of_load, les valeurs deviennent la référence. Rend 0 une fois chargée, -5 pour un objet qui n'est ni un DataWindow, ni un DataStore, ni un DataWindowChild, une ligne qui n'existe pas, ou une valeur que son champ ne peut pas tenir (rien ne change alors : ni une valeur, ni ce que dit le DataWindow), -2 si le composant n'est pas créé
of_save_row (powerobject apo_data, long al_row) → longÉcrit les valeurs que l'utilisateur a changées (depuis la référence : of_load_row, of_reset_update) dans une ligne d'un DataWindow, d'un DataStore ou d'un DataWindowChild : chaque champ changé dans sa colonne (is_column, sinon sa clé), par SetItem, typé pour la colonne (date, datetime, heure, nombre, texte ; un booléen en valeur On / Off d'une colonne case à cocher, 1 / 0 dans une colonne nombre, true / false dans une colonne texte ; un champ vide écrit un NULL — un texte vide, un NULL là où sa colonne dit NilIsNull, une chaîne vide sinon, comme le DataWindow). Un champ que l'utilisateur n'a pas changé n'est pas écrit : la ligne garde son statut, votre Update n'envoie que ce qui a changé. Un TYPE_DATE garde l'heure d'une colonne datetime, un TYPE_TIME sa date. Ce que l'utilisateur tape encore est validé d'abord, et ue_value_changed levé, avant toute lecture ; l'Update reste le vôtre. Rend 0 une fois écrite, -5 pour un objet qui n'est ni un DataWindow, ni un DataStore, ni un DataWindowChild, ou une ligne qui n'existe pas, -4 quand une colonne est restée telle quelle — un SetItem refusé, un champ dont la colonne ne peut pas tenir le type (un TYPE_STRING dans une colonne date), ou une valeur à décimales pour une colonne entière (3.5 étoiles dans un long : SetItem l'arrondirait) : of_get_last_error nomme la première, les autres champs changés sont écrits quand même —, -2 si le composant n'est pas créé
of_is_valid ( ) → booleanTRUE quand aucun champ affiché ne porte d'erreur : chaque champ obligatoire rempli (une case obligatoire cochée), chaque règle suivie, aucune erreur métier. Lu en direct, sans rien montrer ; une erreur métier ne compte que sur un champ que l'utilisateur peut atteindre (affiché et actif)
of_invalid_keys (ref string as_keys[]) → longRemplit as_keys[] avec les adresses des champs en erreur, dans l'ordre de l'écran, sans rien montrer, et rend leur nombre (0 = le formulaire est valide)
of_validate ( ) → longMontre toutes les erreurs d'un coup et met le clavier sur le premier champ en erreur (ue_focus_changed) — ce qu'appelle votre bouton OK avant d'enregistrer. Le clavier quitte le bouton pour le formulaire : la touche suivante va au champ ; ce que l'utilisateur tape encore est validé d'abord, et un lecteur d'écran lit les messages tout de suite. Rend le nombre de champs en erreur (0 = le formulaire est valide, rien ne bouge), -2 si le composant n'est pas créé
of_clear_errors ( ) → longEfface toutes les erreurs métier (is_error écrit par votre code) ; les erreurs des règles restent. Rend 0 une fois appliqué, -2 si le composant n'est pas créé
of_is_dirty ( ) → booleanTRUE quand au moins un champ diffère de sa référence : la valeur donnée à l'ajout, ou ce que of_load, of_load_row ou of_reset_update en ont fait ; ce que l'utilisateur tape encore est validé d'abord. Un nombre écrit autrement (12.50 pour 12.5) est la même valeur
of_dirty_keys (ref string as_keys[]) → longRemplit as_keys[] avec les adresses des champs modifiés depuis la référence, dans l'ordre de l'écran, et rend leur nombre
of_reset_update ( ) → longLes valeurs à l'écran deviennent la référence : of_is_dirty répond de nouveau FALSE. À appeler une fois votre Update réussi, comme ResetUpdate d'un DataWindow. Rend 0 une fois appliqué, -2 si le composant n'est pas créé
of_revert ( ) → longRemet chaque champ à sa valeur de référence, en silence, et cache les erreurs — l'Annuler de l'utilisateur. Rend 0 une fois appliqué, -2 si le composant n'est pas créé
of_focus_field (string as_keys) → longMet le clavier sur un champ, comme un clic : sa section repliée s'ouvre d'abord (ue_section_toggled), ue_focus_changed suit. Rend 0 une fois fait, -5 pour une adresse jamais ajoutée ou un champ que l'utilisateur ne peut pas atteindre (masqué, désactivé — celui qui avait le clavier compris —, une image, au-delà du plafond de démonstration), -2 si le composant n'est pas créé
of_current_key ( ) → stringAdresse du champ qui a le clavier — ou l'avait en dernier, pendant que le clavier est ailleurs dans la fenêtre : "order/qty", "" si aucun encore. Toujours la courante, lue au composant ; c'est aussi ce que donne ue_focus_changed
of_get_last_error ( ) → stringPourquoi la dernière écriture a été refusée : une valeur que le type ne peut pas tenir (is_value n'a pas de code retour ; une image http://, un nombre infini comme 1e400), un is_pattern que le formulaire ne sait pas lire, une valeur que of_load a refusée, une colonne que of_save_row a laissée. Vide quand elle est passée ; sinon la dernière erreur de la bibliothèque, comme pour tout composant
of_reset ( ) → longVide le formulaire puis ramène chaque propriété du composant à sa valeur par défaut : une colonne, ni lecture seule ni question. Rend 0 une fois appliqué, -2 si le composant n'est pas créé

Sur une section et sur un champ #

Une section contient des champs ; une liste ou des boutons radio contiennent leurs options, désignées par leur valeur. Les trois méthodes les parcourent.

MéthodeRôle
of_count ( ) → longNombre de champs de la section, masqués compris ; nombre d'options d'une liste, de boutons radio ou d'étiquettes (0 pour les autres types)
of_keys_at (long al_index) → stringAdresse du champ de rang al_index (à partir de 1) — "order/qty", réinjectable telle quelle dans of_field ; sur une liste, l'adresse suivie de la valeur de l'option. "" hors bornes
of_has (string as_key) → booleanLa section contient-elle ce champ ? La liste offre-t-elle cette valeur ?

Événements #

ÉvénementDéclenché quand
ue_value_changed (string as_keys, string as_value)L'utilisateur a changé une valeur : saisie validée en quittant le champ ou par Entrée, case cochée, bouton radio, option, date ou couleur choisie (Haut / Bas parcourent une liste, Suppr vide une liste, une date ou une couleur). as_keys est l'adresse du champ, as_value sa nouvelle valeur en texte ; elle est déjà écrite. Une valeur écrite par votre code ne lève rien, comme SetItem. Ce que l'utilisateur tape encore quand votre code lit le formulaire (of_values, of_save_row, is_value…) est validé à ce moment, comme le fait AcceptText : l'événement est levé là, avant la lecture
ue_value_changing (string as_keys, string as_value) → booleanAnnulable, posé avant qu'une valeur de l'utilisateur ne soit écrite, et seulement quand ib_veto_changes = true (désactivé par défaut). Renvoyer FALSE refuse : le champ reprend l'ancienne valeur, que of_field(as_keys).is_value rend encore pendant l'événement. Renvoyez TRUE pour accepter. Quand votre code lit le formulaire (of_values, of_validate, is_value…) pendant que l'utilisateur tape encore dans un champ, la question est posée tout de suite, avant la lecture : la lecture rend la valeur que vous avez acceptée
ue_focus_changed (string as_from_keys, string as_keys)Le champ qui a le clavier a changé : un clic, la touche Tab, votre code (of_focus_field, of_validate — un ordre le lève aussi, comme SetColumn), ou le champ est parti : retiré, masqué, vidé (as_keys vaut alors ""). Le clavier qui quitte le formulaire pour un autre contrôle de la fenêtre ne lève rien
ue_section_toggled (string as_key, boolean ab_collapsed)Une section s'est repliée (ab_collapsed = TRUE) ou dépliée : un clic sur son en-tête (Entrée ou Espace quand il a le clavier), son ib_collapsed posé par votre code, ou of_focus_field sur un champ d'une section repliée
ue_ellipsis_clicked (string as_keys)Le bouton … d'un champ (ib_ellipsis) a été cliqué, ou Ctrl+Entrée pressé dans le champ : ouvrez votre recherche (un client, un article) et écrivez ce que l'utilisateur a choisi dans of_field(as_keys).is_value. Ce qui était tapé est déjà validé (ue_value_changing posé d'abord avec ib_veto_changes, ue_value_changed levé). Jamais levé pour un champ désactivé
ue_ready ( )Le composant a fini de charger ; tout ce qui a été envoyé avant a été rejoué
ue_runtime_missing ( )Le runtime WebView2 est absent : le composant reste vide
ue_bg_color (long al_color)Le composant a calculé sa couleur de fond de thème ; l'userobject l'a déjà adoptée (backcolor)

Clavier et souris #

GesteEffet
Tab, Maj+TabPassent d'un champ à l'autre, dans l'ordre de l'écran : les champs du haut, puis chaque section, son en-tête d'abord ; un champ désactivé est sauté ; des boutons radio sont un arrêt, les flèches choisissent
Entrée dans un champ d'une ligneValide la saisie puis va à la fenêtre : son bouton par défaut (votre OK) lit un formulaire à jour. Avec ib_veto_changes, la page ne pose pas la question : la saisie reste en attente, et la lecture du bouton par défaut la valide (ue_value_changing posé à ce moment) — sans bouton par défaut, la sortie du champ la valide
ÉchapDans un champ que l'utilisateur a modifié, remet sa valeur ; sinon va à la fenêtre : son bouton Annuler
Liste, date, couleur : Entrée, Espace, F4, Alt+BasOuvrent la fenêtre partagée (liste, calendrier, sélecteur de couleur) ; Entrée s'arrête là, elle ne part pas au bouton par défaut de la fenêtre — sur un en-tête de section non plus, elle le replie ou le déplie
Liste : Haut / BasOption précédente / suivante, sans ouvrir la liste
Liste, date, couleur : Suppr, Retour arrièreVident la valeur
Étiquettes : Entrée, virgule, TabAjoutent ce qui est tapé (Tab garde le clavier dans le champ) ; Retour arrière dans la saisie vide retire la dernière étiquette ; Gauche / Droite parcourent les étiquettes, Suppr retire celle qui est marquée ; Bas ouvre les suggestions, Échap les ferme
Étoiles : flèches, Début / Fin, chiffres, SupprDroite / Haut une étoile de plus (une demie avec ib_half_stars), Gauche / Bas une de moins — inversées dans une langue écrite de droite à gauche ; Début une étoile, Fin toutes ; un chiffre autant d'étoiles ; 0, Suppr ou Retour arrière effacent la note
Nombre : Haut / BasAjoutent ou retirent le pas du champ (id_step, 1 par défaut), gardé entre is_min et is_max : ue_value_changed suit. Avec ib_spin, ses deux boutons font pareil, et la molette tant qu'il a le clavier
Ctrl+Entrée, clic sur …Un champ avec ib_ellipsis : valide ce qui est tapé puis lève ue_ellipsis_clicked — votre recherche
Clic sur un en-tête de sectionReplie ou déplie la section (ue_section_toggled)

Exemples #

Une fiche client #

// 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")

Éditer une ligne de DataWindow #

La clé de chaque champ est le nom de sa colonne : le formulaire lit la ligne, l'utilisateur la modifie, le formulaire l'écrit.

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

Étiquettes et étoiles #

Un avis sur un produit : une note sur cinq avec des demi-étoiles, et des étiquettes libres avec des suggestions. Les deux valeurs restent du texte, que of_save_row écrit dans une colonne nombre et une colonne texte.

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

Prévenir avant de perdre des modifications #

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

Bonnes pratiques #

Hérité du socle commun #

Ces membres existent sur tous les composants visuels — ils ne sont pas propres à celui-ci. Ils sont détaillés une seule fois, dans les chapitres transverses ; cette table dit seulement où les lire.

MembresRôleDétaillé dans
of_count · of_keys_at · of_hasParcourir ce que le composant contient3.2 Les items
of_resetRemettre le composant à zéro3.6 Remettre un composant à zéro : of_reset()
of_register_shortcut · of_clear_shortcutsRaccourcis clavier du composant3.5 Les raccourcis clavier
of_is_created · of_is_ready · of_get_last_errorS'il est né, s'il est prêt, ce qui a échoué3.7 Diagnostic
of_save_as_png · of_save_as_jpgExporter le rendu en image3.8 Exporter le rendu en image
of_set_redrawGrouper les modifications en un seul repaint3.10 Bonnes pratiques
of_preload_iconsIcônes affichées sans délaiAffichage instantané : of_icon
of_set_translationTraduire un libellé du composant5.2 Adapter un libellé : of_set_translation
of_focus_webviewDonner le focus au composant6.4 Clavier et focus
of_print · of_print_to_pdfImprimer, ou écrire un PDF6.9 Imprimer
of_set_property · of_get_property · of_component_namePiloter une propriété par son nom3.1 Le moteur de propriétés
is_accessible_name · is_accessible_descriptionLe nom et la description que dit un lecteur d'écran3.4 Propriétés et events optionnels

Deux aides ne sont pas héritées : of_icon et of_escape_markup vivent sur n_pbt_utils. Déclarez-en une — n_pbt_utils lnv_utils, rien à créer — et appelez-les dessus.

← Référence des composants · Sommaire du guide