Problème affectation dynamique ImageList à un ComboBoxEx.Images [Résolu]

Signaler
Messages postés
129
Date d'inscription
mercredi 4 février 2004
Statut
Membre
Dernière intervention
25 juillet 2012
-
Guillemouze
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
-
Bonsoir le forum,

J'ai un problème avec le composant ComboBoxEx et plus particulièrement avec sa propriété Images.

J'ai créé une procédure qui permet de mettre à jour une ComboBoxEx de façon dynamique (texte + image) dont voici le code simplifié :

procedure MiseAJour(var cmbListeDeroulante: TComboBoxEx);
var
  i: Integer;
  ListeImages: TImageList;
  Bitmap: TBitmap;

begin
  try
    // Permet de créer la liste des images et de la remplir.
    ListeImages := TImageList.Create(nil);
    ListeImages.Height := 20;
    ListeImages.Width := 20;
    Bitmap:= TBitmap.Create;
    Bitmap.LoadFromFile('C:\Images\Image1.bmp');
    ListeImages.Add(Bitmap, nil);
    Bitmap.LoadFromFile('C:\Images\Image2.bmp');
    ListeImages.Add(Bitmap, nil);
    Bitmap.LoadFromFile('C:\Images\Image3.bmp');
    ListeImages.Add(Bitmap, nil);
    Bitmap.LoadFromFile('C:\Images\Image4.bmp');
    ListeImages.Add(Bitmap, nil);
    Bitmap.LoadFromFile('C:\Images\Image5.bmp');
    ListeImages.Add(Bitmap, nil);

    // Permet d'associer la liste d'images à la liste déroulante.
    cmbListeDeroulante.Images.Assign(ListeImages);

    // Permet de nettoyer la liste déroulante dans le cas où il y a déjà des données.
    cmbListeDeroulante.Clear;

    // Permet d'ajouter les éléments dans la liste déroulante.
    for i := 0 to 4 do
      cmbListeDeroulante.ItemsEx.AddItem('Image' + IntToStr(i + 1), i, i, -1, -1, nil);
  finally
    ListeImages.Free;
    imgBitmap.Free;
  end;
end;

Dans l'exemple ci-dessus j'obtiens un Access violation. J'ai donc essayé cette variante en remplaçant le "Assign" (ligne en vert) :

    cmbListeDeroulante.Images.AddImages(ListeImages);

Mais là, même résultat c'est-à-dire Access violation. J'ai donc pensé que Images n'était pas créé et j'ai donc ajouté cette ligne de code avant le "Assign" (ligne en vert) :

    cmbListeDeroulante.Images.Create(nil);
    cmbListeDeroulante.Images.Assign(ListeImages);

Et là toujours cet Access violation !

Quelqu'un peut-il m'aiguiller SVP car je ne vois pas pourquoi avec la création j'ai un Access violation ?

Merci d'avance pour votre aide.

Que la force soit avec vous !

9 réponses

Messages postés
4200
Date d'inscription
samedi 16 octobre 2004
Statut
Modérateur
Dernière intervention
2 janvier 2019
27
Explications :

cela est du au fait que, comme les Evenements, les propriétés du style Images, PopupMenu, Action, ActiveControl, Menu, etc, ne sont la que pour stocker une instance existante d'un objet.

voici comme elle sont déclarée :

TMaClasse = class
private
  fCompo: TComponent;
  // Ceci est en fait un pointeur sur l'instance existante d'un composant, il est nil par defaut

  procedure SetCompo(Value: TComponent);
  // permet d'effectuer les operations necessaire pour l'assignation a fCompo
public
  procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  // permet de gerer la notification de liberation externe de fCompo, sans cela, un appel a fCompo alors
  // que ce dernier serait libéré provoquerai une erreur dans la classe.



  property Component : TComponent read fCompo write SetCompo;
  // ceci est la propriété visible permettant de "linker" un composant a notre classe.
end;

procedure TMaClasse.Notification(AComponent: TComponent; Operation: TOperation);
begin
  // si le composant linké se libère on remet fCompo a nil.  if (AComponent fCompo) and (Operation opRemove) then
    fCompo := nil;
  inherited; 
end;



procedure MaClasse.SetCompo(Value: TComponent);
begin
  if fCompo <> Value then
  begin
    // Si fCompo etait assigné on lui indique de ne plus prevenir la classe de sa libération
    if Assigned(fCompo) then
      fCompo.RemoveFreeNotification;
 
    // on assigne notre nouveau composant ou nil
    fCompo := Value;
    // si fCompo est de nouveau assigné, il doit nous prevenir de son eventuelle libération
    if Assigned(fCompo) then
      fCompo.FreeNotification(Self);
  end;
end;

Voila, ce genre de propriété permet de linké un composant avec un autre, le Owner ne doit pas, en regles generales, effectuer la creation ou la libération d'une telle propriété. Elles ne servent qu'a pouvoir interragir avec un composant.

Messages postés
4200
Date d'inscription
samedi 16 octobre 2004
Statut
Modérateur
Dernière intervention
2 janvier 2019
27
procedure UpdateImageList(ComboBox: TComboBoxEx; ImageList: TImageList; const ImagesName : array of string);
var
  I : Integer;
  BMP: TBitmap;
begin  if (Length(ImagesName) 0) or (ComboBox nil) or (ImageList = nil) then
    Exit;

  BMP := TBitmap.Create;
  try
    ComboBox.ItemsEx.BeginUpdate;
    try
      ComboBox.Clear;
      ComboBox.Images := ImageList;

      for I := Low(ImagesName) to high(ImagesName) do
      begin
        if FileExists(
ImagesName[I]
) then
        begin
          BMP.LoadFromFile(
ImagesName[I]
);
          ImageList.Add(BMP, nil);
          ComboBox.ItemsEx.AddItem(
ImagesName[I]
, I, I, -1, -1, nil);
        end;
      end;
    finally
      ComboBox.ItemsEx.EndUpdate;
    end;
  finally
    BMP.Free;
  end;
end;






Messages postés
4200
Date d'inscription
samedi 16 octobre 2004
Statut
Modérateur
Dernière intervention
2 janvier 2019
27
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls;

type
  TForm1 = class(TForm)
    ComboBoxEx1: TComboBoxEx;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    fImageList : TImageList;
  protected
    procedure UpdateImageList(ComboBox: TComboBoxEx);
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure Tform1.UpdateImageList(ComboBox: TComboBoxEx);
var
  I : Integer;
  BMP: TBitmap;
  FLN, PTH : String;
begin
  BMP := TBitmap.Create;
  try
    ComboBox.ItemsEx.BeginUpdate;
    try
      ComboBox.Clear;
      ComboBox.Images := fImageList;

      PTH := 'c:\images\';
      for I := 0 to 4 do
      begin
        FLN := 'image'+IntToStr(I+1)+'.bmp';
        if FileExists(PTH+FLN) then
        begin
          BMP.LoadFromFile(PTH+FLN);
          fImageList.Add(BMP, nil);
          ComboBox.ItemsEx.AddItem(FLN, I, I, -1, -1, nil);
        end;
      end;
    finally
      ComboBox.ItemsEx.EndUpdate;
    end;
  finally
    BMP.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fImageList        := TImageList.Create(Self);
  fImageList.Width  := 20;
  fImageList.Height := 20;
  UpdateImageList(ComboBoxEx1);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  fImageList.Free;
end;

end.

Messages postés
129
Date d'inscription
mercredi 4 février 2004
Statut
Membre
Dernière intervention
25 juillet 2012
1
Merci beaucoup foxi pour ta réponse si rapide et si complète, désolé de ne pas avoir pu être aussi réactif !

Donc si j'ai bien compris, la propriété Images du composant ComboBoxEx n'est qu'un pointeur vers un TImageList existant et à la conception ça marche bien puisque les deux sont créés sur la form et que l'on a que besoin de les lier.

Hors dans l'hypothèse d'une création dynamique, je suppose qu'il n'y a pas moyen d'assigner un TImageList mais juste de pointer dessus.

Je ne mets pas réponse accepté pour ton code car dans mon cas il ne s'applique pas cependant je vais m'en servir pour arriver à mes fins ! En revanche tes explications sont claires et m'ont permis de trouver une solution donc je mets réponses accepté pour ta deuxième réponse.

Mon problème est que ma procédure est stockée dans une unité toute simple sans form que j'appelle depuis plusieurs autres form via le uses. Donc si j'ai compris les deux solutions qu'ils me restent sont :
<li>De déclarer dans mon unité un TImageList global que je pourrais lier à la propriété Images par une simple affectation (qui initialisera le pointeur en question) depuis ma procédure</li><li>De créer mon propre composant ComboBoxEx en dérivant de TCustomComboBox pour arriver au résultat souhaité</li>Pourrais-tu avoir la gentillesse de me dire si je suis sur la bonne voie ?

Merci d'avance pour ton aide et encore merci pour l'aide que tu m'as déjà apporté.

Que la force soit avec vous !
Messages postés
129
Date d'inscription
mercredi 4 février 2004
Statut
Membre
Dernière intervention
25 juillet 2012
1
Merci encore foxi pour ton aide.

Donc mes hypothèses précédentes se vérifie à propos du TImageList.

Je te remercie pour ton dernier code, je vais m'en inspiré pour compléter ma procédure car je veux l'appeler avec uniquement le ComboBoxEx comme paramètre par référence.

Ton aide m'a été vraiment précieuse et tu m'a mis sur la bonne voie alors je t'en remercie.

A plus au détour d'un autre post ...

Que la force soit avec vous !
Messages postés
3793
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
6
@f0xi : tu écris dans ton premier commentaire que tu crées dynamiquement ton TImageList de la façon suivante :

fImageList := TImageList.Create(self);

Mais tu le libères dans le OnDestroy, alors que tu définis la fiche comme propriétaire de l'objet. N'est-ce pas superflu de libérer cet enfant alors que la fiche, à sa fermeture, devrait s'en occuper ? J'aimerais être fixé sur ce point car j'ai toujours des doutes sur la libération des objets enfants/parents.

Cordialement, Bacterius !
Messages postés
4200
Date d'inscription
samedi 16 octobre 2004
Statut
Modérateur
Dernière intervention
2 janvier 2019
27
non, on libere toutes les ressources créées manuellement de façon manuelle.

si je crée un objet, je libere ce dernier. je ne laisse pas un autre composant s'occuper de sa liberation, SAUF si ce composant saitle faire, peux le faire et le fait car il est prevus pour ça.
en aucuns cas je ne laisse le ramasse miette le faire, en aucuns cas je laisse les composant se drebrouiller entre eux

Messages postés
3793
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
6
D'accord.

Cordialement, Bacterius !
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
5
Petite précision sur la source de la violation d'accès :
    // Permet d'associer la liste d'images à la liste déroulante.
    cmbListeDeroulante.Images.Assign(ListeImages);

Comme l'a dit foxi, "cela est du au fait que les propriétés du style
Images [...] ne sont la que
pour stocker une instance existante d'un objet.", la propriété Images n'est qu'un lien vers ton instance. Il est donc nil à l'origine.
Le code qui tente donc d'être exécuté est :
nil.Assign(ListeImages); d'où l'access violation.