Utilisation des ListBox comme sous-formulaires

Kent D. WILBUR

Les ListBox sont un apport important de la version 2004 de 4D. Dans cette note technique, nous abordons deux exemples d'utilisation : en remplacement des zones de défilement groupées et en tant que sous-formulaire. Les ListBox sont bien plus simples à créer et à utiliser, elles offrent beaucoup de flexibilité ainsi que des propriétés qui n'existaient pas ou alors en écrivant beaucoup de code. Contrairement au sous-formulaire, les ListBox peuvent être utilisées sans avoir recours aux transactions et sans accéder répétitivement au serveur pour manipuler les enregistrements dans le formulaire inclus. ♪

- Titre : Utilisation des ListBox comme sous-formulaires

- Auteur : Kent D. WILBUR

- Parution : 1 octobre 2005

- Licence : Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Introduction

- II. Avantages du sous-formulaire

- III. Avantages de la ListBox

- IV. La base exemple

- La ListBox en tant qu’objet

- Les en-têtes de colonnes

- Les colonnes

- V. La méthode formulaire du formulaire d’entrée

- VI. La méthode objet de la ListBox

- VII. Ajout et suppression de lignes dans la ListBox

- VIII. Les méthodes objet des colonnes de la ListBox

- IX. Mise à jour du total de la facture

- X. La ListBox de choix personnalisée

- XI. Limite de la ListBox

- XII. Résumé

- XIII. Base exemple

I - Introduction

4D 2004 nous a offert une réécriture complète des formulaires de style liste ; parmi ceux-ci, les formulaires de sortie, les sous-formulaires et les formulaires utilisateurs. En complément est apparu un nouvel objet de gestion des listes appelé ListBox. Considérez une ListBox comme une zone de défilement de tableaux groupés avec des propriétés très améliorées. J'ai entendu parler des ListBox comme de « tableaux groupés sous stéroïdes ». J'ai aussi entendu dire qu'il est difficile de choisir entre un sous-formulaire et une ListBox. Cette note technique ne répondra pas à cette question. Mais, elle vous donnera quelques idées sur les cas où une ListBox peut être préférable à un sous-formulaire. Elle vous montrera aussi comment utiliser une ListBox à la place d'un sous-formulaire si tel est votre choix.

II - Avantages du sous-formulaire

Les sous-formulaires existent depuis longtemps. Tout le monde a l'habitude de s'en servir. Ils sont rapides et faciles à mettre en place et requièrent peu de code pour les fonctionnalités de base. Si vous avez besoin qu'un double-clic sur une ligne permette d'éditer un enregistrement dans un formulaire d'entrée, le sous-formulaire est la solution la plus simple. Si vous avez besoin de divers boutons en en-tête pour proposer diverses tâches, avec 4D 2004, les sous-formulaires le permettent aisément.

III - Avantages de la ListBox

Les sous-formulaires nécessitent beaucoup de communication entre le client et le serveur. 4D Client ne charge qu'un enregistrement à la fois. C'est pourquoi, chaque fois qu'un utilisateur passe d'un enregistrement à un autre dans un sous-formulaire, 4D Client envoie cet enregistrement au serveur pour le sauvegarder. (Nous parlons ici d'enregistrements de tables liées, et non de sous-enregistrements.) Cela peut, en soi, créer un trafic important sur le réseau. Ajoutons à cela le problème posé quand l'utilisateur clique sur le bouton Annuler et que vous devez alors annuler toutes les modifications, d'où la nécessité des transactions. L'usage des transactions ne peut être évité et cela augmente la charge du serveur parce que chaque enregistrement sauvé dans une transaction est écrit dans une zone temporaire avant d'être intégré aux données une fois la transaction validée. Bien sûr, si la transaction est annulée, la zone temporaire est simplement vidée. Mais cela aussi crée un travail supplémentaire pour le serveur.

À mon avis, ce sont les deux seules raisons vraiment importantes pour envisager l'utilisation des ListBox comme sous-formulaires. Tout d'abord, comme les ListBox sont des tableaux, toutes les données parviennent au client une fois et une seule. (Elles parviennent aussi une fois et une seule pour l'affichage avec des sous-formulaires traditionnels). Pendant que la ListBox est en cours d'utilisation, il n'y a aucun trafic créé sur le réseau. Une fois qu'un enregistrement est mis à jour, les tableaux sont renvoyés au serveur pour être sauvegardés. Comme TABLEAU VERS SELECTION est optimisé, cela devrait être assez efficace. Au pire, ce n'est pas potentiellement moins bien que l'utilisation des sous-formulaires et au mieux, cela créera substantiellement moins de trafic réseau.

AUCUNE TRANSACTION. Vous avez bien lu. AUCUNE TRANSACTION ! En utilisant les ListBox, vous éliminez la cause principale de l'utilisation des transactions dans le formulaire d'entrée. Car vous n'avez sauvé des enregistrements à aucun moment jusqu'au OK final. Vous n'avez pas besoin de transaction, car, sur une annulation, vous n'enregistrez simplement pas les modifications apportées aux tableaux en revenant aux données. Aucune modification n'est intervenue, donc aucun ANNULER TRANSACTION n'est nécessaire. L'élimination complète, ou au moins drastique, des transactions est un facteur important d'amélioration de la performance.

Maintenant, AUCUNE TRANSACTION ne signifie pas que dans certaines circonstances elles ne vous soient pas utiles, voire nécessaires. Je suis tout à fait d'accord et je comprends combien les transactions sont importantes dans beaucoup de cas. Mais, en règle générale, pour une simple entrée de données, les transactions ne sont pas nécessaires lorsque vous utilisez des ListBox comme sous-formulaires.

Les ListBox ont aussi certaines propriétés natives qui ne nécessitent aucune programmation, telles les couleurs de lignes alternées. Oui, cela peut s'obtenir dans un sous-formulaire, mais il faut le programmer pour obtenir l'effet désiré. Les colonnes sont redimensionnables sans splitters. Cela fait moins d'objets à créer, aligner, puis réaligner encore et encore lorsque vous modifiez le formulaire. Les ListBox permettent le glisser-déposer pour réordonner les lignes et les colonnes. C'est impossible avec un sous-formulaire traditionnel.

IV - La base exemple

La base exemple est conçue tout d'abord pour montrer comment un sous-formulaire simple peut être remplacé par une ListBox. Elle n'utilise pas de transactions et offre néanmoins toutes les fonctionnalités d'un sous-formulaire normal avec des transactions.

Sur cet écran d'entrée, la ListBox apparaît exactement comme un simple sous-formulaire. Cependant, sans aucune programmation, un clic sur l'en-tête de colonne provoque le tri sur cette colonne. Les lignes et colonnes peuvent être reclassées par glisser-déplacer. Je ne voulais pas en rajouter trop dans les extras, mais j'ai ajouté une propriété qui permet à l'utilisateur de réarranger les lignes par glisser-déplacer et de conserver cet ordre après l'enregistrement. Cette propriété est impossible à proposer sur un sous-formulaire normal.

Une ListBox est un objet unique qui possède différents composants. Chaque type de composant possède ses propres propriétés et chaque composant individuel possède ses propres propriétés qui sont différentes de celles des autres composants du même type.

La ListBox en tant qu’objet

Dans cet exemple, son nom est fLineItems. C'est un tableau booléen. Elle peut avoir sa propre méthode objet, indépendamment de ses autres composants. Les propriétés de l'objet fLineItems sont fixées comme suit :

- 6 colonnes (dont 5 affichées) ;

- les en-têtes de colonnes sont visibles ;

- la sélection multiple est désactivée (elle n'aurait pas de signification ici) ;

- les quadrillages horizontal et vertical sont activés ;

- la ListBox peut être agrandie verticalement, mais pas horizontalement ;

- une couleur alternée a été choisie ;

- les lignes sont déplaçables ;

- les colonnes sont triables.

Les seuls événements activés sont Sur après tri et Sur déplacement colonne.

Les en-têtes de colonnes

Chaque colonne a un en-tête avec des propriétés qui peuvent être différentes de celles des autres en-têtes. Les en-têtes de colonnes n'ont PAS de méthode objet. Les en-têtes de notre exemple n'ont aucune propriété particulière.

Les colonnes

Chaque colonne est un tableau. Chaque colonne peut posséder une méthode objet qui lui est propre. Même s'il est possible d'ajouter ou de supprimer des colonnes par programmation dans une ListBox, si vous voulez associer une méthode objet à une colonne, vous devrez créer la ListBox comme objet statique parce que vous ne pouvez pas créer ou affecter une méthode objet à une colonne par programmation.

Comme vous pouvez le voir dans cette liste de propriétés, il y a beaucoup de caractéristiques qui peuvent être fixées individuellement pour chaque colonne. Dans cette ListBox, presque toutes les propriétés sont les mêmes, sauf ce qui suit. Chaque colonne a un nom d'objet et un nom de variable unique. aLInvoiceNo (numéro de ligne) est la seule colonne définie comme invisible. atProductCode (code produit) et aiQty (quantité) sont les deux seules colonnes saisissables, et les seules qui possèdent une méthode objet. Le seul événement activé pour ces deux colonnes est Sur données modifiées. Notez qu'il est possible de modifier la police, la taille, la couleur de police, la couleur de fond alternée et de nombres autres propriétés relatives à l'apparence pour chaque colonne. Cependant, je vais vous éviter de chercher : il n'est PAS possible de modifier l'un de ces attributs pour une cellule en fonction de la valeur dans cette cellule. Les ListBox sont conçues pour remplacer les zones de défilement groupées, non pour se substituer à Area List Pro ou 4D View. Vous pouvez, néanmoins, changer la couleur de fond d'une ligne entière selon la valeur d'une cellule de la ligne. Quoique la base exemple n'utilise pas cette propriété, je vous indique que vous pourriez le faire avec un tableau de « Couleurs de polices » associé à la ListBox dans son ensemble. (Cf. la liste de propriétés de la ListBox ci-dessus.)

V - La méthode formulaire du formulaire d’entrée

L'essentiel du code nécessaire pour utiliser une ListBox à la place d'un sous-formulaire est relativement simple et banal. Le chargement, la création et la sauvegarde des enregistrements de la table liée [LignesFactures] s'effectuent dans la méthode formulaire du formulaire d'entrée. Bien que la base exemple ne soit même pas multiprocess, tout le code relatif à l'utilisation de la ListBox est compatible multiutilisateurs.

Pour le formulaire d'entrée, les seuls événements concernés par la gestion de la ListBox sont :
Sur chargement, Sur libération, et Sur validation.

Si (Faux)

` Méthode formulaire : Formulaire Entrée Table [Factures]
` Note technique : ListBox/Sous-formulaire
` Créée par : Kent Wilbur
` Date: 7/18/2005

` Affectation des valeurs par défaut dans le formulaire d'entrée de la table des factures ([Invoices];"Input")

<>f_Version2004x2:=Vrai
<>fK_Wilbur:=Vrai
Fin de si

C_ENTIER LONG($i)
C_ENTIER LONG($LFormEvent)
C_ENTIER LONG($LSizeOfArray)

$LFormEvent:=Evenement formulaire

GEN_InputFormMethod

Au cas ou
: ($LFormEvent=Sur chargement )
` Affectation des valeurs par défaut
fNewInvoice:=Faux
ACTIVER BOUTON(*;"@Included")

` Réinitialisation des valeurs par défaut dans des circonstances particulières
Au cas ou
: (Nouvel enregistrement([Facture]))   ` Nouvel enregistrement
[Facture]FactureNo:=GEN_LNextSequence ("InvoiceNo")
fNewInvoice:=Vrai

: (Enregistrement verrouille([Facture]))
INACTIVER BOUTON(*;"@Included")   ` Inactiver les boutons d'ajout et de suppression
Fin de cas

` Vider les tableaux à utiliser
TABLEAU ENTIER LONG(aLInvoiceNo;0)
TABLEAU TEXTE(atProductCode;0)
TABLEAU TEXTE(atTitle;0)
TABLEAU ENTIER(aiQty;0)
TABLEAU REEL(arSalePrice;0)
TABLEAU REEL(arExtPrice;0)
TABLEAU ENTIER($aiPosition;0)

Si (Non(fNewInvoice))   `Rechercher les lignes de factures existantes
LIEN RETOUR([Facture]FactureNo)
SELECTION VERS TABLEAU([LignesFactures]FactureNo;aLInvoiceNo;
[LignesFactures]CodeProduit;atProductCode;[Produits]Libellé;atTitle;
[LignesFactures]Quantité;aiQty;[LignesFactures]PrixVente;arSalePrice;[LignesFactures]Total_HT;
arExtPrice;[LignesFactures]Position;$aiPosition)
TRIER TABLEAU($aiPosition;atProductCode;atTitle;aiQty;arSalePrice;arExtPrice;aLInvoiceNo)
Fin de si

: ($LFormEvent=Sur libération )
Si (fNewInvoice)   ` Retourner un nouveau numéro quelconque si la création de l'enregistrement
`n'a pas été validée
$x:=GEN_LNextSequence ("InvoiceNo";[Facture]FactureNo)
Fin de si
fNewInvoice:=Faux   ` Programmation paranoïde

` Vider les tableaux pour libérer la mémoire
TABLEAU ENTIER LONG(aLInvoiceNo;0)
TABLEAU TEXTE(atProductCode;0)
TABLEAU TEXTE(atTitle;0)
TABLEAU ENTIER(aiQty;0)
TABLEAU REEL(arSalePrice;0)
TABLEAU REEL(arExtPrice;0)

: ($LFormEvent=Sur validation )
` Supprimer toutes les lignes inutilisées des tableaux
Boucle ($i;Taille tableau(aLInvoiceNo);1;-1)
Si ((atProductCode{$i}="") | (aiQty{$i}=0))
SUPPRIMER LIGNES(aLInvoiceNo;$i)
SUPPRIMER LIGNES(atProductCode;$i)
SUPPRIMER LIGNES(atTitle;$i)
SUPPRIMER LIGNES(aiQty;$i)
SUPPRIMER LIGNES(arSalePrice;$i)
SUPPRIMER LIGNES(arExtPrice;$i)
Fin de si
Fin de boucle

` Sauver la position des lignes pour un usage ultérieur
$LSizeOfArray:=Taille tableau(aLInvoiceNo)
TABLEAU ENTIER($aiPosition;$LSizeOfArray)
Boucle ($i;1;$LSizeOfArray)
$aiPosition{$i}:=$i
Fin de boucle

LIEN RETOUR([Facture])
Si (Enregistrements trouves([LignesFactures])>$LSizeOfArray)   ` "Workaround" pour le cas où
` nous aurions moins de lignes à sauver que nous n'en avions au départ.
SUPPRIMER SELECTION([LignesFactures])
Fin de si

TABLEAU VERS SELECTION(aLInvoiceNo;[LignesFactures]FactureNo;atProductCode;
[LignesFactures]CodeProduit;atTitle;[Produits]Libellé;aiQty;[LignesFactures]Quantité;
arSalePrice;[LignesFactures]PrixVente;arExtPrice;[LignesFactures]Total_HT;$aiPosition;
[LignesFactures]Position)
fNewInvoice:=Faux   ` Le numéro est attribué, le marquer comme utilisé si nécessaire

Fin de cas

`Fin méthode formulaire

L'événement Sur chargement prépare le formulaire. On commence par vérifier que toutes les valeurs par défaut nécessaires sont affectées. S'il s'agit d'un nouvel enregistrement, on lui affecte un nouveau numéro de facture ([Facture]FactureNo) à l'aide de la méthode projet Gen_LNextSequence. Du fait que ce formulaire n'utilise pas de transactions, il n'est pas nécessaire de se soucier de verrouiller l'enregistrement utilisé pour la numérotation automatique pour les autres utilisateurs. Si vous voulez en savoir plus sur Gen_LNextSequence, fNewInvoice et cette méthode, lisez ma note technique « Replacing the Sequence Number Command » écrite en décembre 1998. C'est toujours une information valide.

Voir la note « Replacing the Sequence Number Command » (disponible en anglais uniquement).

Si l'enregistrement est verrouillé par un autre utilisateur ou un autre process, les boutons d'ajout et de suppression de lignes sont inactivés et le code produit et la quantité ne sont pas saisissables. Ainsi, l'utilisateur ne peut pas modifier des informations qui ne seraient pas mises à jour et se demander par la suite pourquoi les modifications n'ont pas été faites. De plus, pour un enregistrement verrouillé, l'événement Sur validation (ci-dessous) ne sera jamais exécuté et il n'est donc pas nécessaire d'empêcher l'exécution du code qui doit mettre à jour les enregistrements de la table [LignesFactures].

Finalement, pour une facture existante, tous les enregistrements liés sont recherchés et chargés dans les tableaux de la ListBox. Les tableaux sont triés par un indicateur de position qui est sauvegardé juste avant l'affichage de la page.

: ($LFormEvent=Sur libération )

Si (fNewInvoice)   ` Retourner un nouveau numéro quelconque si la création de
` l'enregistrement n'a pas été validée
$x:=GEN_LNextSequence ("InvoiceNo";[Facture]FactureNo)
Fin de si
fNewInvoice:=Faux

` Vider les tableaux pour libérer la mémoire
TABLEAU ENTIER LONG(aLInvoiceNo;0)
TABLEAU TEXTE(atProductCode;0)
TABLEAU TEXTE(atTitle;0)
TABLEAU ENTIER(aiQty;0)
TABLEAU REEL(arSalePrice;0)

TABLEAU REEL(arExtPrice;0)

L'événement Sur libération procède au nettoyage du formulaire. Il rend disponible le numéro de séquence si l'utilisateur a cliqué sur Annuler pour une nouvelle facture. Puis, il libère la mémoire utilisée par les tableaux de la ListBox.

: ($LFormEvent=Sur validation )

` Supprimer toutes les lignes inutilisées des tableaux
Boucle ($i;Taille tableau(aLInvoiceNo);1;-1)
Si ((atProductCode{$i}="") | (aiQty{$i}=0))
SUPPRIMER LIGNES(aLInvoiceNo;$i)
SUPPRIMER LIGNES(atProductCode;$i)
SUPPRIMER LIGNES(atTitle;$i)
SUPPRIMER LIGNES(aiQty;$i)
SUPPRIMER LIGNES(arSalePrice;$i)
SUPPRIMER LIGNES(arExtPrice;$i)
Fin de si
Fin de boucle

` Sauver la position des lignes pour un usage ultérieur
$LSizeOfArray:=Taille tableau(aLInvoiceNo)
TABLEAU ENTIER($aiPosition;$LSizeOfArray)
Boucle ($i;1;$LSizeOfArray)
$aiPosition{$i}:=$i
Fin de boucle

LIEN RETOUR([Facture])
Si (Enregistrements trouves([LignesFactures])>$LSizeOfArray)
` "tourne-autour", dans le cas où nous aurions moins de lignes à sauvegarder qu'au chargement.
SUPPRIMER SELECTION([LignesFactures])
Fin de si

TABLEAU VERS SELECTION(aLInvoiceNo;[LignesFactures]FactureNo;atProductCode;
[LignesFactures]CodeProduit;atTitle;[Produits]Libellé;aiQty;[LignesFactures]Quantité;
arSalePrice;[LignesFactures]PrixVente;arExtPrice;[LignesFactures]Total_HT;
$aiPosition;[LignesFactures]Position)
fNewInvoice:=Faux   ` Le numéro est attribué, le marquer comme utilisé si nécessaire

Fin de cas
`Fin méthode formulaire

L'événement Sur validation effectue l'essentiel du travail. Tout d'abord, il s'assure que l'utilisateur a bien entré les codes produit. Si certains sont vides, les lignes sont supprimées avant d'être sauvegardées.

Ensuite, un tableau local est créé pour sauvegarder l'ordre des lignes. J'aurais pu avoir une autre colonne invisible dans la ListBox pour stocker l'ordre, mais j'ai trouvé que ce serait trop compliqué de tenir ce tableau à jour lorsque des lignes seraient créées, supprimées ou déplacées. Et comme cela n'a aucune importance jusqu'au moment où les données sont sauvegardées, j'ai simplement choisi de ne le créer et l'utiliser qu'au moment où il devenait nécessaire.

Enfin, les données sont enregistrées par un TABLEAU VERS SELECTION dans les enregistrements de la table liée [LignesFactures]. S'il s'agissait d'un nouvel enregistrement, le flag fNewInvoice est effacé dès lors que les enregistrements liés sont sauvés et attribués à une facture, le numéro de facture (la clé primaire de la table [Facture]) lui étant alors définitivement attribué.

VI - La méthode objet de la ListBox

Si je ne voulais pas montrer comment on sauvegarde l'ordre de tri des lignes, la méthode objet de la ListBox ne serait pas nécessaire. Lors de la création d'un nouvel enregistrement, de l'ajout, de la suppression ou de la modification d'une ligne, l'enregistrement [Facture] est modifié et, quand cet enregistrement est sauvé, l'événement Sur validation se produit. Les conditions d'exécution du Sur validation sont la modification effective de l'enregistrement parent et l'appel à la commande VALIDER. Lorsque l'utilisateur se contente de trier une colonne ou de déplacer une ligne, rien ne change dans les données. La ListBox n'est qu'un objet tableau dans le formulaire. Aucune valeur de champ n'est effectivement modifiée. C'est pourquoi, lorsque l'utilisateur se contente de modifier l'ordre des lignes nous devons « salir » l'enregistrement [Facture]. Je veux dire par là modifier un champ de l'enregistrement pour forcer le Sur validation. Nous voulons le faire avec aussi peu d'impact que possible. Donc, le code se contente de réassigner sa valeur courante à un champ petit et non indexé. J'ai choisi d'utiliser le champ booléen [Facture]Réglée. Les champs booléens sont petits et rarement indexés. C'est donc un choix idéal pour notre objectif. Les événements Sur déplacement ligne et Sur après tri sont spécifiques aux ListBox et correspondent parfaitement à cette tâche.

Pour mieux simuler l'action automatique d'un bouton de suppression, le bouton ne sera activé que lorsqu'une ligne est sélectionnée. L'événement Sur nouvelle sélection détecte si une ligne est sélectionnée et si le bouton doit être activé.

Si (Faux)

` Méthode objet: ListBox fLinesItems du formulaire [Factures] Entrée
` Note technique : ListBox/Sous-formulaire
` Créée par : Kent Wilbur
` Date: 7/18/2005

` Objectif: Détecter les glisser-déplacer de lignes et on
` revalorise un champ de l'enregistrement [Factures] pour forcer le Sur validation

<>f_Version2004x2:=Vrai
<>fK_Wilbur:=Vrai
Fin de si

C_ENTIER LONG($LFormEvent)

$LFormEvent:=Evenement formulaire

Au cas ou
: ($LFormEvent=Sur déplacement ligne ) | ($LFormEvent=Sur après tri )
[Facture]Réglée:=[Facture]Réglée   ` on force l'enregistrement à l'aide d'un
`booléen non indexé pour un impact minimal.

Fin de cas

VII - Ajout et suppression de lignes dans la ListBox

Lorsque vous utilisez une ListBox, il n'y a malheureusement pas d'actions automatiques disponibles pour les boutons qui ajoutent ou suppriment des lignes. De ce fait, vous devrez écrire tout le code qui effectue ces actions. Nous avons déjà évoqué comment simuler l'activation du bouton Supprimer sous-enregistrement si nécessaire.

Il y a un effet positif dans l'écriture de votre propre code. Du fait que vous ne pouvez pas utiliser d'action automatique, vous éliminez le problème des boutons automatiques qui ne sont pas actifs tant que le sous-formulaire n'est pas sélectionné. Cette contrainte d'interface est souvent perturbante pour l'utilisateur final qui effectue la saisie. Mais, dès lors que nous écrivons notre propre code, le bouton d'ajout sera disponible en permanence pour l'utilisateur.

Si (Faux)

` Méthode objet : bDeleteIncluded dans le formulaire [Facture] Entrée
` Note technique : ListBox/Sous-formulaire
` Créée par : Kent Wilbur
` Date: 7/18/2005

` Objectif: Suppression d'une ligne de ListBox

<>f_Version2004x2:=Vrai
<>fK_Wilbur:=Vrai
Fin de si

Si (atProductCode>0)
SUPPRIMER LIGNE LISTBOX(*;"tLineItems";atProductCode)
Invoice_CalcTotals    `Recalcul du total facture

Fin de si

Pour les tableaux affichés dans une ListBox, la suppression de lignes est très simple. La commande SUPPRIMER LIGNE LISTBOX supprime l'élément spécifié de tous les tableaux en même temps, y compris les colonnes cachées. C'est beaucoup plus simple que lorsque vous deviez le faire à la main en bouclant sur tous les tableaux groupés et en supprimant chaque élément vous-même.

Après la suppression d'une ligne, il faut faire un peu de ménage. Il peut être nécessaire de recalculer le total de la facture, d'où l'appel de la méthode Invoice_CalcTotals. Après la suppression d'une ligne, aucune ligne n'est sélectionnée, donc, on efface un flag et on inactive le bouton de suppression jusqu'à ce qu'une nouvelle ligne soit sélectionnée.

Si (Faux)

` Méthode objet: bAdd dans le formulaire ([Invoices];"Input")
` Note technique : ListBox/Sous-formulaire
` Créée pr : Kent Wilbur
` Date: 7/18/2005

` Objectif: Ajout de lignes dans la ListBox

<>f_Version2004x2:=Vrai
<>fK_Wilbur:=Vrai
Fin de si

` Declaration des variables locales
C_ENTIER LONG($LSizeOfArray)

$LSizeOfArray:=Taille tableau(aLInvoiceNo)+1
INSERER LIGNE LISTBOX(*;"tLineItems";$LSizeOfArray)
aLInvoiceNo{$LSizeOfArray}:=[Facture]FactureNo   ` C'est nécessaire parce que vous n'avez
` pas la 'Mise à jour automatique en sous-formulaire' et devez le faire par programmation
SELECTIONNER LIGNE LISTBOX(fLineItems;$LSizeOfArray)   ` Selectionner la nouvelle ligne
DEFILER LIGNES(fLineItems;$LSizeOfArray)   ` Défiler jusqu'à la ligne sélectionnée, si besoin

EDITER ELEMENT(atProductCode{$LSizeOfArray};$LSizeOfArray)   ` Positionner le point d'insertion pour l'ajout du code produit

L'ajout d'une ligne ne nécessite guère plus de travail. Ce code effectue un ajout, mais il pourrait être réécrit pour faire une insertion à l'emplacement de la ligne sélectionnée si vous le vouliez.

Comme nous devons gérer tout le tableau, il faut s'assurer que le tableau qui représente le numéro de facture est rempli. aLInvoiceNo est le tableau qui représente la clé d'appel pour le lien de [LignesFactures] vers [Facture]. Il doit être rempli avant l'exécution de la commande SELECTION VERS TABLEAU dans l'événement Sur validation. Je l'ai placé ici pour ne jamais désynchroniser les tableaux et l'enregistrement parent.

Après l'ajout de la ligne, le code la sélectionne et défile jusqu'à cette nouvelle ligne au cas où elle serait ajoutée au-dessous de la zone visible de la ListBox. Un dernier point très important est le positionnement du point d'insertion dans la nouvelle ligne pour que l'utilisateur puisse directement entrer le code produit du produit ajouté à la facture. C'est ce qui se produit dans un sous-formulaire, mais, pour une ListBox, vous devez programmer spécifiquement cette action. La commande qui exécute cette action est EDITER ELEMENT.

Faites le test de l'ajout d'une ligne. La plupart des produits de la base commencent par un zéro « 0 ». Vous pouvez donc obtenir une liste de choix conséquente en entrant 00, 01, 02, 03, 04 ou 05 et en tapant la touche tabulation.

VIII - Les méthodes objet des colonnes de la ListBox

La seule méthode complexe dans cette base est celle qui valide l'entrée d'un code produit par un utilisateur. Comme nous n'utilisons pas de champs, nous ne pouvons pas utiliser la propriété du lien « Liste des enregistrements liés ». Cette propriété concatène le joker (@) à la valeur saisie par l'utilisateur et lui propose une liste de choix des occurrences correspondantes. Tout cela est disponible sans aucune programmation. Lorsque nous utilisons une ListBox, si nous voulons le même résultat, nous devons le programmer.

Si (Faux)

` Méthode objet : atProductCode dans la List Box dans le formulaire [Facture]Entrée
` Note technique : ListBox/Sous-formulaire
` Créée par : Kent Wilbur
` Date: 7/18/2005

` Objectif: Ajouter l'information dans la ligne de ListBox lorsque l'utilisateur saisit un code produit

<>f_Version2004x2:=Vrai
<>fK_Wilbur:=Vrai
Fin de si

C_ENTIER LONG($LWindowID)

CHERCHER([Produits];[Produits]CodeProduit=atProductCode{atProductCode})
Si (Enregistrements trouves([Produits])=1)   `Remplir les tableaux avec l'enregistrement trouvé
atTitle{atProductCode}:=[Produits]Libellé
arSalePrice{atProductCode}:=[Produits]Prix
Sinon
CHERCHER([Produits];[Produits]CodeProduit=atProductCode{atProductCode}+"@")
`Recherche avec joker
Si (Enregistrements trouves([Produits])>1)   `Plus d'un enregistrement trouvé :
` faire choisir l'utilisateur dans une liste
TABLEAU TEXTE(atSelectProductCode;0)   `S'assurer que les tableaux sont vides
TABLEAU REEL(arSelectPrice;0)
TABLEAU TEXTE(atSelectTitle;0)
SELECTION VERS TABLEAU([Produits]CodeProduit;atSelectProductCode;
[Produits]Libellé;atSelectTitle;[Produits]Prix;arSelectPrice)
$LWindowID:=WIN_LNewWindow (<>WIN_LUseFormSize;<>WIN_LUseFormSize;
<>WIN_LUpper;Dialogue modal déplaçable ;->[zDialogs];"SeletProduct";
"Choisissez un produit";"*")
DIALOGUE([zDialogs];"SeletProduct")
FERMER FENETRE
Si (OK=1)  `transférer la ligne sélectionnée dans la ligne de ListBox
atProductCode{atProductCode}:=atSelectProductCode{atSelectProductCode}
atTitle{atProductCode}:=atSelectTitle{atSelectProductCode}
arSalePrice{atProductCode}:=arSelectPrice{atSelectProductCode}
Sinon   `Rien n'a été sélectionné, effacer les valeurs restantes dans la ligne
atProductCode{atProductCode}:=""
atTitle{atProductCode}:=""
arSalePrice{atProductCode}:=0
aiQty{atProductCode}:=0
Fin de si
TABLEAU TEXTE(atSelectProductCode;0)   `Libérer la mémoire utilisée par les tableaux
TABLEAU REEL(arSelectPrice;0)
TABLEAU TEXTE(atSelectTitle;0)
Sinon   `Aucun enregistrement trouvé, effacer les entrées de la ligne
ALERTE("Produit Inexistant")
atProductCode{atProductCode}:=""
atTitle{atProductCode}:=""
arSalePrice{atProductCode}:=0
aiQty{atProductCode}:=0
Fin de si
Fin de si

Invoice_CalcTotals (atProductCode)

Tout d'abord, le joker est concaténé à la valeur entrée par l'utilisateur et une recherche des enregistrements correspondants est effectuée sur la table des produits. Si un seul enregistrement est trouvé, il est utilisé. Si aucun enregistrement n'est trouvé, un message d'erreur est affiché.

Le cas complexe est celui où plusieurs enregistrements sont trouvés. Comme nous n'utilisons pas les propriétés automatiques du lien, nous pouvons afficher plus que les deux colonnes disponibles en standard. De plus, nous pouvons afficher une fenêtre où nous voulons et de la taille que nous voulons. L'ouverture de la fenêtre est effectuée par la méthode projet Win_LNewWindow. Si vous voulez en savoir plus sur ma méthode Win_LNewWindow, ses paramètres et comment elle fonctionne, lisez ma note technique « Opening Windows Where You Want Them » écrite en février 2004.

Nous examinerons le dialogue et le code qu'il utilise un peu plus tard. Mais, une fois que l'utilisateur a choisi quelque chose dans ce dialogue, l'exécution continue et affecte les valeurs choisies aux cellules correspondantes de la ligne. À la fin, le total de la facture est recalculé.

La seule autre colonne qui possède une méthode objet est la colonne Qty qui ne fait que recalculer le total de la facture.

` Méthode objet: aiQty idans la ListBox dans le formulaire [Factures] Entrée

` Note technique : ListBox/SousFormulaire
` Créée par : Kent Wilbur
` Date: 7/18/2005

` Objectif: Recalculer les données modifiées

Invoice_CalcTotals (aiQty)

IX - Mise à jour du total de la facture

Lorsque la méthode projet Invoice_CalcTotals reçoit en paramètre un numéro de ligne, elle recalcule la cellule arExtPrice qui contient le montant de la ligne. À chaque appel, la méthode boucle sur tous les éléments du tableau et additionne les montants pour les sauver dans le champ [Facture]TotalFacture.

` Méthode projet: Invoice_CalcTotals(entier long)

` Note technique : ListBox/Suous-formulaire
` Créée par : Kent Wilbur

` Objectif: Recalcul du total de la facture [Invoices]InvoiceTotal

` Déclaration des parametres
C_ENTIER LONG($1;$LLineItem)

` Déclaration des variables locales
C_ENTIER LONG($i)
C_REEL($rTotal)

` Réaffectation pour la lisibilité
Si (Nombre de parametres>0)
$LLineItem:=$1
arExtPrice{$LLineItem}:=arSalePrice{$LLineItem}*aiQty{$LLineItem}
Fin de si

$rTotal:=0
Boucle ($i;1;Taille tableau(arExtPrice))
$rTotal:=$rTotal+arExtPrice{$i}
Fin de boucle

[Facture]TotalFacture:=$rTotal

`Fin méthode

X - La ListBox de choix personnalisée

Le dernier élément à examiner rapidement est la liste de choix personnalisée. Elle a été créée pour permettre à l'utilisateur de choisir parmi les produits dont le code correspond partiellement à la valeur saisie.
Dans ce formulaire, la ListBox est beaucoup plus simple. Rien n'est saisissable, rien n'est caché, ce n'est que de l'affichage. Si l'utilisateur fait un double-clic, utilise la touche Retour ou la touche Entrée, l'enregistrement sélectionné est choisi. S'il utilise la touche d'échappement, le dialogue est annulé.
Le seul code associé à ce formulaire est la méthode objet de l'objet ListBox.

` Méthode objet: ListBox fSelectBox dans le formulaire ([zDialogs];"SeletProduct")

` Note technique : ListBox/Sous-formulaire
` Créée par : Kent Wilbur
` Date: 7/18/2005

` Objectif: Gérer les actions dans la ListBox

` Déclaration des variables locales
C_ENTIER LONG($LFormEvent)

$LFormEvent:=Evenement formulaire

Au cas ou
: ($LFormEvent=Sur chargement )
SELECTIONNER LIGNE LISTBOX(fSelectBox;1)   ` Sélectionner la première ligne
: ($LFormEvent=Sur double clic )
VALIDER

Fin de cas

XI - Limite de la ListBox

Il y a une limite à l'utilisation d'une ListBox. On ne peut pas l'imprimer. C'est un objet écran conçu uniquement pour l'interface utilisateur. Si vous voulez imprimer cette facture, vous devrez créer un formulaire spécifique pour l'impression qui utilise un sous-formulaire traditionnel.

XII - Résumé

Les ListBox sont un apport important à 4D. Nous avons seulement jeté un coup d'œil à deux exemples d'utilisation. En clair, je n'utilise plus jamais les zones de défilement groupées. Les ListBox sont bien plus simples à créer et à utiliser et elles offrent beaucoup de flexibilité et des propriétés qui n'existaient pas ou pas sans écrire beaucoup de code.

Ensuite, les ListBox ont certains avantages sur les formulaires inclus lorsqu'on les utilise comme sous-formulaires. Sans beaucoup de code, comme je vous l'ai montré, vous pouvez créer une présentation très commode qui remplace les sous-formulaires. Les ListBox peuvent être utilisées sans avoir recours aux transactions et sans accéder répétitivement au serveur pour manipuler les enregistrements dans le formulaire inclus.

Les ListBox sont un grand apport à 4D 2004.

XIII - Base exemple

Téléchargez la base exemple : base exemple