PBToolboxAI v4 ← Site

crosstab — u_pbt_crosstab #

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

Tableau croisé dynamique complet : zones lignes / colonnes / valeurs alimentées depuis un DataStore, agrégations, filtres, mise en forme conditionnelle, regroupement de dates, mesures calculées et exports CSV / Excel.

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


En bref #

Userobjectu_pbt_crosstab
Classe d'items— (les champs se placent par méthodes)
Sert àDonner à vos utilisateurs une analyse croisée de vos données, qu'ils réarrangent eux-mêmes, sans écrire de SQL ni sortir vers Excel
Limite en mode démo500 lignes sources exploitées ; exports CSV et Excel et copie (Ctrl+C) désactivés — voir le mode démo

Le principe #

Vous fournissez au composant un jeu de données à plat — un DataStore, donc n'importe quelle requête déjà écrite dans votre application. Le crosstab s'occupe du reste : il en déduit la liste des champs, et vous les répartissez dans quatre zones.

ZoneCe qu'elle contientEffet
LignesDes champs de regroupementUn niveau de lignes par champ, repliable
ColonnesDes champs de regroupementUn niveau d'en-têtes de colonnes par champ
ValeursDes champs numériques et leur agrégationCe qui est calculé dans les cellules
FiltresDes champs de sélectionUn filtre au-dessus du tableau, appliqué à tout

Tout le calcul se fait dans le composant : une fois les données transmises, réarranger le tableau ne déclenche aucun aller-retour vers la base ni vers PowerBuilder.


Démarrage rapide #

// event open de la fenetre
datastore lds
lds = create datastore
lds.dataobject = "d_sales"
lds.SetTransObject(SQLCA)
lds.Retrieve()

// 1. Transmettre les donnees : les champs sont deduits des colonnes
uo_crosstab.of_from_datastore(/*data*/ lds)

// 2. Repartir les champs dans les zones
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_col_field(/*field*/ "year")
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)

// 3. Presenter : format des montants et totaux generaux
uo_crosstab.of_set_value_format(/*field*/ "amount", /*decimals*/ 0, /*thousands*/ "locale", /*suffix*/ "$", /*symbol_before*/ true)
uo_crosstab.ib_row_grand_total = true
uo_crosstab.ib_col_grand_total = true
// event ue_cell_double_clicked de uo_crosstab : (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value)
// L'utilisateur veut le detail derriere un chiffre : ouvrir la liste correspondante.
of_open_detail(as_row_tuple_json, as_col_tuple_json)

Constantes #

ConstanteValeurPour
TOTALS_BOTTOM · TOTALS_TOP"bottom" "top"is_totals_position
VALUES_COLS · VALUES_ROWS"cols" "rows"is_values_axis
AGG_SUM · AGG_COUNT · AGG_DISTINCT_COUNT"sum" "count" "dcount"of_add_value_field
AGG_AVG · AGG_MIN · AGG_MAX"avg" "min" "max"of_add_value_field
LABEL_GT · LABEL_LT · LABEL_BETWEEN"gt" "lt" "between"of_set_label_filter
LABEL_CONTAINS · LABEL_BEGINS · LABEL_ENDS"contains" "begins" "ends"of_set_label_filter

Les constantes d'agrégation se lisent sur le composant : uo_crosstab.AGG_SUM. Celles d'un champ (SHOW_*, CF_*) se lisent sur le handle du champ.


Propriétés #

PropriétéTypeDéfautRôle
is_totals_positionstring"bottom"Où se place la ligne de total général : TOTALS_BOTTOM (en pied, défaut) ou TOTALS_TOP (en tête, juste sous les en-têtes). Seul le total général se déplace : le sous-total d'un groupe reste sur la ligne du groupe
is_values_axisstring"cols"Orientation des mesures quand il y en a plusieurs : VALUES_COLS (côte à côte en colonnes, défaut) ou VALUES_ROWS (empilées en lignes)
is_currency_symbolstring""La devise que le menu Format du nombre d'un chip de valeur propose, à côté de « Aucun symbole » et « % ». Vide = celle de la langue d'affichage ($ en anglais, € ailleurs)
is_thousandsstring"locale"Séparateur de milliers des mesures qui n'en posent aucun (of_set_value_format avec un séparateur vide) — le même réglage que le menu Options de la grille : THOUSANDS_LOCALE (celui de la langue d'affichage, défaut), THOUSANDS_SPACE, THOUSANDS_NONE, ou le séparateur lui-même (",", ".", " ")
ib_field_listbooleantrueAffiche le panneau de champs, où l'utilisateur réarrange le tableau à la souris
ib_row_subtotalsbooleantrueAffiche le sous-total de chaque groupe de lignes, écrit sur la ligne du groupe lui-même, au-dessus de ses membres ; désactivé, cette ligne garde son libellé sans chiffre
ib_col_subtotalsbooleantrueAffiche un sous-total par groupe de colonnes
ib_row_grand_totalbooleantrueAffiche la ligne de total général sous le tableau (jumelle de ib_col_grand_total)
ib_col_grand_totalbooleantrueAffiche la colonne de total général après le tableau (jumelle de ib_row_grand_total)
ib_copy_headersbooleantrueCtrl+C copie les en-têtes avec les cellules sélectionnées : les noms des colonnes sur une première ligne (la mesure n'y est nommée que si le tableau la montre : plusieurs valeurs, ou aucun champ en colonnes) et le membre de chaque ligne — son chemin entier, « Nord / Lille » — dans une première colonne, dont le coin porte les noms des champs en lignes (« region / ville »). Un collage dans un tableur dit ainsi ce que sont les chiffres. false copie les chiffres seuls. Lue en direct, remise à true par of_reset
ib_enabledbooleantrueGrisé : la grille montre toujours ses chiffres — un croisé vide n'est pas la même chose qu'un croisé que l'application a éteint — mais elle cesse de répondre au pointeur
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'un champ #

of_field (string as_field) renvoie le handle d'un champ : vous le récupérez une fois, puis vous pilotez le champ par ses propriétés. Le handle est créé au premier appel et réutilisé ensuite.

PropriétéTypeDéfautRôle
is_labelstringen-tête du DataWindowLibellé lisible du champ ("amount" → "Chiffre d'affaires"). Par défaut le texte d'en-tête de la colonne dans le DataWindow, sinon son nom ; une chaîne vide le rend. Posé par votre code, il survit à of_from_datastore
is_showstring"normal"Ce que la cellule affiche : "normal", "pctGrand" (% du total général), "pctRow" (% de la ligne), "pctCol" (% de la colonne), "running" (cumul), "diff" (écart au précédent)
is_conditional_formattingstring"none"Mise en forme conditionnelle : CF_NONE, CF_SCALE (échelle de couleurs), CF_BARS (barres dans la cellule) ou CF_ICONS (une flèche par tiers : haut, stable, bas)

Les valeurs de is_show et is_conditional_formatting sont aussi disponibles en constantes sur le handle (SHOW_PCT_COL, CF_SCALE…). Posés avant que le champ soit en Valeurs, ils sont gardés, se relisent tels quels et s'appliquent à son arrivée.

// Local variables
n_pbt_crosstab_field lnv_field

// Le champ Montant : son libelle et une echelle de couleurs
lnv_field = uo_crosstab.of_field(/*field*/ "amount")
lnv_field.is_label = "Chiffre d'affaires"
lnv_field.is_conditional_formatting = lnv_field.CF_SCALE

⚠️ Changement incompatible. Cette propriété s'appelait is_cf : l'abréviation ne disait rien au point d'appel. L'ancien nom n'existe plus — un code qui l'utilise ne compile pas. Le remplacement est mécanique : is_cf → is_conditional_formatting, sans changement de valeurs ni de comportement.


Méthodes #

Alimenter et nommer #

MéthodeRôle
of_from_datastore (datastore ads_data)Transmet le jeu de données : les champs sont déduits des colonnes du DataStore, leur libellé du texte d'en-tête. Un texte qui contient une tabulation ou un retour à la ligne reste dans sa ligne ; une valeur vide (NULL, texte vide) est un seul membre (vide), rangé en dernier ; les libellés posés par is_label survivent au rechargement. Renvoie 0 une fois les données chargées, -5 si le DataStore n'est pas valide ou n'a aucune colonne, -2 si le composant n'est pas créé
of_field (string as_field)Renvoie le handle d'un champ, pour le libeller ou le mettre en forme (voir Propriétés d'un champ)

Construire le tableau #

MéthodeRôle
of_clear_layout ( )Vide les quatre zones : le tableau redevient vierge, les données restent chargées. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_add_row_field (string as_field)Ajoute un champ en zone Lignes (l'ordre des appels donne l'ordre des niveaux). Un champ déjà en Valeurs y reste : il peut être aux deux (un comptage distinct par membre). Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas ou pour une mesure calculée (elle va en Valeurs seulement), -2 si le composant n'est pas créé
of_add_col_field (string as_field)Ajoute un champ en zone Colonnes. Un champ déjà en Valeurs y reste : il peut être aux deux (un comptage distinct par membre). Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas ou pour une mesure calculée (elle va en Valeurs seulement), -2 si le composant n'est pas créé
of_add_value_field (string as_field, string as_agg)Ajoute une mesure en zone Valeurs, avec son agrégation (AGG_*, vide = AGG_SUM). Le champ garde sa place en Lignes, Colonnes ou Filtres. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas ou une autre agrégation, -2 si le composant n'est pas créé
of_add_filter_field (string as_field)Ajoute un champ en zone Filtres, au-dessus du tableau. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas ou pour une mesure calculée (elle va en Valeurs seulement), -2 si le composant n'est pas créé
of_remove_field (string as_field)Retire un champ du tableau : de toutes les zones où il est (Lignes, Colonnes, Filtres, et chacune de ses valeurs), avec ses filtres — comme « Retirer » depuis la liste des champs. Le reste de la disposition reste tel quel, et ue_layout_changed dit la nouvelle. Renvoie 0 une fois appliqué, -4 quand le champ n'est dans aucune zone, -5 pour un champ que les données n'ont pas

Les agrégations acceptées par of_add_value_field sont portées par le composant en constantes : AGG_SUM (défaut), AGG_COUNT, AGG_DISTINCT_COUNT (comptage de valeurs distinctes, les cellules vides ignorées comme dans Excel), AGG_AVG, AGG_MIN, AGG_MAX.

Totaux et sous-totaux #

Les totaux se règlent par propriétés, décrites plus haut : ib_row_grand_total et ib_col_grand_total pour les totaux généraux, ib_row_subtotals et ib_col_subtotals pour les sous-totaux, is_totals_position pour la place de la ligne de total général.

Filtrer #

MéthodeRôle
of_set_member_filter (string as_field, string as_values_tab)Ne garde que les valeurs listées d'un champ. Les valeurs sont séparées par des tabulations (~t) ; une liste vide retire le filtre, comme of_set_member_order. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé
of_clear_member_filter (string as_field)Retire ce filtre. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé
of_set_value_filter (string as_field, string as_type, double ad_a, double ad_b, integer ai_measure_index)Filtre sur le total de la mesure n° ai_measure_index (la première = 1) : VALUEFILTER_TOP ou VALUEFILTER_BOTTOM (les ad_a premiers ou derniers), VALUEFILTER_GT, VALUEFILTER_LT (au-dessus, en dessous de ad_a), VALUEFILTER_BETWEEN (entre ad_a et ad_b). Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, un autre type ou une position sans mesure en Valeurs, -2 si le composant n'est pas créé
of_clear_value_filter (string as_field)Retire ce filtre. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé
of_set_label_filter (string as_field, string as_type, double ad_a, double ad_b)Filtre numérique sur la valeur du champ lui-même : LABEL_GT, LABEL_LT, LABEL_BETWEEN. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas ou un autre type, -2 si le composant n'est pas créé
of_set_label_filter (string as_field, string as_type, string as_a, string as_b)Filtre texte sur la valeur du champ : LABEL_CONTAINS, LABEL_BEGINS, LABEL_ENDS (as_a, sans tenir compte de la casse ; as_b ne sert pas). Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, un autre type ou un texte vide, -2 si le composant n'est pas créé
of_clear_label_filter (string as_field)Retire ce filtre. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé

Mettre en forme #

MéthodeRôle
of_set_value_format (string as_field, integer ai_decimals, string as_thousands, string as_suffix { , boolean ab_symbol_before })Format d'une mesure : nombre de décimales (0 à 6), séparateur de milliers (",", ".", " " : le séparateur lui-même, "," allant avec un point décimal et "." avec une virgule ; "space" espace fine, "none", "locale" celui de la langue d'affichage ; "" laisse la mesure sur is_thousands, le défaut du menu Options), symbole — après le nombre par défaut (1 234 EUR), AVANT quand ab_symbol_before vaut true ($1,234). Les trois réglages appartiennent à CETTE mesure ; posés avant que le champ soit en Valeurs, ils sont gardés et appliqués à son arrivée. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé
of_clear_value_format (string as_field)Retour au format par défaut. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé
of_set_member_order (string as_field, string as_values_tab)Ordre d'affichage imposé aux valeurs d'un champ (séparées par ~t) ; une chaîne vide rend l'ordre naturel. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas, -2 si le composant n'est pas créé

Dates et champs calculés #

MéthodeRôle
of_group_date_field (string as_field, string as_part)Crée un champ dérivé d'une colonne date, datetime ou timestamp (une heure seule n'a pas de date à regrouper) : DATE_YEAR, DATE_QUARTER ou DATE_MONTH, nommé <colonne>__<partie>. Il rejoint la liste des champs et s'emploie comme les autres. Renvoie 0 une fois appliqué, -5 pour un champ que les données n'ont pas ou une autre partie, -2 si le composant n'est pas créé
of_add_calc_field (string as_name, string as_label, string as_formula)Champ calculé ligne à ligne ("[amount] * 0.8" = le net de chaque vente, sommé ensuite comme toute colonne), utilisable dans n'importe quelle zone. Pas pour un ratio de totaux (prix moyen) : c'est of_add_calc_measure. Renvoie 0 une fois appliqué, -5 sur un argument invalide, -2 si le composant n'est pas créé
of_remove_calc_field (string as_name)Retire un champ calculé. Renvoie 0 une fois appliqué, -5 si aucun champ calculé ne porte ce nom, -2 si le composant n'est pas créé
of_add_calc_measure (string as_name, string as_label, string as_formula)Mesure calculée cellule à cellule, sur les totaux ("[marge] / [ca]" = taux de marge global). Se place uniquement en zone Valeurs. Sa formule ne cite que des champs de données : une autre mesure calculée est refusée (ue_calc_field_error). Renvoie 0 une fois appliqué, -5 sur un argument invalide, -2 si le composant n'est pas créé
of_remove_calc_measure (string as_name)Retire une mesure calculée. Renvoie 0 une fois appliqué, -5 si aucune mesure calculée ne porte ce nom, -2 si le composant n'est pas créé

Une formule accepte les opérateurs + - * / ( ), des nombres et des champs entre crochets. Une formule invalide déclenche ue_calc_field_error — rien ne plante.

Déplier, mémoriser, exporter #

MéthodeRôle
of_expand_all ( ) · of_collapse_all ( )Déplie ou replie tous les groupes, de lignes ET de colonnes. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_expand_to_level (integer ai_level)Déplie jusqu'à un niveau donné (1 = premier niveau seulement). Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_get_layout ( ) → stringRend l'état complet du tableau — à conserver tel quel, puis à rejouer par of_set_layout
of_set_layout (string as_state_json)Restaure un état obtenu précédemment. Renvoie 0 une fois appliqué, -5 pour un texte vide ou qui n'est pas du JSON, -2 si le composant n'est pas créé
of_get_cell_value (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, ref double ad_value)Lit la valeur d'une cellule, celle que le tableau affiche (0,8 sous 80,0 %, le cumul sous un cumul). Sa ligne et sa colonne sont les tuples que donne ue_cell_double_clicked — {"region":"Nord","city":"Lille"}, un membre vide null, {} (ou une chaîne vide) pour le total — et ai_measure est la position de la valeur dans la zone Valeurs, à partir de 1. Un tuple nomme les champs de sa zone depuis le premier : {"region":"Nord"} est le sous-total de Nord. La réponse ne dépend pas de l'affichage : une branche repliée ou des sous-totaux masqués se lisent quand même. Renvoie 0 avec la valeur dans ad_value (NULL pour une cellule sans valeur), -4 quand le tableau n'a pas cette cellule (un membre ou un champ qu'il ne montre pas là), -5 pour un tuple mal formé ou une position sans mesure, -2 si le composant n'est pas créé
of_export_csv (string as_path)Écrit un fichier CSV de la vue courante (UTF-8 avec BOM, point-virgule) ; ue_csv_saved confirme. Les nombres prennent le séparateur décimal de la langue d'affichage (virgule en français, allemand, italien, espagnol, portugais) ; un libellé qui se lirait comme une formule (= + - @) est écrit précédé d'une apostrophe. Le jumeau de of_export_xlsx. Renvoie 0 une fois appliqué, -5 sur un argument invalide, -2 si le composant n'est pas créé
of_export_xlsx (string as_path)Écrit un fichier Excel du tableau tel qu'il s'affiche, mise en forme comprise (négatifs en rouge compris) ; ue_xlsx_saved confirme — ou refuse avec sa raison au-delà de 16 384 colonnes ou 1 048 576 lignes, les limites d'une feuille Excel. Renvoie 0 une fois appliqué, -5 sur un argument invalide, -2 si le composant n'est pas créé

Communes #

MéthodeRôle
of_reset ( )Remet le composant à son état neuf. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_set_redraw (boolean)Regroupe une rafale de modifications en un seul rendu. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_save_as_png (string) · of_save_as_jpg (string)Exporte le rendu en image. Renvoie 0, -2 si le composant n'est pas créé, -4 si la capture échoue, -5 sur un chemin vide

Événements #

ÉvénementDéclenché quand
ue_layout_changed (string as_layout_json)L'utilisateur a réarrangé le tableau (déplacé un champ, changé une agrégation, trié une colonne, replié un groupe…) : tout ce que of_get_layout enregistre
ue_cell_double_clicked (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value)Double-clic sur une cellule : les deux premiers arguments décrivent le croisement ({"region":"Nord","city":"Lille"}, un membre vide y vaut null, {} pour le total), ai_measure la position de la valeur double-cliquée dans la zone Valeurs, à partir de 1, et ad_value la valeur que la cellule affiche — 0.8 sous « 80,0 % », le cumul sous un cumul — ou NULL pour une cellule vide. Les deux tuples et ai_measure forment l'adresse que of_get_cell_value relit. C'est le point d'entrée d'un détail
ue_csv_saved (string as_path, boolean ab_ok, string as_error)Le fichier CSV a été écrit — ou non, et as_error dit pourquoi
ue_xlsx_saved (string as_path, boolean ab_ok, string as_error)Le fichier Excel a été écrit — ou non, et as_error dit pourquoi
ue_calc_field_error (string as_field, string as_message)Une formule de champ ou de mesure calculée est invalide
ue_copy (string as_tsv)L'utilisateur a copié une sélection de cellules (Ctrl+C) : à vous de la poser dans le presse-papiers. as_tsv est un texte tabulé qui porte par défaut les en-têtes — noms des colonnes sur une première ligne, membre de chaque ligne (son chemin entier, « Nord / Lille ») dans une première colonne — ou les chiffres seuls avec ib_copy_headers = false. En mode démo la copie est un export : refusée, la grille le dit
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)

Ce que l'utilisateur peut faire sans une ligne de code #

Le tableau est vivant : c'est tout l'intérêt du composant. Avec le panneau de champs affiché (ib_field_list = true), l'utilisateur peut :

Chacune de ces manipulations remonte dans ue_layout_changed : couplé à of_get_layout / of_set_layout, cela permet d'offrir des « vues enregistrées » à vos utilisateurs.

Au clavier. Chacun de ces gestes est atteignable sans souris : Tab amène sur un champ, un triangle de repli ou un en-tête triable, Entrée ou Espace le déclenche. Sur un champ, cela ouvre son menu — celui qui porte Ajouter aux lignes / aux colonnes / aux valeurs / aux filtres et Retirer : toute la construction du tableau passe par là. Maj+F10 ou la touche Menu ouvrent aussi ce menu, et le focus suit le champ qu'on vient de déplacer, même dans une autre zone.


Exemples #

Un rapport de ventes complet #

// Lignes : region, puis ville a l'interieur de chaque region
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_row_field(/*field*/ "city")

// Colonnes : une par annee
uo_crosstab.of_add_col_field(/*field*/ "year")

// Cellules : le montant total
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)

// Presentation : montants lisibles, sous-totaux et totaux generaux
uo_crosstab.of_set_value_format(/*field*/ "amount", /*decimals*/ 0, /*thousands*/ "locale", /*suffix*/ "$", /*symbol_before*/ true)
uo_crosstab.ib_row_subtotals = true
uo_crosstab.ib_row_grand_total = true
uo_crosstab.ib_col_grand_total = true

// Colorer les cellules pour reperer les gros montants d'un coup d'oeil
uo_crosstab.of_field(/*field*/ "amount").is_conditional_formatting = n_pbt_crosstab_field.CF_SCALE

Des libellés lisibles #

Vos colonnes s'appellent souvent mt_ht ou cd_reg. Renommez-les une fois pour toutes, juste après of_from_datastore.

// Les donnees d'abord : les champs prennent le nom des colonnes
uo_crosstab.of_from_datastore(/*data*/ lds)

// Puis un libelle lisible pour chaque champ
uo_crosstab.of_field(/*field*/ "region").is_label    = "Region"
uo_crosstab.of_field(/*field*/ "city").is_label     = "Ville"
uo_crosstab.of_field(/*field*/ "category").is_label = "Categorie"
uo_crosstab.of_field(/*field*/ "year").is_label     = "Annee"
uo_crosstab.of_field(/*field*/ "amount").is_label   = "Chiffre d'affaires"
uo_crosstab.of_field(/*field*/ "quantity").is_label  = "Quantite"

Analyser des parts plutôt que des montants #

// Local variables
n_pbt_crosstab_field lnv_amount

// Repartir de zones vides, puis placer les champs
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "category")
uo_crosstab.of_add_col_field(/*field*/ "year")
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)

// Ne garder que deux categories a l'ecran (valeurs separees par une tabulation)
uo_crosstab.of_set_member_filter(/*field*/ "category", /*values_tab*/ "Informatique~tMobilier")

// Le champ Montant, pour regler son affichage
lnv_amount = uo_crosstab.of_field(/*field*/ "amount")

// Afficher la part de chaque cellule dans le total de sa colonne
lnv_amount.is_show = lnv_amount.SHOW_PCT_COL

// Une petite barre dans chaque cellule pour comparer les parts d'un regard
lnv_amount.is_conditional_formatting = lnv_amount.CF_BARS

Une mesure à vous : le prix moyen #

Une mesure calculée s'évalue sur les totaux de chaque cellule, pas ligne à ligne : c'est ce qui rend un ratio juste.

// Repartir de zones vides, puis placer les champs
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "region")

// Les deux totaux qui vont servir de base au calcul
uo_crosstab.of_add_value_field(/*field*/ "amount",  /*agg*/ uo_crosstab.AGG_SUM)
uo_crosstab.of_add_value_field(/*field*/ "quantity", /*agg*/ uo_crosstab.AGG_SUM)

// Prix moyen = montant total divise par quantite totale
uo_crosstab.of_add_calc_measure(/*name*/ "avg_price", /*label*/ "Prix moyen", /*formula*/ "[amount] / [quantity]")
uo_crosstab.of_set_value_format(/*field*/ "avg_price", /*decimals*/ 2, /*thousands*/ "locale", /*suffix*/ "$", /*symbol_before*/ true)

// Puis la placer parmi les valeurs comme n'importe quel champ (l'agregat ne compte pas : une mesure se calcule)
uo_crosstab.of_add_value_field(/*field*/ "avg_price", /*agg*/ uo_crosstab.AGG_SUM)
// event ue_calc_field_error de uo_crosstab : (string as_field, string as_message)
// Formule invalide : prevenir sans rien casser, le tableau reste affiche.
uo_status.of_panel(/*key*/ "main").is_text = "Formule " + as_field + " : " + as_message

Analyser par mois, trimestre ou année #

Une colonne de date ne se croise pas telle quelle — chaque jour ferait sa propre ligne. Dérivez d'abord le niveau voulu.

// Creer trois champs derives de la colonne sale_date
uo_crosstab.of_group_date_field(/*field*/ "sale_date", /*part*/ "year")
uo_crosstab.of_group_date_field(/*field*/ "sale_date", /*part*/ "quarter")
uo_crosstab.of_group_date_field(/*field*/ "sale_date", /*part*/ "month")

// Puis les croiser comme n'importe quel champ : annee en colonnes, trimestre en dessous
uo_crosstab.of_add_col_field(/*field*/ "sale_date__year")
uo_crosstab.of_add_col_field(/*field*/ "sale_date__quarter")

Le palmarès des dix meilleures régions #

// Ne garder que les 10 regions au plus fort total sur la premiere mesure
uo_crosstab.of_set_value_filter(/*field*/ "region", /*type*/ "top", /*a*/ 10, /*b*/ 0, /*measure_index*/ 1)

Exporter #

L'export reprend exactement la vue en cours : mêmes filtres, mêmes totaux, même mise en forme.

// Vers Excel : le fichier est ecrit directement au chemin indique
uo_crosstab.of_export_xlsx(/*path*/ "C:\temp\ventes.xlsx")
// event ue_xlsx_saved de uo_crosstab : (string as_path, boolean ab_ok, string as_error)
// inv_notif = un n_pbt_toaster declare en variable d'instance de la fenetre
if ab_ok then
    inv_notif.is_title = "Export termine"
    inv_notif.is_text  = as_path
    inv_notif.is_kind  = inv_notif.KIND_SUCCESS
else
    inv_notif.is_title = "Export impossible"
    inv_notif.is_text  = as_error
    inv_notif.is_kind  = inv_notif.KIND_ERROR
end if
inv_notif.of_show()
// Vers CSV : un fichier, comme pour Excel ; ue_csv_saved confirme
uo_crosstab.of_export_csv(/*path*/ "C:\exports\ventes.csv")

Offrir des vues enregistrées #

// Local variables
string ls_view

// Enregistrer la vue courante : of_get_layout la rend tout de suite
ls_view = uo_crosstab.of_get_layout()

// Conserver le contenu TEL QUEL : il se rejoue sans transformation.
of_save_view(is_current_view, ls_view)
// Plus tard : rejouer une vue enregistree
uo_crosstab.of_set_layout(/*state_json*/ of_read_view("Ventes par region"))

Descendre au détail derrière un chiffre #

// event ue_cell_double_clicked de uo_crosstab : (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value)
// Les deux premiers arguments decrivent le croisement (quelles valeurs de lignes,
// quelles valeurs de colonnes) : de quoi reconstruire une requete de detail.
w_sales_detail lw_detail

// Ouvrir la fenetre de detail avec le croisement en parametre
OpenWithParm(lw_detail, as_row_tuple_json + "|" + as_col_tuple_json)

Les deux tuples et ai_measure forment l'adresse de la cellule : of_get_cell_value la relit plus tard, telle que le tableau l'affiche à ce moment-là.

// ue_cell_double_clicked event of uo_crosstab : the address of the cell, read again later
// Local variables
double ld_value

// Read the cell again from its address : 0 when it exists
if uo_crosstab.of_get_cell_value(/*row_tuple_json*/ as_row_tuple_json, /*col_tuple_json*/ as_col_tuple_json, /*measure*/ ai_measure, /*value*/ ld_value) = 0 then

	// ld_value is what the cell shows ; NULL for an empty cell
end if

Une cellule se lit aussi sans clic, par ses membres : un tuple partiel désigne un sous-total. Et of_remove_field retire un champ de toutes ses zones d'un coup, filtres compris.

// The sales of Nord in 2025, first measure of Values
// Local variables
double ld_north

// A partial address : the region and the year, no other member
uo_crosstab.of_get_cell_value(/*row_tuple_json*/ '{"region":"Nord"}', /*col_tuple_json*/ '{"year":"2025"}', /*measure*/ 1, /*value*/ ld_north)

// Take the year out of the table, filters included
uo_crosstab.of_remove_field(/*field*/ "year")

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_set_property · of_get_property · of_component_namePiloter une propriété par son nom3.1 Le moteur de propriétés
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

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