Problème affectation dynamique ImageList à un ComboBoxEx.Images

Résolu
informatixo Messages postés 129 Date d'inscription mercredi 4 février 2004 Statut Membre Dernière intervention 25 juillet 2012 - 16 juin 2009 à 17:31
Guillemouze Messages postés 991 Date d'inscription samedi 25 octobre 2003 Statut Membre Dernière intervention 29 août 2013 - 18 juin 2009 à 13:10
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

f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
16 juin 2009 à 18:20
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.

3
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
17 juin 2009 à 04:17
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;






3
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
16 juin 2009 à 18:05
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.

0
informatixo Messages postés 129 Date d'inscription mercredi 4 février 2004 Statut Membre Dernière intervention 25 juillet 2012 1
16 juin 2009 à 23:40
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 !
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
informatixo Messages postés 129 Date d'inscription mercredi 4 février 2004 Statut Membre Dernière intervention 25 juillet 2012 1
17 juin 2009 à 12:20
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 !
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
17 juin 2009 à 16:17
@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 !
0
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
17 juin 2009 à 22:40
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

0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
18 juin 2009 à 12:04
D'accord.

Cordialement, Bacterius !
0
Guillemouze Messages postés 991 Date d'inscription samedi 25 octobre 2003 Statut Membre Dernière intervention 29 août 2013 6
18 juin 2009 à 13:10
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.
0
Rejoignez-nous