FMX – Boîte de choix

Auto-complétion et alternatives

Au cours de mes développements d’applications, j’ai souvent eu à proposer des boîtes de choix à l’utilisateur, choix généralement fournis à partir d’une autre table de données. Remplir une boîte de choix (TComboBox) par l’intermédiaire des LiveBindings est chose aisée ; plus ardu est d’aider l’utilisateur à faire son choix lorsque les propositions sont nombreuses, par exemple afin de sélectionner un client pour obtenir son code. Le défi de cet article est de proposer et étayer plusieurs solutions en fonction des besoins.

Commentez Donner une note  l'article (5)

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Illustration

L’application que je présente dans ce chapitre n’est là que comme base de départ de la réflexion. J’utilise un fichier des clients disponible dans le répertoire exemple de Delphi (<répertoire d’installation>\Samples\Data\clients.cds). En voici le design :

J’ai délibérément choisi un programme multiplateforme et donc utilise à fond les possibilités de LiveBindings pour rédiger un programme avec le moins de lignes de code possible (pour cette illustration, pas une seule).

Image non disponible

À l’exécution, j’obtiens ceci :

Image non disponible

La grille n’est là que pour montrer les données chargées. En réalité, seule la boîte de choix sera utilisée.

L’objectif, à savoir sélectionner le numéro du client, est atteint, mais à moins que l’utilisateur ne connaisse le petit nom de chacun en fonction de son numéro, cette boîte de choix n’est pas très adaptée. Est-ce améliorable ? Heureusement oui, en utilisant la propriété FillDisplayCustomFormat de la liaison et en la modifiant ainsi :

%s+' '+Dataset.LAST_NAME.Text+' '+Dataset.FIRST_NAME.Text

Pour ceux qui ne sont pas à l’aise avec l’utilisation des LiveBindings, quelques explications. Cette fonctionnalité, apparue en même temps que la possibilité de la compilation multiplateforme, contient, entre autres choses, un interpréteur d’expressions. La propriété FillDisplayCustomFormat n’est qu’une possibilité de passer une expression au cours du remplissage de la liste.

Bien sûr, par codification (par exemple en ajoutant une colonne calculée à l’ensemble de données et en codant l’événement OnCalcFields), en intervenant plus en amont dans la source de données (par l’obtention des données par SQL) ou encore en remplissant la liste par code, on peut très bien obtenir le même résultat.

Image non disponible

Cette manipulation rend certes la boîte de choix plus explicite, mais force à élargir le composant afin de voir le texte entier. À ce propos, je signale que jouer sur la propriété ItemWidth permet d’obtenir une liste plus large que la boîte de choix.

Bien sûr, la « formule » de l’expression peut être changée. Notez surtout le Dataset.<nom_de_colonne>.text qui permet d’accéder aux valeurs.

II. Auto-complétion

La première chose pratique à proposer à l’utilisateur, c’est bien la fonctionnalité d’auto-complétion, surtout si la liste est importante. Le principe en est relativement simple : si l’utilisateur frappe des touches en un laps de temps donné, la boîte se positionne sur l’élément correspondant au plus près au groupe de touches frappées et évite ainsi à l’utilisateur de faire une recherche dans toute la liste en utilisant le défilement.

II-A. Première solution : dériver le composant

La dérivation du composant est simple dans le principe et implique peu de code.

Si vous avez déjà déposé un TComboBox sur votre forme, vous n’aurez même pas besoin d’indiquer l’utilisation de l’unité FMX.ListBox dans la liste des unités nécessaires (uses).

Deux variables privées sont à ajouter au TCombobox de base, une pour tester les délais (chronometre) et une autre pour mémoriser les touches (keys). En dernier lieu, la procédure OnKeyDown sera surclassée pour implémenter la fonctionnalité souhaitée.

 
Sélectionnez
type

  TCombobox = class(FMX.ListBox.TComboBox)
  private
    Chronometre:TDatetime;
    Keys:string;
  protected
    procedure KeyDown(var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState);override;
  end;

Vous noterez la formulation de la dérivation TCombobox = class(FMX.ListBox.TComboBox)

Je ne crée pas un nouveau composant, mais force un nouveau comportement du TComboBox standard à l’intérieur de la forme.

Cette opération (hacking) me permet d’accéder à toutes les propriétés non publiées, comme Count ou ItemIndex, ou normalement inaccessibles comme Items.

 
Sélectionnez
{ TCombobox }

procedure TCombobox.KeyDown(var Key: Word; var KeyChar: System.WideChar;
  Shift: TShiftState);
var I: Integer;
begin
  if key=vkReturn then exit;
  if CharInSet(keychar,[chr(48)..chr(57),chr(65)..chr(90),chr(97)..chr(122)])
   then begin
    // préférences personnelles de délai 500 ms
    if MilliSecondsBetween(Chronometre,Now)<500
      then keys:=keys+keychar
      else keys:=keychar; // sinon nouvelle séquence

    //reset du chronomètre
    Chronometre:=Now;
    //Recherche de l'élément
    for I := 0 to Self.count-1 do
 //    if StartsText(Keys,Items[i]) then begin
       if ContainsText(Items[i],Keys) then begin
        Self.itemindex:=i;
        break;  //premier élément trouvé
      end;
  end;
  inherited;
end;

L’utilisation des fonctions MilliSecondsBetween, StartsTextWith ou ContainsText implique l’ajout des unités System.DateUtils pour la première et System.StrUtils pour les autres.

L’avantage de cette démarche est sa simplicité, mais elle n’est pas sans inconvénient :

  • du fait de la dérivation du composant de base, tous les composants TCombobox sur la forme auront le même comportement, ce qui n’est peut-être pas souhaitable ;
  • la modification se fait pour l’unité alors qu’une application n’en contient rarement qu’une seule ;
  • le délai de frappe n’est pas modifiable ;
  • les touches sont filtrées par la fonction CharInSet (chiffres, lettres majuscules et minuscules), ce qui vous a peut-être un peu choqué en ne faisant pas la part belle à l’internationalisation du procédé. Une solution alternative serait de faire l’inverse et de filtrer les touches de clavier « spéciales », mais cela suppose une bonne connaissance des divers claviers.

II-B. Deuxième solution : créer un nouveau composant

Dès que l’on parle de créer un nouveau composant, le doute (je n’ose écrire la peur) s’installe. Un débutant se dira que c’est compliqué. Un programmeur confirmé y verra d’autres inconvénients comme la « perte de temps », « un casse-tête n’en valant pas la peine », etc.

Si je ne peux rien répondre au programmeur confirmé, je peux au moins rassurer le débutant : non, la création d’un composant n’est pas si compliquée qu’elle en a l’air et la première section a déjà bien débroussaillé le chemin. En gros, il s’agit de reprendre dans une unité le code indiqué au-dessus, de saupoudrer d’un peu de propriétés et d’ajouter l’enregistrement du composant dans l’EDI !

Comment faire sans stress ? Sans aucune hésitation, en passant par l’assistant de création de composant (dans le menu : l’option « Composant » puis « Nouveau composant »).

N’hésitez pas à consulter la documentation à ce propos.

Image non disponible
Image non disponible
Image non disponible

En trois étapes, j’obtiens une première unité squelette.

Avant d’aller plus avant, j’aimerais expliquer pourquoi j’ai préféré passer par la création d’une simple source plutôt que de demander la création d’un nouveau package. Comme toute création de composant est à faire précautionneusement, je préfère déboguer mon code et le meilleur moyen pour cela est bien de créer le composant à l’exécution avant de l’installer. Je reporte donc à plus tard la création du paquet !

 
Sélectionnez
unit AutoComplete;

interface

uses
  System.SysUtils, System.Classes, FMX.Types, FMX.Controls, FMX.ListBox;

type
  TAutoCompleteComboBox = class(TComboBox)
  private
    { Déclarations privées }
  protected
    { Déclarations protégées }
  public
    { Déclarations publiques }
  published
    { Déclarations publiées }
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Tutoriels', [TAutoCompleteComboBox]);
end;

end.

Après réflexion, je vais d’ores et déjà ajouter quelques propriétés. La réponse rapide que j’ai déjà apportée mettait en lumière les variables privées Chronomometre et Keys. Le délai de frappe modifiable serait évidemment un plus. Enfin, j’envisage également l’ajout du mode de recherche (début de chaîne ou contenu dans) sensible à la casse ou non.

Après ce recensement des fonctionnalités désirées, j’insère les lignes de code suivantes dans la partie privée :

private
Sélectionnez
FChronometre: TDateTime;
FKeys: string;

En préalable à la déclaration des nouvelles propriétés que je vais ajouter, je déclare un nouveau type pour définir les modes de recherche :

type
Sélectionnez
TSearchMode = (smStarts,smContains);

Enfin, dans la partie publiée, j’ajoute les lignes suivantes :

published
Sélectionnez
property Delai: Integer read GetDelai write SetDelai default 500;
property ModeRecherche : TSearchMode read FSearchMode write SetMode default TSearchMode.smStarts;
property CasseSensible : Boolean read FCaseSensitive write SetCaseSensitive default false;

L’utilisation de la combinaison de touche Maj+Ctrl+C va me faciliter la tâche, en écrivant, pour moi, les lignes de code manquantes dans la classe (ajout des noms de propriétés, ajout des fonctions et procédures) et dans la partie implementation.

Après Alt+C
Sélectionnez
unit AutoComplete;

interface

uses
  System.SysUtils, System.Classes, //  System.UITypes,
  FMX.Types, FMX.Controls, FMX.ListBox;

type
   TSearchMode = (smStarts,smContains);

   TAutoCompleteComboBox = class(TComboBox)
   private
     FChronometre: TDateTime;
     FKeys: string;
     FDelai: Integer;
     FSearchMode : TSearchMode;
     FCaseSensitive: Boolean;
     procedure SetMode(const Value: TSearchMode);
     procedure SetCaseSensitive(const Value: Boolean);
   protected
     procedure SetDelai(AValue: Integer);
     function GetDelai: Integer;
   public
   published
     property Delai: Integer read GetDelai write SetDelai default 500;
     property ModeRecherche : TSearchMode read FSearchMode write SetMode default TSearchMode.smStarts;
     property CasseSensible : Boolean read FCaseSensitive write SetCaseSensitive default false;
   end;

procedure Register;

implementation

procedure TAutoCompleteComboBox.SetCaseSensitive(const Value: Boolean);
begin
  FCaseSensitive := Value;
end;

procedure TAutoCompleteComboBox.SetDelai(AValue: Integer);
begin
 FDelai := AValue
end;

procedure TAutoCompleteComboBox.SetMode(const Value: TSearchMode);
begin
FSearchMode:=Value;
end;

function TAutoCompleteComboBox.GetDelai: Integer;
begin
   SetDelai(FDelai); // vérifier délai raisonnable  0 et 4 s
   Result := FDelai;
end;

procedure Register;
begin
   RegisterComponents('Tutoriels', [TAutoCompleteComboBox]);
end;

end.

Je vais maintenant ajouter le moteur de l’auto-complétion, tel que déjà entre-aperçu dans la partie II.A. Une seule différence : prendre en compte mes différents modes de recherche.

Dans la partie protégée, j’ajoute la déclaration de la procédure KeyDown qui écrasera celle héritée :

protected
Sélectionnez
procedure KeyDown(var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState); override;

Toutefois, pour alléger le code, je vais séparer la partie recherche dans une chaîne de la procédure et mettre celle-ci dans une fonction qui renverra l’index de l’élément de liste trouvé :

public
Sélectionnez
function Recherche(AText: string; AShowDropDown: Boolean = True): Integer;

Pourquoi cette déclaration se fait-elle dans la partie publique ? Parce que, même si je n’en vois pas encore l’utilité, il me sera possible d’utiliser cette fonction à l’exécution !

J’utilise de nouveau la combinaison de touches Maj+Ctrl+C qui ajoute les lignes suivantes dans la partie implementation.

 
Sélectionnez
function TAutoCompleteComboBox.Recherche(AText: string;
   AShowDropDown: Boolean = True): Integer;
begin

end;

procedure TAutoCompleteComboBox.KeyDown(var Key: Word; var KeyChar:System.WideChar;  Shift: TShiftState);
begin
  inherited;
end;

À partir de là, c’est plus une question de codage qu’autre chose ! Voici donc ma vision du processus.

procedure KeyDown
Sélectionnez
procedure TAutoCompleteComboBox.KeyDown(var Key: Word; var KeyChar:System.WideChar;  Shift: TShiftState);
begin
   // test des caractères saisis 
   if CharInSet(keychar,[chr(48)..chr(57),chr(65)..chr(90),chr(97)..chr(122)]) then
   begin
      // vérification du délai
      if MilliSecondsBetween(TimeOf(Now), FChronometre)<=FDelai
      then FKeys := FKeys + KeyChar
      else FKeys := KeyChar;
     // recherche du texte et positionnement dans la liste
     itemIndex:= Recherche(FKeys);
     // réinitialisation du chronomètre
     if ItemIndex<>-1 then
     begin
       FChronometre := TimeOf(Now);
     end;
   end
   else inherited;
end;
Fonction Recherche
Sélectionnez
function TAutoCompleteComboBox.Recherche(AText: string;
   AShowDropDown: Boolean = True): Integer;
var found : Boolean;
begin
 // au besoin, force l’affichage de la liste
 if (not Self.DroppedDown) and (AShowDropDown) then Self.DropDown;
 found :=False;
 // recherche dans la liste en fonction des options
 for result := 0 to Self.Items.Count - 1 do
   begin
    if FSearchMode=TSearchMode.smStarts then
      begin
      if FCaseSensitive 
          then found:=StartsStr(FKeys, Self.Items[Result])
          else found:=StartsText(FKeys, Self.Items[Result]);
        end
    else begin
      if FCaseSensitive 
          then found:=ContainsStr(Self.Items[Result],FKeys)
          else found:=ContainsText(Self.Items[Result],FKeys);
       end;
    if found then Break;
   end;
 if not found then Result := -1;
end;

Une fois l’unité réalisée et sauvegardée, je passe aux tests. Il est temps que je dévoile mon petit programme de test que vous pourrez retrouver entéléchargement ici.

Image non disponible
Design

Bien sûr, au design, les deux boîtes que je vais tester ne vont pas apparaître.

Image non disponible
Exécution

Quelques explications de code s’imposent, surtout en ce qui concerne le test avec un remplissage fait par l’intermédiaire des LiveBindings.

FormCreate
Sélectionnez
procedure TForm11.FormCreate(Sender: TObject);
var AAutoComplete,BAutoComplete : TAutoCompleteComboBox;
begin
// création Composant AutoComplete Test
// remplissage par code  
AAutoComplete:=TAutoCompleteComboBox.Create(Self);
with AAutoComplete do
 begin
   Parent:=Self;
   Position.X:=24;
   Position.Y:=160;
   Width:=241;
   CasseSensible:=False;
   Delai:=500;
   ModeRecherche:=TSearchMode.smContains;
   Clients.Active:=True;
   Clients.First;
   while not Clients.EOF do
     begin
       Items.Add(format('%.0f %s %s',[ClientsSS_NUMBER.asFloat,
                                      ClientsLAST_NAME.AsString,
                                      ClientsFIRST_NAME.asString]));
       Clients.Next;
     end;
   Clients.Active:=False;
 end;
// simulation LiveBindings Test
BAutoComplete:=TAutoCompleteComboBox.Create(Self);
with BAutoComplete do
 begin
   Parent:=Self;
   Position.X:=24;
   Position.Y:=224;
   Width:=241;
   CasseSensible:=False;
   Delai:=500;
   ModeRecherche:=TSearchMode.smContains;
 end;


with TLinkFillControlToField.Create(Self) do // pas de synchronisation 
// with TLinkListControltoField.Create(Self) do // avec synchronisation
  begin
      Control := BAutoComplete;
      Track := True;
      FillDataSource := BindSourceDB1;
      FillDisplayFieldName := 'SS_NUMBER';
      FillDisplayCustomFormat := '%s+'#39' '#39'+Dataset.LAST_NAME.Text+'#39' '#39'+Dataset.FIRST_NAME.text';
      AutoFill := True;
  end;
Clients.Active:=True;
end;

En effet, tester la création d’un composant utilisant les LiveBindings interdit toute possibilité d’utiliser le concepteur visuel de liaison ou même l’ajout d’une liaison au TBindingsList au cours du design. J’utilise alors la technique exposée dans le tutoriel LiveBindings et POO pour créer le lien au cours de l’exécution.

Dernier point qui a son importance : vous remarquerez qu’après le remplissage par code du premier composant via une boucle classique, l’ensemble de données est refermé. Il est très important de le faire et de le rouvrir après la création par programmation du lien entre la seconde boîte de recherche et les données.

Comme les tests sont concluants, il ne me reste plus qu’à créer un nouveau paquet ou ajouter le source de mon composant dans un paquet existant avant de l’installer. Pour cela, je fais à nouveau appel à l’assistant de création de composant. Cette fois-ci, j’utilise les options du menu : Composant/Installer un composant

Image non disponible
Image non disponible

Suggestion d’amélioration du composant : travailler sur le filtrage de touches de la procédure KeyDown.

Source du composant téléchargeableici.

Attention, avant de le tester, vérifiez les chemins des fichiers.

Image non disponible
menu contextuel

III. Alternative

Toujours frileux ? Je vous propose une alternative qui ouvre de nombreuses perspectives tout en restant dans l’objectif premier : l’aide au choix.

III-A. Principe

L’origine de ce tutoriel est, en fait, un billet de mon blog où je déplorais les limites du TComboBox. Comme d’habitude, pressé par le temps, il me fallait trouver une solution rapidement. En décomposant les fonctionnalités de la boîte de choix, j’y ai trouvé deux choses : une zone de saisie (quoiqu’en lecture seule) et une liste. D’où l’idée d’associer un TEdit et un TListView, ce dernier ayant déjà fait l’objet de plusieurs communications de ma part, aussi bien dans mon blog que via divers tutoriels que vous pourrez retrouver sur mon site personnel

Le design est donc simple et, bien sûr, la liste ne sera visible qu’à la demande.

Image non disponible

Et le résultat est au rendez-vous avec quelques lignes de code seulement ! Le remplissage de la liste (plus de 4000 éléments) se fait via Livebindings.

 
Sélectionnez
// montrer la liste
procedure TForm1.SearchEditButton1Click(Sender: TObject);
begin
ListeClients.Visible:=True;
end;

// cacher la liste en cas de sortie (clic dans une autre zone de la forme)
procedure TForm1.ListeClientsExit(Sender: TObject);
begin
ListeClients.Visible:=False;
end;

// gérer la sélection de l'élément
procedure TForm1.ListeClientsItemClick(const Sender: TObject;
  const AItem: TListViewItem);
begin
/// récupération de la valeur
/// première solution, traitement du texte affiché
// SearchClient.Text:=LeftStr(AItem.Text,4);
/// deuxième solution, la valeur souhaitée est reportée, seule,
/// dans une zone de l'item de liste (cachée ou non)
// SearchClient.Text:=AItem.Detail;
/// troisième solution, rendue possible par la synchronisation
SearchClient.Text:=Datas.FDClientsNUM_CLIENT.AsString;
ListeClients.Visible:=False;
end;

// abandon de la recherche par l’utilisation de la touche Escape
procedure TForm1.ListeClientsKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: Char; Shift: TShiftState);
begin
if Key=vkEscape then
 begin
  ListeClients.Visible:=False;
  TEdit(ListeClients.Parent).SetFocus;
 end;
end;
procedure TForm1.ListeClientsExit(Sender: TObject);
begin
ListeClients.Visible:=False;
end;

Toutefois, impossible d’intercepter l’utilisation la touche Escape à l’intérieur de la boîte de recherche ! Pour pouvoir le faire, il faudra, en premier lieu, indiquer l’utilisation de l’unité FMX.SearchBox, puis associer l’événement ListeClientsKeyDown à la boîte de recherche

(voir ce billet).

 
Sélectionnez
uses ... FMX.SearchBox;

procedure TForm1.FormCreate(Sender: TObject);
begin
...
// association de l'évènement
if ListeClients.controls[1].ClassType = TSearchBox
  then  TSearchBox(ListeClients.controls[1]).OnKeyDown:=ListeClientsKeyDown;
end;
Image non disponible
Image non disponible

Vous excuserez la censure, l’image fournie ici étant réalisée à partir de données d’entreprise !

L’utilisation de cette technique a un avantage certain puisque la liste peut être largement personnalisable, par exemple en y ajoutant des groupes.

Ci-dessous, une boîte de choix sur l’ensemble de données customer.cds fourni par Embarcadero dans le répertoire exemple (..\samples\data) que vous retrouverez aussi dans le second exemple.

Image non disponible

III-B. Utilisation de cadres

Mettre ces deux composants ainsi qu’une partie du code dans un cadre FireMonkey pourrait également être une solution intéressante et permettrait de créer un pseudo-composant. Je vous invite à visionner à ce propos le webinaire animé par Patrick Prémartin : Créer des composants visuels sans faire de composant.

J’ai donc tenté l’expérience. Tout d’abord, voici mon cadre :

Image non disponible

Posé sur une forme, voilà un premier résultat encourageant :

Image non disponible

Malgré cela, j’émettrai une première critique : les TBindingsList, TBindSource et TClientDataset commencent à foisonner ! S’il y en a peu, cela peut rester gérable ; en tout cas, le concepteur visuel de liaisons reste utilisable.

Ma crainte était surtout la suivante : la liste serait-elle « contrainte » à la taille du cadre ou s’afficherait-elle en entier ? Elle s’affiche en entier, telle que dessinée, ce qui est la bonne nouvelle. Au moment du design, je peux donc réduire, après avoir fait les ajustements de la liste, le cadre à la taille de la zone de saisie.

Image non disponible
Image non disponible

Suite à ce premier test, l’éditeur de liaisons démontrait que modifier la source de données était possible, mais il me fallait écrire un code qui n’utilisait aucun nom de colonne et aucune synchronisation. Comme c’était moins pratique, j’ai dû faire une première concession au niveau du remplissage du TEdit associé à la liste. J’ai choisi de changer l’apparence de l’élément de liste (ListItem en ListItemRightDetail), d’indiquer au niveau des liens que la valeur à récupérer se trouve dans l’objet detail, objet que je peux même rendre invisible.

Pourquoi l’objet detail ? Parce qu’il est très facile d’y accéder !

Il est très important de changer le type d’élément de liste. Presque tous les types contiennent l’élément detail, il faut juste faire attention à ce que ce soit un de ceux-là !

Il suffit alors de changer le code lors de la sélection de l’élément de liste :

 
Sélectionnez
// Concession 
procedure TComboBoxFrame.ListeItemClick(const Sender: TObject;
  const AItem: TListViewItem);
begin
 Edit1.Text:=AItem.Detail;  // valeur sélectionnée
 Liste1.Visible:=False ;
 Edit1.SetFocus ;
end;

Résultat :

Image non disponible

Cette solution est compliquée puisqu’elle nécessite plus de manipulations au moment du design, mais pas insurmontable. Par ailleurs, je peux désormais changer au moment du design les sources de données et les liens et obtenir deux listes différentes à partir d’un même cadre. De là ma réflexion suivante : autant retirer toute notion de données et de liaisons.

Image non disponible

Toutes ces « améliorations » ont un coût. Ce qui, au départ, n’était qu’une utilisation d’un cadre avec ses données devient plus une copie de deux composants avec, je le concède, quelques méthodes particulières. Il y a de plus en plus d’opérations à mener après le placement du cadre :

  • établir les liaisons (LiveBindings) ;
  • ajuster la position de la liste ;
  • rendre la liste invisible une fois le design terminé ;
  • réduire le cadre à la taille de la zone de saisie ;
  • etc.

Au sein d’un même projet, l’utilisation de ce dernier cadre reste donc très pratique.

La dernière étape décrite au cours du webinaire consistait à faire de ce cadre un vrai composant, mais elle s’est révélée un échec dans ce cas. Bien que la technique exposée par Patrick Prémartin soit possible, ce qui cloche c’est que tout ou presque est non modifiable, dont toute la partie concernant les LiveBindings. Je n’ai pas poussé plus avant après ce constat.

Je déduirai de cette expérience que l’alternative est applicable avec des cadres, voire des templates de cadre, mais pas avec un composant créé à partir d’un cadre.

Vous retrouverez mes essais dans ce projet.

Attention, pour le tester, vérifiez les chemins des fichiers de données et ajoutez le cadre dans votre liste de composants.

III-C. Astuces

N’hésitez pas à modifier les propriétés des éléments de liste.

Image non disponible

Ou même à basculer en mode conception pour rendre votre liste conforme à vos souhaits.

Image non disponible
Liste en mode conception

IV. Pour conclure

Je vous ai exposé trois méthodes pour améliorer l’utilisation de boîtes de choix :

  • dériver la classe principale ;
  • créer un nouveau composant ;
  • utiliser un TEdit associé à un TListView et exploiter toutes les possibilités de cette dernière. Et, en option, utiliser cette alternative dans un cadre.

J’espère aussi avoir un peu vulgarisé la création de composant par héritage !

IV-A. Références utilisées

Je ne pourrais citer toutes les questions posées dans les forums, ce qu’un moteur de recherche avec quelques mots clés pourra facilement vous ressortir. Je tiens cependant à signaler ces deux articles de Brovin Yaroslav : « Nouvelle approche de développement de contrôles FireMonkey 'Contrôle - Modèle – Présentation' » Partie 1, Partie 2 TEdit avec auto complétion, qui pourraient ouvrir d’autres perspectives intéressantes.

IV-B. Remerciements

Au terme de cet article, je tiens à remercier les différents intervenants. Ceux qui, au cours d’une discussion dans le forum m’ont permis de saisir les subtilités de la dérivation utilisée chapitre II.A et mes quelques testeurs. Patrick Prémartin sans qui je n’aurais jamais songé aux cadres du chapitre III.B et, par ricochet, l’organisateur de ce webinaire Maxime Capellot.

Et, bien évidemment, un immense merci à l’équipe rédactionnelle tant aux relecteurs techniques gvasseur58 qu’au correcteur f-leb de mon orthographe et grammaire quelques fois hésitantes !

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2019 Serge Girard. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.