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 #
| Userobject | u_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émo | 500 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.
| Zone | Ce qu'elle contient | Effet |
|---|---|---|
| Lignes | Des champs de regroupement | Un niveau de lignes par champ, repliable |
| Colonnes | Des champs de regroupement | Un niveau d'en-têtes de colonnes par champ |
| Valeurs | Des champs numériques et leur agrégation | Ce qui est calculé dans les cellules |
| Filtres | Des champs de sélection | Un 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 #
| Constante | Valeur | Pour |
|---|---|---|
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é | Type | Défaut | Rôle |
|---|---|---|---|
is_totals_position | string | "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_axis | string | "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_symbol | string | "" | 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_thousands | string | "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_list | boolean | true | Affiche le panneau de champs, où l'utilisateur réarrange le tableau à la souris |
ib_row_subtotals | boolean | true | Affiche 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_subtotals | boolean | true | Affiche un sous-total par groupe de colonnes |
ib_row_grand_total | boolean | true | Affiche la ligne de total général sous le tableau (jumelle de ib_col_grand_total) |
ib_col_grand_total | boolean | true | Affiche la colonne de total général après le tableau (jumelle de ib_row_grand_total) |
ib_copy_headers | boolean | true | Ctrl+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_enabled | boolean | true | Grisé : 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_style | string | "" | Style visuel du composant (constantes THEME_STYLE_*) ; vide = celui de l'application, suivi à chaque changement |
is_theme_mode | string | "" | Variante claire ou sombre (constantes THEME_MODE_*) ; vide = celle de l'application, suivie à chaque changement |
il_theme_accent | long | -1 | Couleur d'accent de ce composant (-1 = l'accent de l'application, ou celui du thème) |
is_tooltip | string | "" | Info-bulle simple affichée au survol du composant |
is_super_tooltip_title | string | "" | Titre de l'info-bulle enrichie (prend le pas sur is_tooltip) |
is_super_tooltip_text | string | "" | Texte de l'info-bulle enrichie (balisage riche accepté) |
is_super_tooltip_image | string | "" | 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é | Type | Défaut | Rôle |
|---|---|---|---|
is_label | string | en-tête du DataWindow | Libellé 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_show | string | "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_formatting | string | "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éthode | Rô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éthode | Rô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éthode | Rô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éthode | Rô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éthode | Rô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éthode | Rô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 ( ) → string | Rend 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éthode | Rô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énement | Dé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 :
- faire glisser un champ d'une zone à l'autre, et réarranger le croisement à volonté ;
- changer l'agrégation d'une mesure (somme, moyenne, comptage…) ;
- filtrer les valeurs d'un champ par une liste à cocher ;
- replier ou déplier un groupe de lignes ou de colonnes ;
- trier sur un en-tête ;
- sélectionner puis copier un bloc de cellules, avec ses en-têtes pour qu'un collage dans un tableur dise ce que sont les chiffres (
ib_copy_headers).
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 #
- Appelez
of_from_datastoreune seule fois par jeu de données : réarranger le tableau ensuite ne coûte rien, retransmettre les données coûte cher. - Posez les libellés (
of_field("...").is_label) juste aprèsof_from_datastore: ils suivent le champ partout, y compris dans le panneau de champs et dans les exports. - Filtrez côté SQL ce qui n'a pas vocation à être analysé : le crosstab est rapide, mais un DataStore deux fois plus petit s'ouvre deux fois plus vite.
of_clear_layout()vide les zones sans retransmettre les données : c'est le bon appel pour proposer plusieurs analyses sur la même source.- Un champ date se croise toujours via
of_group_date_field, jamais directement. - Laissez le panneau de champs visible sur les écrans d'analyse, masquez-le sur les tableaux de bord figés.
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.
| Membres | Rôle | Détaillé dans |
|---|---|---|
of_count · of_keys_at · of_has | Parcourir ce que le composant contient | 3.2 Les items |
of_reset | Remettre le composant à zéro | 3.6 Remettre un composant à zéro : of_reset() |
of_set_property · of_get_property · of_component_name | Piloter une propriété par son nom | 3.1 Le moteur de propriétés |
of_register_shortcut · of_clear_shortcuts | Raccourcis clavier du composant | 3.5 Les raccourcis clavier |
of_is_created · of_is_ready · of_get_last_error | S'il est né, s'il est prêt, ce qui a échoué | 3.7 Diagnostic |
of_save_as_png · of_save_as_jpg | Exporter le rendu en image | 3.8 Exporter le rendu en image |
of_set_redraw | Grouper les modifications en un seul repaint | 3.10 Bonnes pratiques |
of_preload_icons | Icônes affichées sans délai | Affichage instantané : of_icon |
of_set_translation | Traduire un libellé du composant | 5.2 Adapter un libellé : of_set_translation |
of_focus_webview | Donner le focus au composant | 6.4 Clavier et focus |
of_print · of_print_to_pdf | Imprimer, ou écrire un PDF | 6.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.