Faire gérer par l'utilisateur l'ordre des enregistrements dans une table 
L'objectif L'ensemble de nos notes techniques sont dans une table Paradox, comme tout ce que vous voyez sur notre site. Ces notes sont classées par thème (notes générales, méthodologie, ObjectPal, ...) et sont dans un certain ordre à l'intérieur d'un thème donné. Cet ordre peut être modifié au fil du temps (on peut choisir l'ordre alphabétique, ou un ordre de difficulté, ou regrouper les sujets voisins, ...). Puisque nous disposons d'une application Paradox nous permettant la mise à jour de ces notes, il est intéressant de faire gérer cet ordre directement par l'utilisateur (ici le documentaliste qui gère les notes techniques) et de lui permettre de modifier celui-ci à son gré. 
La structure des tables Si on veut présenter nos notes ordonnées par thème, il nous faut un index composé de l'identifiant du thème (nous avons une table des thèmes qui nous fournit le libellé et qui a également un ordre géré par l'utilisateur) et d'un champ Ordre (de type Number) pour classer les notes par thème.
Bien sûr nous pourrions prendre cet identifiant comme clé primaire. Ce serait une très mauvaise idée car chaque fois que nous modifierons l'ordre, il faudrait répercuter ce changement d'identifiant dans tous les liens pointant sur cet enregistrement. On préfèrera toujours une clé primaire non modifiable et si possible mono-champ.
On aura un identifiant primaire Id_NoteT de type entier (le format court suffira pendant quelques temps :-) et un index secondaire composé de Id_Theme et du champ Ordre (nous appelerons par exemple notre index IdTheme_Ordre ). 
Les objets de notre fiche La fiche Paradox qui nous permet de gérer le sommaire de nos notes technqiues comprend un cadre de table présentant la liste des notes techniques ordonnées suivant l'index IdTheme_Ordre.
Une première solution consiste à afficher la zone Ordre et à laisser l'utilisateur modifier celui-ci pour déplacer l'enregistrement.
Cette solution n'est pas toujours satisfaisante car elle nécessite pour être facilement utilisable que l'ordre soit un nombre entier et que l'utilisateur prévoit des "trous" lorsqu'il effectue ses insertions, pour anticiper de nouveaux ajouts et des modifications.
La solution que nous présentons ci-après nécessite un peu de programmation mais permet de masquer complètement le champ Ordre à l'utilisateur.
Nous ajoutons à côté de notre cadre de table un groupe de 2 boutons correspondant respectivement à la remontée et à la descente de notre note d'un cran dans la liste. Nous appelons ces boutons respectivement BtUp et BtDown et le groupe GRP_UP_DOWN . 
La programmation Il reste maintenant à gérer l'insertion à un endroit quelconque d'un nouvel enregistrement ainsi que le déplacement dans un sens ou dans l'autre d'un enregistrement.
Détection de l'insertion:
Dans la méthode action du cadre de table, nous insérons le code qui aura pour rôle de capter l'insertion et de déclencher le traitement adéquat: 
SWITCH CASE eventInfo.id() = DataUnlockRecord OR eventInfo.id() = DataPostRecord : ; ..... If Not GRP_UP_DOWN.ActionCreer_LO(eventInfo.id()) Then DISABLEDEFAULT eventInfo.setErrorCode(userError) endif
CASE eventInfo.id() = DataInsertRecord : ; ...... If Not GRP_UP_DOWN.ActionCreer_LO(eventInfo.id()) Then DISABLEDEFAULT eventInfo.setErrorCode(userError) endif
ENDSWITCH

Traitement de l'insertion:
La méthode ActionCreer_LO est localisée dans le groupe GRP_UP_DOWN et possède le code suivant (nous indiquons également les constantes et variables définies au niveau du groupe): 
Const TFNomST = "NoteT" ; nom de l'objet cadre de table ChpNomST = "Ordre" ; nom du champ Ordre dans la table endconst Var TFUI UIObject TCUI Tcursor OldNU, NU Number EndVar
method ActionCreer_LO(Const ActionIdSI SmallInt) Logical TFUI.attach(TFNomST) TCUI.attach(TFUI) switch case ActionIdSI = DataInsertRecord : gAutoAppendLO = TFUI.blankRecord if not gAutoAppendLO then NU = TCUI.(ChpNomST) if not TCUI.priorRecord() then OldNU = 0 else OldNU = TCUI.(ChpNomST) endif NU = (NU+OldNU)/2 else if TCUI.nRecords() = 0 then NU = 1 else TCUI.end() NU = TCUI.(ChpNomST) + 1 endif endif case ActionIdSI = DataUnlockRecord and TFUI.inserting: dmPut(TFUI.tableName, ChpNomST, NU) endswitch
return true endMethod

Traitement du bouton Up:
Le bouton BtUp permet de faire remonter la note dans la liste. Son code (méthode PushButton) est le suivant. 
TFUI.attach(TFNomST) If TFUI.blankRecord Then Return EndIf If TFUI.locked Then If Not TFUI.action(DataUnLockRecord) Then Return EndIf EndIf TCUI.attach(TFUI) OldNU = TCUI.(ChpNomST) If TCUI.atFirst() Then Return EndIf TCUI.priorRecord() NU = TCUI.(ChpNomST) TCUI.(ChpNomST) = (OldNU + NU) /2 TCUI.unLockRecord() TCUI.nextRecord() TCUI.(ChpNomST) = NU TCUI.unLockRecord() TCUI.nextRecord() TCUI.(ChpNomST) = OldNU TCUI.unLockRecord() TFUI.resync(TCUI) TFUI.priorRecord()

Traitement du bouton Down:
La fonction est l'inverse de la précédente. La programmation est la suivante: 
TFUI.attach(TFNomST) If TFUI.blankRecord Then Return EndIf If TFUI.locked Then If Not TFUI.action(DataUnLockRecord) Then ReturnEndIf EndIf TCUI.attach(TFUI) OldNU = TCUI.(ChpNomST) If TCUI.atLast() Then Return EndIf TCUI.nextRecord() NU = TCUI.(ChpNomST) TCUI.(ChpNomST) = (OldNU +NU)/2 TCUI.unLockRecord() TCUI.priorRecord() TCUI.(ChpNomST) = NU TCUI.unLockRecord() TCUI.priorRecord() TCUI.(ChpNomST) = OldNU TCUI.unLockRecord() TFUI.resync(TCUI) TFUI.nextRecord()

Le système peut être enrichi : par exemple en ajoutant d'autres boutons permettant de déplacer l'enregistrement par saut de n enregistrements ou de l'envoyer en début ou fin de liste. Les programmeurs intrépides peuvent également se lancer dans une programmation d'un drag and drop (attention cependant à la localisation du code) et dans un paramétrage complet du système.
N'hésitez pas à nous faire part de vos résultats ! 
|