Affichage des articles dans C++Builder

Dans un article précédent j’avais expliqué comment charger une image à partir des ressources en utilisant la VCL. Maintenant, voici l’équivalent mais cette fois-ci avec FireMonkey.

New FireMonkey HD Application

Une des première chose à connaître dans FireMonkey est les formats d’images supportés par la classe TBitmap.

Voici la liste des formats supportés sur toutes les plates-formes :

  • JPEG (.jpeg, .jpg)
  • TIFF (.tiff, .tif)
  • GIF
  • PNG
  • BMP

Voici la liste des formats supplémentaires avec Direct2D sur Windows :

  • JPEG XR (.hdp, .jxr, .wdp)
  • ICO

Voici la liste des formats supplémentaires avec GDI+ sur Windows :

  • WMF
  • ICO

Finalement, la liste des formats supplémentaires sur OS X :

  • JPEG 2000 (.jp2)
  • PSD
  • TGA
  • ICNS

Étant donné que TBitmap gère tous ces types d’images, cela simplifie beaucoup le code nécessaire pour charger une image des ressources. Avec ces trois lignes, vous allez pouvoir charger n’importe quelle image mentionnée plus haut.

    TResourceStream *Res = new TResourceStream((unsigned)HInstance, "PNG_LOGO", (System::WideChar *)RT_RCDATA);
    Image1->Bitmap->LoadFromStream(Res);
    delete Res;

Pour utiliser ce code il faut que toutes les images dans les ressources soient de type RCDATA. Faites attention dans l’IDE lorsque vous ajoutez une image Bitmap: le type par défaut est BITMAP, donc il faut le changer.

Bonne chance dans vos projets FireMonkey!

Depuis le début du mois de septembre, j’évalue la version d’essai de C++Builder XE2. Une des fonctionnalités que j’apprécie particulièrement est de pouvoir compresser et décompresser des fichiers zip. C’est la nouvelle classe TZipFile qui se chargera de ce travail. Il existe déjà plusieurs composants et bibliothèques pour faire cela, mais je trouve ça intéressant de pouvoir le faire nativement. Pour ma part, j’utilisais ZipArchive Library de Artpol Software.

RAD Studio XE2 Splash Screen

Maintenant, voyons comment utiliser cette classe. Tout d’abord, voici l’entête qu’il faudra ajouter à votre projet.

#include <System.Zip.hpp>

Voici un exemple très simple de code pour effectuer la compression d’un fichier.

    TZipFile *ZipFile = new TZipFile(); // Création de l'objet

    ZipFile->Open("c:\\fichier.zip", zmWrite); // Ouverture du fichier en mode écriture

    ZipFile->Add("c:\\test.xml", "test.xml"); // Ajout d'un fichier dans l'archive

    ZipFile->Close(); // Fermeture du fichier

    delete ZipFile; // Libération de la mémoire

Ce n’est pas plus compliqué que cela!

Le composant TPopupMenu possède peu d’évènements. Seulement OnChange et OnPopup sont disponibles. Dans certains cas, il pourrait être pratique de détecter la fermeture d’un menu contextuel. Malheureusement, cet évènement n’existe pas, mais il est par contre possible de le coder.

Lorsqu’un menu contextuel se ferme, l’évènement WM_MENUSELECT est envoyé avec certains paramètres. Le seul problème c’est que ce n’est pas la fenêtre qui reçoit l’évènement. C’est la classe TPopupList qui a cette responsabilité. Dans Menus.hpp on retrouve cette ligne de code:

extern PACKAGE TPopupList* PopupList;

L’objet PopupList est créé à l’initialisation, il va donc falloir capturer les messages qu’il reçoit pour appeler une méthode lors de la fermeture du menu.

Voici la liste des méthodes et attributs à ajouter a votre fichier .h:

private:	// User declarations
    void __fastcall PopupWndProc(Messages::TMessage &Message);

    WNDPROC OldPopupProc;
    TFNWndProc FPopupProcInst;
    TPopupMenu *FLastOpenPopupMenu;
protected:
    void __fastcall DoPopupExit(TPopupMenu *PopupMenu);

La première étape est de rediriger les messages vers la méthode PopupWndProc. Avant de faire cela on va d’abord enregistrer l’ancienne adresse de la procédure de fenêtre dans OldPopupProc. Voici le code à ajouter à votre constructeur:

    FPopupProcInst = MakeObjectInstance(PopupWndProc);
    OldPopupProc = (WNDPROC)SetWindowLongPtr(PopupList->Window, GWL_WNDPROC,
        (LONG_PTR)FPopupProcInst);

Dans le destructeur on va restaurer l’appel vers la méthode WndProc originale et libérer la mémoire allouée par la fonction MakeObjectInstance:

    if(FPopupProcInst != NULL)
    {
        SetWindowLong(PopupList->Window, GWL_WNDPROC, (LONG)OldPopupProc);
        FreeObjectInstance(FPopupProcInst);
    }

À la réception du message WM_MENUSELECT on regarde si MenuFlag est à 0xFFFF et Menu à NULL. Si c’est le cas, alors le menu est fermé et on appelle DoPopupExit. Cette méthode a pour paramètre un pointeur vers le TPopupMenu qui vient de se fermer. La variable FLastOpenPopupMenu contient cette information. On lui affecte une valeur à la réception du message WM_INITMENUPOPUP qui est appelé lorsqu’un TPopupMenu est sur le point d’être actif. À la place de ce message, l’évènement OnPopup aurait pu être utilisé, mais je l’ai fait de cette manière pour rendre le code plus général. Les lignes 25 et 26 servent à appeler la méthode WndProc originale.

void __fastcall TForm1::PopupWndProc(Messages::TMessage &Message)
{
    if(Message.Msg == WM_MENUSELECT)
    {
        TWMMenuSelect *MenuSelect = (TWMMenuSelect *)&Message;
        if(MenuSelect->MenuFlag == 0xFFFF && MenuSelect->Menu == NULL)
        {
            DoPopupExit(FLastOpenPopupMenu);
        }
    }
    else if(Message.Msg == WM_INITMENUPOPUP)
    {
        TWMInitMenuPopup *InitMenuPopup = (TWMInitMenuPopup *)&Message;
        const int ListCount = PopupList->Count;
        for(int i = 0; i < ListCount; ++i)
        {
            TPopupMenu *LPopupMenu = (TPopupMenu *)PopupList->Items[i];
            if(LPopupMenu->Handle == InitMenuPopup->MenuPopup)
            {
                FLastOpenPopupMenu = LPopupMenu;
                break;
            }
        }
    }
    Message.Result = CallWindowProc(OldPopupProc, PopupList->Window,
        Message.Msg, Message.WParam, Message.LParam);
}

La seule chose qui manque est de mettre votre code à l’intérieur de cette méthode.

void __fastcall TForm1::DoPopupExit(TPopupMenu *PopupMenu)
{
}

Avec le code plus haut, il serait très facile de faire un composant que l’on pourrait utiliser dans plusieurs projets qui nécessitent d’être notifiés lors de la fermeture d’un menu contextuel. L’ajout d’une propriété OnPopupExit serait simple à implémenter.

J’espère que cet article vous sera pratique.

Dans cet article je vous expliquerai comment changer le texte contenu dans les ressources de C++Builder pendant l’exécution de votre programme. Si vous concevez une application multilingue, cette technique sera pratique.

Dans votre fichier d’en-tête, ajoutez la déclaration suivante:

    void __fastcall HookResourceString(TResStringRec& ResStringRec, Char* StrID);

Voici la méthode à ajouter à votre code:

void __fastcall TForm1::HookResourceString(TResStringRec& ResStringRec, Char* StrID)
{
    DWORD OldProtect;

    VirtualProtect(&ResStringRec, sizeof(ResStringRec), PAGE_EXECUTE_READWRITE, &OldProtect);
#if __BORLANDC__ >= 0x630
    // Pour C++Builder XE et plus
    ResStringRec.Identifier = Integer(StrID);
#else
    ResStringRec.ident = Integer(StrID);
#endif
    VirtualProtect(&ResStringRec, sizeof(ResStringRec), OldProtect, &OldProtect);
}

Le code est assez simple: on change la protection sur ResStringRec pour que l’on puisse y mettre un pointeur vers la chaîne de caractères StrID. Lorsque la modification est terminée, on remet la protection précédente.

Il est à noter que la structure TResStringRec n’est pas la même sur C++Builder 2010 et C++Builder XE. Étant donné que je n’ai pas d’autre version à ma disposition, je n’ai pas pu les vérifier.

Sur C++Builder 2010:

struct PACKAGE TResStringRec
{
    long *module;
    long ident;
};

Sur C++Builder XE:

struct DECLSPEC_DRECORD TResStringRec
{
public:
    unsigned *Module;
    int Identifier;
};

Voici un exemple d’utilisation avec la fonction MessageDlg:

HookResourceString(_SMsgDlgConfirm, L"Mon titre");
HookResourceString(_SMsgDlgYes, L"Absolument");
HookResourceString(_SMsgDlgNo, L"Pas du tout");
HookResourceString(_SMsgDlgCancel, L"Laisse faire");

MessageDlg("Veux-tu faire quelque chose?", mtConfirmation,
        TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0, mbCancel);

Le premier paramètre de la méthode est le nom de la ressource qui correspond aux noms que l’on retrouve dans le fichier Consts.hpp. Donc, il est important d’inclure ce fichier d’en-tête dans votre code. Si vous êtes intéressés à connaître le texte original, alors je vous conseille de jeter un coup d’œil au fichier Consts.pas.

Je vous laisse avec une capture d’écran du résultat.
Boîte de dialogue personnalisée

Cet article montrera comment changer le canal alpha d’une image de type PNG lors de l’exécution d’un programme codé avec C++Builder.

En premier lieu, il faut créer un nouveau projet auquel on ajoute un composant TTrackBar. C’est avec ce contrôle que l’on changera la transparence de l’image. Il sera gradué de 0 à 100, car il représente un pourcentage d’opacité.

Transparence d'un PNG

Dans le constructeur de la Form, on ajoute le code suivant:

    OriginalImage = new TPngImage();
    OriginalImage->LoadFromFile("../tux.png");

    TrackBar1->Align = alBottom;// Le composant est aligné au bas de la fenêtre
    TrackBar1->Min = 0;         // Le minimum est 0%
    TrackBar1->Max = 100;       // Le maximum est 100%
    TrackBar1->Position = 0;    // Position de départ à 0%
    TrackBar1->Frequency = 5;   // Marque de graduation à tout les 5 incréments
    TrackBar1->PageSize = 5;    // Déplacement par bonds de 5
    TrackBar1->ShowSelRange = false;// La zone sélectionnée n'est pas affichée

    DoubleBuffered = true; // Évite le scintillement lors d'un redimensionnement

Les lignes 1 et 2 servent à créer un objet TPngImage qui servira à garder en mémoire l’image originale. Le but de OriginalImage est de ne pas avoir à recharger l’image à chaque fois que le TTrackBar se déplacera. Dans ce code, on charge l’image directement du disque dur avec LoadFromFile, mais on aurait pu la charger à partir des ressources du programme.

Dans l’évènement OnDestroy de la fenêtre, on ajoute cette ligne pour libérer la mémoire allouée par OriginalImage:

    delete OriginalImage;

Dans votre fichier d’en-tête, ajoutez le code suivant dans la section private:

    TPngImage *OriginalImage;
    void __fastcall SetTransparency(TPngImage *PngImage, float TransPct);

Maintenant, voici la pièce de résistance, la méthode qui change l’alpha d’un PNG:

void __fastcall TForm1::SetTransparency(TPngImage *PngImage, float TransPct)
{
    int x, y;
    Byte *DestAlpha, Alpha;
    float Pct = TransPct / 100.0f;
    for(y = 0; y < PngImage->Height; y++)
    {
        DestAlpha = (Byte *)PngImage->AlphaScanline[y];
        for(x = 0; x < PngImage->Width; x++)
        {
            Alpha = RoundTo(Pct * DestAlpha[x], 0);
            if((DestAlpha[x] - Alpha) < 0)
                DestAlpha[x] = 0;
            else
                DestAlpha[x] -= Alpha;
        }
    }
}

Le premier paramètre d’entrée est un objet de type TPngImage qui sera modifié. Le deuxième paramètre est une valeur en pourcentage, donc on attend une valeur entre 0 et 100. 0 rendra l’image complètement opaque et celle-ci ne sera donc pas modifiée tandis qu’à 100, elle est complètement transparente.

La méthode AlphaScanline lit une ligne de numérisation contenant le canal alpha et renvoie un pointeur sur la mémoire contenant cette ligne.

Puisque la fonction RoundTo est utilisée pour arrondir, il faut ajouter cet entête:

#include <Math.hpp>

À cause de problème de scintillement avec un TImage, l’image sera dessinée directement sur le Canvas de la fenêtre. Voici le code à ajouter à l’évènement OnChange du TTrackBar:

    TPngImage *ImageToProcess = new TPngImage();
    Graphics::TBitmap *TempImage = new Graphics::TBitmap();

    ImageToProcess->Assign(OriginalImage);
    SetTransparency(ImageToProcess, TrackBar1->Position);

    TempImage->Canvas->Brush->Color = Form1->Color;
    TempImage->SetSize(OriginalImage->Width, OriginalImage->Height);
    TempImage->Canvas->Draw(0, 0, ImageToProcess);

    Canvas->Draw(Width/2 - TempImage->Width/2,
        Height/2 - TempImage->Height/2 - TrackBar1->Height, TempImage);

    delete ImageToProcess;
    delete TempImage;

Deux images temporaires seront créées. ImageToProcess est de type TPngImage et on lui assigne l’image originale et on la modifie ensuite en appelant SetTransparency. TempImage est un TBitmap qui sert à la double mise en mémoire tampon. On y dessine l’arrière plan de la couleur de la fenêtre et ensuite on y dessine ImageToProcess. C’est cette image que l’on dessine sur le Canvas au centre de la fenêtre avec la méthode Draw.

Dans l’évènement OnPaint de la fenêtre, on ajoute ce code pour que l’image soit toujours présente:

    TrackBar1Change(NULL);

Dans l’évènement OnResize de la fenêtre, on doit redessiner l’image pour qu’elle reste centrée:

    Invalidate();

Voilà ce qui met fin à l’article. Vous pouvez maintenant vous amuser avec la barre graduée afin de changer l’opacité de l’image.

Je vous laisse avec une ligne de code qui pourrait vous être pratique si vous désirez seulement changer à une reprise le canal alpha d’un PNG dans un TImage:

SetTransparency((TPngImage *)Image1->Picture->Graphic, 75);

Dans cet article, je vous présente un petit truc pour mieux gérer les exceptions du langage dans C++Builder.

Depuis la version 2007, il est possible d’ajouter un bouton à la barre d’outils qui se nomme Notify on Language Exceptions. Ce bouton permet d’activer ou de désactiver l’option du même nom que l’on trouve dans Tools / Options… / Debugger Options / Embarcadero Debuggers / Language Exceptions.

Comme vous le voyez, on dirait que cette option est quasiment cachée, donc le fait d’avoir un bouton accessible d’un seul clique de souris peut faire sauver beaucoup de temps lors du débogage d’un programme.

Pour ajouter le bouton dans la barre d’outils, il suffit d’aller dans View / Toolbars / Customize…. Ensuite, il faut aller dans l’onglet Commands et choisir la catégorie Run. Maintenant, il faut faire un glisser-déposer de l’item Notify on Language Exceptions dans la barre d’outils de C++Builder. Voici une capture d’écran pour mieux illustrer mes propos:

Notify on Language Exceptions

Une fois terminé, vous pouvez fermer la fenêtre Customize.

Dans le but de tester le bon fonctionnement du bouton, nous allons créer un nouveau projet dans lequel le code suivant sera inséré dans le constructeur de la Form:

    try
    {
        int a, b = 0;
        a = 2 / b;
    }
    catch(...)
    {
    }

Si le bouton est enfoncé lorsqu’on appuie sur Run en mode Debug, alors le message suivant apparaîtra: « Project Project1.exe raised exception class EDivByZero with message 'Division by zero' ».

C’est normal car il y a effectivement une division par zéro. Si l’on désactive l’option et que l’on relance l’application, le message disparaît comme par magie.

Voilà, c’est tout!

Cet article vous expliquera comment compiler la bibliothèque MikMod sous forme de bibliothèque statique (.lib) que vous pourrez intégrer directement dans un programme fait avec C++Builder. Tout d’abord, MikMod est une bibliothèque de programmes codée en langage C qui sert à faire jouer des fichiers module. Cela inclut les fichiers MOD (15 et 31 instruments), S3M (ScreamTracker 3), IT (Impulse Tracker) et XM (FastTracker 2).

La première étape consiste à aller télécharger le code source de la bibliothèque logicielle sur le site Web officiel. Il se trouve dans la section Files de la page. C’est le fichier libmikmod-3.2.0-beta2.zip qui sera utilisé. Il est a noter qu’une version plus récente du code est disponible par CVS sur le site Web de SourceForge.

Une fois le téléchargement terminé, il faut extraire les données dans un dossier que l’on nommera MikMod. Dans C++Builder nous allons créer le nouveau projet en allant dans File / New / Other…. La touche de raccourcis Ctrl+N aurait pu être utilisée. Puis, on sélectionne Static Library et on clique sur OK. Maintenant, nous pouvons sauvegarder notre projet. Pour cela, on va dans File et on sélectionne Save Project As… On choisit comme nom de projet MikMod pour que le fichier lib porte ce nom. L’emplacement du fichier cbproj doit être dans le dossier où les données on été extraites.

Il faut ajouter les fichiers .c au projet.

  • Pour le dossier drivers: "drv_stdout.c" "drv_wav.c" "drv_win.c" "drv_ds.c" "drv_nos.c" "drv_raw.c"
  • Pour le dossier loaders, on sélectionne tous les fichiers.
  • Pour le dossier mmio, on sélectionne tous les fichiers.
  • Pour le dossier playercode, on sélectionne tous les fichiers.

Étant donné que seulement les fichiers .c ont été ajoutés au projet, il manque les chemins d’accès pour certains fichiers .h. Il faut donc aller dans Project / Options… ou Alt+F7. Dans Include path on ajoute: win32\;include\;
Ensuite, dans Conditional defines, on ajoute ceci: WIN32;DRV_WIN;HAVE_FCNTL_H;HAVE_MALLOC_H;HAVE_LIMITS_H;__STDC__=1
Par la suite, on quitte la fenêtre d’option, on choisit la configuration Release et on compile le code. Il se peut qu’il y ait plusieurs avertissements lors de la compilation, mais cela n’empêchera pas le code de fonctionner correctement. Le fichier MikMod.lib devrait être créé dans le dossier Release.

Afin de valider les étapes précédentes, il va falloir créer un nouveau projet indépendant pour être certain que la bibliothèque fonctionnera peu importe l’emplacement du projet. Dans cette nouvelle application VCL, on ajoute un contrôle TTimer et on met la propriété Interval à 100ms. Par la suite, on ajoute au projet les fichiers mikmod_build.h et MikMod.lib.

Dans le constructeur de la Form, on ajoute le code suivant:

	// Enregistrement de la bibliothèque
	MikMod_RegisterDriver(&drv_win);
	MikMod_RegisterAllLoaders();

	// Initialise la bibliothèque
    if(MikMod_Init(""))
    {
        throw(Exception("L'initialisation de MikMod a échouée."));
    }
    else
    {
        // Charge le module
        AnsiString FileName = "..\\music.s3m";
        module = Player_Load(FileName.c_str(), 32, 0);
        if(module)
        {
            module->wrap = true;    // Recommence lorsqu'il finit
            Player_Start(module);   // Démarre la musique
        }
        else
        {
            throw(Exception("Le fichier n'a pu être chargé."));
        }
    }

C’est le pilote pour l’API multimédia de Windows qui est chargé à la ligne 2. On aurait aussi pu utiliser drv_ds pour que le son soit pris en charge par DirectSound. Par contre, si l’on décide de faire ce choix, il va falloir ajouter DRV_DS à la liste des Conditional defines. En plus, il est nécessaire d’ajouter le fichier dsound.lib au projet pour accéder aux fonctions l’API de DirectSound. Si vous êtes intéressé à générer un fichier wav plutôt que de lire un fichier audio, alors c’est le pilote drv_wav qu’il faut utiliser.

À la ligne 13, on trouve la variable FileName, on doit lui affecter le chemin du fichier module que l’on veut faire jouer.

Dans le destructeur, on arrête la musique et on libère la mémoire avec le code suivant:

    Timer1->Enabled = false;
    if(module)
    {
        Player_Stop();
        Player_Free(module);
        MikMod_Exit();
    }

Dans l’évènement OnTimer du TTimer, on ajoute ceci:

    if(module && Player_Active())
    {
        MikMod_Update();
    }

La fonction MikMod_Update doit être appelée sur une base régulière, car elle sert à remplir la mémoire tampon de la sortie audio.

Si vous êtes intéressé à aller un peu plus loin, vous pouvez ajouter un contrôle TTrackBar au projet auquel les propriétés Max et Position devront être initialisées à 128. Dans l’évènement OnChange, ajoutez le code suivant:

    Player_SetVolume(TrackBar1->Position);

Un contrôle de volume est maintenant disponible dans votre logiciel.

Si vous êtes à la recherche de pièces musicales, je vous conseille d’aller faire un tour sur le site Web The Mod Archive.

À présent, vous avez tout ce qu’il faut pour inclure de la musique à vos applications.

Il existe quelques dossiers spéciaux dans Windows XP qui possèdent un watermark, aussi appelé filigrane en français, en bas à droite. Voici l’exemple du dossier Ma musique:
Listview with a watermark
Le but de cet article est de vous aider à faire la même chose dans un TListview sous C++Builder. Pour que l’effet soit réussi, je vous suggère d’utiliser un fichier de type PNG ou icône car ces deux formats permettent l’utilisation du canal alpha. Si vous décidez d’utiliser le format PNG, je vous conseille de jeter un coup d’œil à mon article qui explique comment charger une image dans les ressources. Parce qu’il est plus simple de faire un code qui charge l’icône de l’application, c’est ce format que nous utiliserons.

La première étape est bien sûr de créer un nouveau projet auquel on ajoute un contrôle TListView. Vous pouvez y ajouter quelques TListColumns et TListItems pour faire un peu plus réel.

Étant donné que j’ai décidé d’utiliser un auto_ptr, il faut inclure l’entête suivant dans votre fichier cpp.

#include <memory>

Dans le constructeur de la Form, ajoutez le code suivant:

    static std::auto_ptr<Graphics::TBitmap> Bitmap(new Graphics::TBitmap);
    Bitmap->Canvas->Brush->Color = ListView1->Color;
    Bitmap->SetSize(32, 32); // Le casque romain est seulement en 32x32 pixels
    Bitmap->Canvas->Draw(0, 0, Application->Icon);

    LVBKIMAGE lv;
    lv.ulFlags = LVBKIF_TYPE_WATERMARK;
    lv.hbm = Bitmap->Handle;
    ListView_SetBkImage(ListView1->Handle, &lv);
    ListView_SetTextBkColor(ListView1->Handle, CLR_NONE);

La première ligne sert à créer une image de type Bitmap. Ce format est nécessaire car la fonction qui va être utilisée plus tard nécessite comme paramètre un HBITMAP. Le seconde ligne va servir à définir la couleur de fond de l’image. Si on effectue cette tâche avant d’établir la grandeur de l’image, l’image sera alors automatiquement de cette couleur. Cela évite d’avoir à dessiner un rectangle de la grandeur du Bitmap par la suite. À la troisième ligne, on utilise une grandeur de 32 par 32 pixels, car c’est la grandeur de l’icône de l’application. L’image par défaut dans C++Builder 2010 est un casque romain. La ligne d’après est utilisée pour dessiner l’icône sur le Bitmap. Maintenant, l’image est prête à être utilisée.

Les lignes 6 à 8 servent à remplir une structure de type LVBKIMAGE qui sera utilisée par la fonction ListView_SetBkImage à la ligne 9.

On pourrait penser que l’on a terminé, eh bien non. Il reste un petit tour de passe nécessaire pour voir notre image dans la listview.

Dans votre fichier d’en-tête, ajoutez les déclarations suivantes dans la section private:

    void __fastcall ListViewWndProc(Messages::TMessage &Message);
    Classes::TWndMethod OldListViewProc;

Ensuite, on retourne dans le constructeur de la Form pour ajouter ce code qui sert à rediriger les appels de Windows à la listview vers la méthode ListViewWndProc:

OldListViewProc = ListView1->WindowProc;
ListView1->WindowProc = ListViewWndProc;

Il faut par la suite ajouter cette méthode:

void __fastcall TForm1::ListViewWndProc(Messages::TMessage &Message)
{
    if(Message.Msg == WM_ERASEBKGND)
    {
        ListView1->DefaultHandler(&Message);
        return;
    }
    OldListViewProc(Message);
}

Si ce code n’est pas utilisé, l’événement WMEraseBkgnd de la classe TWinControl, l’ancêtre de TListView, sera appelé. Cette méthode empêche le message WM_ERASEBKGND de se rendre à la procédure par défaut. C’est pourquoi la méthode DefaultHandler de la listview est appelée et que l’on interdit un appel à OldListViewProc si on reçoit ce message.

À présent, vous avez tout le code nécessaire pour faire votre propre watermark dans un contrôle de type listview.

Depuis C++Builder 2009, le contrôle TEdit possède une nouvelle propriété qui se nomme TextHint. Étant curieux de nature, je décide de l’essayer. Première constatation: il s’agit d’une zone de texte. Donc, je me dis que si je tape quelque chose à l’intérieur et que je compile le code, mon message devrait apparaître quelque part. C’est ce que je m’empresse de faire. Malheureusement, j’ai beau essayer de faire plusieurs actions sur mon contrôle, je ne vois pas mon texte. Alors, je me dis que j’ai sans doute mal compris à quoi peut bien servir cette propriété. Ainsi, je décide d’aller faire un tour dans l’aide. Sur la page de la propriété du TEdit, il n’y a aucune information. Alors, je vais donc sur la page de son parent: le classe TCustomEdit. Voici la description que j’y trouve:

Un conseil ou un message à afficher quand la propriété Text est vide.

Utilisez TextHint pour informer l’utilisateur du type d’entrée attendu dans le champ texte. Les thèmes d’exécution doivent être activés.

Donc, si je me fie à cette information, il devrait y avoir un texte dans mon contrôle car il est vide et les thèmes sont activés sur mon PC. Je décide d’employer les grands moyens: j’ouvre le fichier StdCtrls.pas et je regarde le code de la méthode SetTextHint. La fonction DoSetTextHint est appelée alors je me dirige vers ce code pour me rendre compte que le message EM_SETCUEBANNER est envoyé à Windows. Sur la page MSDN du message, je trouve un texte qui explique que ça ne fonctionne pas si sous Windows XP les langues d’Extrême-Orient sont installées. Bien sûr, sur mon ordinateur, j’ai coché la petite case car j’ai besoin de ces caractères pour tester mes applications multi-langues. Pour ceux qui se demandent où est cette configuration, elle est dans les Options régionales et linguistiques du Panneau de configuration sous l’onglet Langues.

Voici le résultat lorsque je teste sur un système qui remplit toutes les exigences, même celles non documentées dans l’aide officielle:
La propriété TextHint d'un TEdit

Le problème semble être résolu depuis Windows Vista.

J’espère que vous lirez cet article avant de perdre du temps à essayer de comprendre pourquoi cela ne fonctionne pas sur votre ordinateur, ou encore pire sur celui d’un client :) .

Dans C++Builder il est très facile de charger un icône ou même une image Bitmap situé dans les ressources d’une application. La méthode LoadFromResourceName sera alors utilisée.
Voici un exemple pour un fichier Bitmap que l’on veut mettre dans un TImage:

Image1->Picture->Bitmap->LoadFromResourceName((unsigned)HInstance, "BMP_BACKGROUND");

Maintenant, voici un exemple pour un icône que l’on veut utiliser pour un TForm:

Form1->Icon->LoadFromResourceName((unsigned)HInstance, "ICO_DELETE");

Une seule ligne de code est nécessaire pour ces deux types. Par contre, les autres types d’image utilisent le type RC DATA, donc c’est un peu plus compliqué.

Voyons tout d’abord comment ajouter un fichier de type image dans un projet. C’est assez simple car les dernières versions de l’IDE mettent à notre disposition une fenêtre pour exécuter ces tâches. Le fichier .rc sera créé automatiquement par l’interface et il n’aura pas à être modifié manuellement. Pour y accéder, il faut aller dans le menu Project et après dans Resources. Ensuite, il suffit de cliquer le bouton Add pour ajouter un fichier. Par la suite, il est préférable de lui donner un nom approprié.
Ressources sous C++Builder
Pour utiliser la classe TPngImage il faut inclure l’en-tête suivant:

#include <pngimage.hpp>

Voici maintenant le code que j’utilise pour charger l’image PNG et la mettre dans un TImage:

    TPngImage *PngImage = new TPngImage;
    PngImage->LoadFromResourceName((unsigned)HInstance, "PNG_LOGO");
    Image1->Picture->Bitmap->Assign(PngImage);
    delete PngImage;

Le code est assez simple, mais il est quand même un peu plus long que les deux précédents. On doit créer l’objet PngImage pour y charger temporairement la ressource. Pour assigner le PNG à l’objet Image1, on utilise Assign.

Une même méthode similaire pourrait être appliquée avec les classes TGIFImage et TJPEGImage. Voici un exemple pour une image de type JPEG:

    TJPEGImage *JPEGImage = new TJPEGImage;
    TResourceStream *Res = new TResourceStream((unsigned)HInstance, "JPEG_PHOTO", (System::WideChar *)RT_RCDATA);
    JPEGImage->LoadFromStream(Res);
    Image1->Picture->Bitmap->Assign(JPEGImage);
    delete Res;
    delete JPEGImage;

La classe TResourceStream permet de lire les ressources d’une application et l’enregistrer dans un flux mémoire. Ensuite, on charge ce flux dans l’objet JPEGImage à l’aide de la méthode LoadFromStream.

Je crois maintenant vous entendre penser: « Quoi, il n’existe pas de façon plus générale de charger n’importe quel type d’image dans un TImage? ». La réponse est oui, bien sûr, si vous possédez C++Builder 2010. La classe TWICImage peut être utilisée à cet fin. Il s’agit d’une encapsulation de Windows Imaging Component qui permet le chargement d’une grande variété de formats d’images. Voici l’enum des formats supportés:

enum TWICImageFormat { wifBmp, wifPng, wifJpeg, wifGif, wifTiff, wifWMPhoto, wifOther };

TWICImage est défini dans Graphics.hpp et on l’utilise comme suit:

    TWICImage *WicImg = new TWICImage();
    TResourceStream *Res = new TResourceStream((unsigned)HInstance, "PNG_LOGO", (System::WideChar *)RT_RCDATA);
    WicImg->LoadFromStream(Res);
    Image1->Picture->Graphic = WicImg;
    delete Res;
    delete WicImg;

TWICImage est un descendant de TGraphic tout comme TJPEGImage et TGIFImage. C’est pourquoi le code est pratiquement le même et que je ne le commenterai pas. Il est à noter cependant que TWICImage est disponible sur Windows XP SP3 ou une version supérieure.

À partir de maintenant, vous n’aurez plus d’excuse à utiliser des Bitmaps en ressources. Il est désormais plus simple que jamais d’utiliser n’importe quel autre format. Encore une fois, j’espère que cela vous sera utile.