3. Přidání grafiky k ovladačům
  1. Okno seznamu a kombinovaný ovladač mají styly nazvané Owner draw, což znamená, že místo standardní metody zobrazování textu prvků v ovladači je použito vlastní zobrazování prvků. Časté použití ovladače s vlastním (uživatelským) zobrazováním, je v případě použití grafiky místo textu nebo společně s textem. Všechny ovladače s vlastním zobrazováním obsahují seznam prvků. Implicitně tyto seznamy jsou seznamy řetězců, které se zobrazují jako text. Ke každému prvku v seznamu můžeme přiřadit objekt, čímž si usnadníme určování, který objekt kreslit. Obecně při používání ovladačů s vlastním zobrazováním provedeme tyto kroky: nastavíme styl vlastního zobrazování, přidáme k seznamu řetězců grafické objekty a zobrazíme prvky vlastním zobrazením.

  2. Komponenty ListBox a ComboBox mají vlastnost Style. Tato vlastnost určuje, zda ovladač používá implicitní nebo vlastní zobrazování. Komponenty mřížek mají vlastnost DefaultDrawing k povolení nebo zakázání implicitního zobrazování. Komponenty ListBox a ComboBox mají styly vlastního zobrazování nazvané fixed a variable, pro mřížky je vždy používán styl fixed (i když jednotlivé řádky a sloupce mohou mít různé rozměry, je nutno určit velikost každé buňky před zobrazením). Význam těchto stylů je popsán v následující tabulce:
     
    Styl vlastního zobrazování Význam
    Fixed Všechny prvky mají stejnou výšku určenou vlastností ItemHeight.
    Variable Výšky jednotlivých prvků se mohou lišit a jsou určeny za běhu aplikace.

    Každý seznam řetězců má možnost, mimo svého seznamu řetězců, obsahovat i seznam objektů. Např. aplikace správce souborů může obsahovat společně s písmeny jednotek i bitové mapy indikující typ jednotek. K tomu je zapotřebí přidat k aplikaci požadované obrázky a odkazy na ně vložit do seznamu řetězců.
    Ovladač obrázku je komponenta obsahující obrázek (např. bitovou mapu). Ovladač obrázku můžeme použít k zobrazení obrázku na formuláři. Můžeme jej také použít k držení skrytých obrázků, které používáme v naší aplikaci. Např. můžeme uložit bitové mapy pro ovladače vlastního zobrazování ve skrytých ovladačích obrázků. To provedeme takto: Přidáme komponenty Image na formulář, nastavíme jejich vlastnosti Name, nastavíme jejich vlastnosti Visible na false a nastavíme jejich vlastnosti Picture na požadovanou bitovou mapu pomocí Editoru obrázků z Inspektora objektů.
    Když již máme obrázky v aplikaci, můžeme je přiřadit k řetězcům v seznamu řetězců (můžeme přidávat objekty společně s řetězci nebo je přiřadit k již existujícím řetězcům). Pokud jsou potřebná data dostupná, pak je vhodné přidávat objekty a řetězce najednou. Následující příklad ukazuje jak můžeme přidávat obrázky k seznamu řetězců. Je to část aplikace, ve které společně s písmenem pro každou existující jednotku, přidáme bitovou mapu indikující typ jednotky (tato obsluha bude použita později při vývoji správce souborů).
    void __fastcall TFMForm::FormCreate(TObject *Sender)
    {
      int AddedIndex;
      char DriveName[4] = "A:\";
      for (char Drive = 'A'; Drive <= 'Z'; Drive++)
      {
        DriveName[0] = Drive;
        switch (GetDriveType(DriveName))
        {
          case DRIVE_REMOVABLE:       // přidání prvku seznamu
            DriveName[1] = '\0';      // bere pouze písmeno jednotky
            AddedIndex = DriveList->Items->AddObject(DriveName,
               Floppy->Picture->Graphic);
            DriveName[1] = ':'        // převádí zpět na tvar A:\
            break;
          case DRIVE_FIXED:          // přidání prvku seznamu
            DriveName[1] = '\0';
            AddedIndex = DriveList->Items->AddObject(DriveName,
               Fixed->Picture->Graphic);
            DriveName[1] = ':'
            break;
          case DRIVE_REMOTE:        // přidání prvku seznamu
            DriveName[1] = '\0';
            AddedIndex = DriveList->Items->AddObject(DriveName,
               Network->Picture->Graphic);
            DriveName[1] = ':'
            break;
        }
        if ((int)(Drive - 'A') == getdisk()) // aktuální jednotka?
          DriveList->ItemIndex = AddedIndex; // pak vybere prvek seznamu
      }
    }
    Když nastavíme styl vlastního zobrazování, pak již Windows nezobrazuje ovladač na obrazovku. Pro každý viditelný prvek v ovladači začne generovat události. Naše aplikace tyto události musí využít pro zobrazení prvků.
    Dříve než aplikace může začít se zobrazováním prvku u stylu Variable, Windows generuje událost OnMeasureItem. Tato událost říká aplikaci, kde prvek bude na ovladači zobrazen. Windows určuje velikost prvku (velikost potřebnou k zobrazení textu prvku současným písmem). Naše aplikace může událost zpracovat a změnit volbu Windows. Např. chceme-li text prvku nahradit obrázkem, pak zadáme velikost obrázku. Ke změně velikosti prvku vytvoříme obsluhu události OnMeasureItem (komponenty mřížky tuto událost nemají). Tato obsluha události má dva důležité parametry: index prvku a velikost prvku. Velikost je předávána odkazem a aplikace ji může zmenšit nebo zvětšit. Pozice následujícího prvku závisí na velikosti předchozích prvků. Např. když v okně seznamu, aplikace nastaví výšku prvního prvku na 5 bodů, pak druhý prvek začne šestým bodem od horního okraje ovladače, atd. V komponentách ComboBox a ListBox může aplikace ovlivňovat pouze výšku prvku. Šířka prvku je vždy šířka ovladače. U mřížek nelze měnit velikost buněk. Velikost každého řádku a sloupce musíme nastavit před zobrazením pomocí vlastností ColWidths a RowHeights.
    Následující kód připojený k obsluze události OnMeasureItem okna seznamu určuje výšku každého prvku seznamu podle své připojené bitové mapy:
    void __fastcall TForm1::ListBox1MeasureItem(TWinControl *Control,
                    int Index, int &Height)   // výška je předána odkazem
    {
      int BitmapHeight=((TBitmap*)ListBox1->Items->Objects[Index])->Height
                       + 2;
      // uděláme dostatečné místo pro bitovou mapu (+ 2)
      if (BitmapHeight > Height)
        Height = BitmapHeight;
    }
    V předchozím kódu musíme provést přetypování vlastnosti Objects seznamu řetězců. Tato vlastnost je typu TObject a může obsahovat odkaz na libovolný typ objektu. Pokud získáváme objekt z pole, musíme provést přetypování na aktuální typ prvku.
    Když aplikace požaduje zobrazení nebo překreslení ovladače, pak Windows generuje událost OnDrawItem (OnDrawCell) pro každý viditelný prvek v ovladači. Tato událost obsahuje parametry určující index zobrazovaného prvku, obdélník, ve kterém prvek bude zobrazen a obvykle nějaké informace o stavu prvku (např. zda prvek má zaostření). Aplikace zpracuje každou událost zobrazením příslušného prvku v daném obdélníku. Např. následující kód ukazuje jak zobrazit prvky v okně seznamu, který má ke každému řetězci přiřazenou bitovou mapu:
    void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control,
        int Index, TRect &Rect, TOwnerDrawState State)
      TBitmap *Bitmap = (TBitmap *)ListBox1->Items->Objects[Index];
      ListBox1->Canvas->Draw(Rect.Left, Rect.Top + 2, Bitmap);
      ListBox1->Canvas->TextOut(Rect.Left + Bitmap->Width + 2, Rect.Top + 2,
        ListBox1->Items->Strings[Index]);
    }

  3. Dále si ukážeme aplikaci zobrazující seznam písem dostupných v systému (jméno písma bude v seznamu zobrazeno pomocí tohoto písma; použijeme seznam s uživatelským - vlastním zobrazováním prvků). Začneme novou aplikaci. Na formulář umístíme k hornímu okraji komponentu Label s textem Písma systému:, většinu plochy formuláře bude zabírat komponenta ListBox a ve spodní části formuláře bude další komponenta Label (se jménem vybraného písma). U komponenty ListBox nastavíme vlastnost Style na lbOwnerDrawVariable a vytvoříme obsluhy zobrazující seznam. Obsluha události OnMeasureItem (událost zjišťující výšku prvku) bude vypadat takto:

  4. void __fastcall TForm1::ListBox1MeasureItem(TWinControl *Control,
          int Index, int &Height)
    {
      ListBox1->Canvas->Font->Name = ListBox1->Items->Strings[Index];
      ListBox1->Canvas->Font->Size = 0;
      Height = ListBox1->Canvas->TextHeight("Wg") +2;
    }
    Obsluha OnDrawItem (zobrazení prvku seznamu) vypadá takto:
    void __fastcall TForm1::DrawItem(TWinControl *Control,
          int Index, TRect &Rect, TOwnerDrawState State)
    {
      ListBox1->Canvas->FillRect(Rect);
      ListBox1->Canvas->Font->Name =ListBox1->Items->Strings[Index];
      ListBox1->Canvas->Font->Size = 0;
      ListBox1->Canvas->TextOut(Rect.Left+1, Rect.Top+1,
                                ListBox1->Items->Strings[Index]);
    }
    Obsluha kliknutí na prvku seznamu pouze zobrazí jméno vybraného prvku ve spodní komponentě Label:
    Label2->Caption = ListBox1->Items->Strings[ListBox1->ItemIndex];
    Zbývá ještě vytvořit obsluhu OnCreate formuláře. Je tvořena příkazem:
    ListBox1->Items = Screen->Fonts;
    Tím je aplikace hotova. Můžeme ji vyzkoušet. 
  5. V této kapitole se ještě seznámíme s používáním další ze základních komponent a to s komponentou ScrollBar. Přímé použití této komponenty je vzácné. Typickým příkladem je umožnění toho, aby si uživatel mohl vybrat celočíselnou hodnotu z určitého rozsahu. Začneme s vývojem nové aplikace. Na formulář umístíme tři komponenty ScrollBar a vlevo od každé z nich komponentuLabel. Každý posuvník se vztahuje k jedné ze tří základních barev a budeme s nimi určovat složky barvy formuláře. Posuvníky mají mnoho zvláštních vlastností. Min a Max používáme k určení rozsahu možných hodnot, Position obsahuje současnou pozici, vlastnosti LargeChange a SmallChange určují kroky, o které se změní hodnota posuvníku. V našem příkladě budou všechny posuvníky mít možné hodnoty od 0 do 255, počáteční hodnotu 192 a kroky budou 25 a 1. Obsluha události OnScroll posuvníku změní hodnotu Caption sousední komponenty Label a barvu formuláře. Např. pro první posuvník bude tvořena příkazy:

  6. Label1->Caption = "Červená: " + IntToStr(ScrollPos);
    Color=RGB(ScrollBar1->Position,ScrollBar2->Position,ScrollBar3->Position);
    Další posuvníky budou pro složky zelená a modrá. Jejich obsluhy budou podobné. Vytvořte je sami. Funkce RGB vezme tři hodnoty složek a vytvoří hodnotu s kódem výsledné barvy. Povšimněte si, že obsluha události OnScroll má tři parametry: Sender, ScrollCode (druh události) a ScrollPos (poslední pozici posuvníku). Druh události může být použit pro velice přesné rozlišení akcí uživatele. Jeho hodnota indikuje, zda uživatel posouvá ukazatel (scTrack, scPosition a scEndScroll), zda kliknul na šipky nebo na lištu v jednom ze dvou možných směrů (scLineUp, scLineDown, scPageUp a scPageDown) a zda se pokusil o posun mimo rozsah (scTop a scBottom).
  7. Vytvořte aplikaci, kde na formulář umístíte posuvník a komponentu Label s nějakým textem. Pomocí posuvníku měňte velikost písma komponenty Label (od 8 do 72).
  8. Tažení může usnadnit uživateli manipulaci s objekty na formuláři. Uživatel může táhnout celé komponenty nebo táhnout prvky z komponenty typu ListBox do jiných komponent.

  9. Každý ovladač má vlastnost DragMode, která určuje jak komponenta reaguje na zahájení tažení. Pokud DragMode je dmAutomatic, pak tažení začíná automaticky když uživatel stiskne tlačítko myši s kurzorem myši nad ovladačem. Protože dmAutomatic může kolidovat s dalšími aktivitami myši, obvykle nastavujeme DragMode na dmManual (implicitní) a tažení zahájíme zpracováním události stisknutí tlačítka myši. K zahájení ručního řízení tažení voláme metodu BeginDrag komponenty.
    BeginDrag přebírá parametr typu bool nazvaný Immediate. Pokud parametr má hodnotu true, pak tažení začíná bezprostředně, stejně jako když DragMode je nastaveno na dmAutomatic. Při hodnotě false, tažení začíná až při nepatrném pohybu myší (tím lze rozlišit začátek tažení od kliknutí). Do obsluhy stisknutí tlačítka myši můžeme také umístit nějakou podmínku, např. testovat které tlačítko myši bylo stisknuto.
    Následující kód např. zpracovává událost stisknutí myši nad oknem seznamu souborů a tažení zahájíme pouze při stisku levého tlačítka myši:
    void __fastcall TFMForm::FileListBox1MouseDown(TObject *Sender,
                    TMouseButton Button, TShiftState Shift, int X, int Y)
    {
      if (Button == mbLeft) // pouze při stisknutém levém tlačítku
      {
        TFileListBox *pLB = (TFileListBox *)Sender;
        if (pLB->ItemAtPos(Point(X,Y), true) >= 0) // je zde prvek?
          pLB->BeginDrag(false);                   // pokud ano, táhneme
      }
    }
    Dříve než můžeme prvek táhnout, musíme určit, kam můžeme prvek přetáhnout. Když uživatel táhne cokoliv nad ovladačem, pak ovladač získá událost OnDragOver a musíme indikovat, zda ovladač může tažený prvek akceptovat. C++ Builder mění kurzor myši v závislosti na tom, zda tažený prvek je akceptovatelný. K akceptování prvku taženého nad ovladačem vytvoříme obsluhu událostí OnDragOver ovladače. Obsluha má parametr Accept, který nastavíme na true, pokud prvek je akceptován. Nastavením Accept na true specifikujeme, že pokud v tomto místě uživatel uvolní tlačítko myši, pak aplikace může generovat událost OnDragDrop pro stejný ovladač. Pokud Accept je false, pak aplikace nemůže ukončit tažení prvku nad tímto ovladačem. To znamená, že ovladač nikdy nebude generovat událost OnDragDrop pro tento ovladač a nemusí tedy vědět, jak ji obsloužit.
    Událost OnDragOver má několik parametrů, včetně zdroje tažení a souřadnic ukazatele myši. Obsluha události tyto parametry může použít k určení, zda tažení akceptovat. Často ovladač akceptuje tažení prvku na základě typu zdroje tažení, ale může také akceptovat prvky pouze specifických instancí.
    V následujícím příkladě adresářový strom akceptuje tažené prvky pouze pokud pocházejí z okna seznamu souborů:
    void __fastcall TForm1::TreeView1DragOver(TObject *Sender,
      TObject *Source, int X, int Y, TDragState State, bool &Accept)
    {
      if (Source->InheritsFrom(__classid(TFileListBox)))
        Accept = true;
    }
    Nestačí pouze indikovat akceptování tažení, ale musíme také definovat zpracování přetaženého prvku. Pro zpracování přetaženého prvku připojíme k ovladači akceptujícímu tažený prvek obsluhu události OnDragDrop. Podobně jako událost OnDragOver i událost OnDragDrop indikuje zdroj tažení a souřadnice uvolnění kurzoru myši na akceptujícím ovladači. Tyto informace umožňují obsluze získat potřebné informace od zdroje tažení a určit, jak tažení zpracovat. Např. adresářový strom akceptující prvek tažení z okna seznamu souborů může přesunout soubor z jeho současného místa do určeného adresáře:
    void __fastcall TForm1::TreeView1DragDrop(TObject *Sender,
      TObject *Source, int X, int Y)
    {
      if (Source->InheritsFrom(__classid(TFileListBox)))
      {
        TTreeNode *pNode = TreeView1->GetNodeAt(X,Y); // pNode je cíl tažení
        AnsiString NewFile = pNode->Text + AnsiString("\") +
          ExtractFileName(FileList->FileName); // jméno cíle tažení
        MoveFileEx(FileList->FileName.c_str(), NewFile.c_str(),
          MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
      }
    }
    Když operace tažení končí (uživatel přetáhne prvek nebo uvolní tlačítko myši nad ovladačem neakceptujícím tažení), pak je zaslána událost OnEndDrag zpět ovladači odkud začalo tažení. Tato událost má důležitý parametr nazvaný Target, který indikuje ovladač akceptující tažení. Má-li tento parametr hodnotu NULL, pak tažení neakceptoval žádný ovladač. Parametry události OnEndDrag také obsahují souřadnice na přijímajícím ovladači, kde tažení skončilo.
    V následujícím příkladu, okno seznamu souborů zpracovává událost ukončení tažení překreslením svého seznamu souborů, neboť přetažením souboru se obsah seznamu změnil:
    void __fastcall TFMForm::FileList1EndDrag(TObject *Sender,
                    TObject *Target,   int X, int Y)
    {
      if (Target)
        FileList1->Update();
    }
  10. Můžeme použít TDragObject k přizpůsobení chování našeho objektu tažení. Implicitně, události OnDragOver a OnDragDrop indikují zdroj taženého prvku a souřadnice kurzoru myši nad akceptujícím ovladačem. Můžeme získat další stavové informace odvozením objektu tažení od TDragObject a předefinováním jeho virtuálních metod. Pomocí TDragObject, zdroj tažení je samotný objekt a ne objekt ovladače jako u objektu TDragControl.

  11. Vytvoříme uživatelský objekt tažení v události OnStartDrag. Použijeme veřejnou funkci IsDragObject v události OnDragOver když akceptujeme tažení.
    TDragObject umožňuje flexibilnější zpracování tažení. Normálně parametr Source událostí OnDragOver a OnDragDrop je ovladač, kde operace tažení začala. Pokud více ovladačů různých typů může být zdrojem tažení stejného typu dat, pak zdroj musí mít podporu pro každý typ ovladače. Tažený objekt umožňuje cíli znát pouze jak zpracovat tažený objekt jako zdroj, neboť každý ovladač zdroje může vytvořit vhodný typ taženého objektu ve své události OnStartDrag. Události OnDragOver a OnDragDrop mohou říci zda zdroj je tažený objekt a zabrániti ovladači ve volání IsDragObject.
  12. Můžeme také změnit tvar ukazatele myši v průběhu operace tažení. Provedeme to nastavením vlastnosti DragCursor komponenty. Můžeme také vytvořit svůj zdroj kurzoru.
  13. Každý Windowsovský program používá zdroje. Zdroje jsou prvky programu, které nejsou spustitelným kódem. Zdroje např. zahrnují bitové mapy, kurzory, dialogová okna, ikony, nabídky apod. Zdroje jsou obecně obsaženy v souborech skriptu zdrojů (textový soubor s příponou RC), které jsou překládány překladačem zdrojů a potom během fáze sestavování jsou připojeny k aplikačnímu EXE souboru.

  14. Zdroje jsou obvykle spojeny s proveditelným souborem. Některé zdroje, jako jsou bitové mapy, tabulky řetězců a zvukové soubory mohou být uloženy v externích souborech (BMP, TXT a WAV) nebo mohou být připojeny k aplikačnímu EXE souboru. Umístění zdrojů do EXE souboru má dvě výhody: Nevýhodou je zvětšení EXE souboru. Tento soubor není větší než spojení kódu a externích zdrojových souborů, ale je zapotřebí delší čas k zavedení programu.
    Je zapotřebí se rozhodnout, zda zdroje budeme držet v externích souborech nebo zda je připojíme k EXE souboru. Můžeme použít oba způsoby a dokonce jejich kombinaci v rámci jednoho programu. Tradiční Windowsovský program obsahuje alespoň jedno dialogové okno a ikonu. Aplikace C++ Builderu se nepatrně liší. Dialogová okna v C++ Builderu nejsou zdroji (popis formuláře je uložen jako zdroj, ale nejedná se o zdroj dialogového okna). Aplikace C++ Builderu má tradiční zdroj ikony. Když vytváříme aplikaci, pak C++ Builder přebírá soubor zdroje ikony. Podobně, když volíme bitovou mapu pro tlačítko nebo komponentu Image, pak C++ Builder vkládá soubor bitové mapy jako část zdroje formuláře. Formulář a všechny jeho zdroje jsou spojeny s programovým souborem při překladu a sestavování programu. Toto probíhá automaticky.
    Jsou ale situace, kdy potřebujeme implementovat zdroje mimo normální zpracování C++ Builderu, Např. animaci chceme provádět střídáním řady bitových map, které budou zaváděny jako řada zdrojů (zvýšení rychlosti provádění). Je tedy vhodné znát, jak C++ Builder připojuje zdroje k programovému souboru.
    Zdroje musíme také vytvořit. Pokud používáme dobrý editor zdrojů, pak to není složité. Pro vytvoření bitových map, ikon a kurzorů lze použít Editor obrázků C++ Builderu. Tímto editorem ale není možno vytvářet jiné zdroje. Pokud máme překladač Borland C++, pak k editaci zdrojů můžeme použít Resource Workshop z tohoto produktu. Po vytvoření zdrojů máme soubor RC, který můžeme přidat k projektu C++ Builderu přímo nebo jej přeložit do tvaru RES pomocí překladače zdrojů (BRCC32.EXE) a k projektu přidat soubor přeložených zdrojů.
    Soubory RES nebo RC přidáváme k projektu pomocí správce projektu. Správce projektu zobrazíme volbou View | Project Manager. Zde stiskneme tlačítko Add File To Project na paletě nástrojů správce a v dialogovém okně otevření souboru vybereme přidávaný soubor zdrojů (se Správcem projektů se podrobněji seznámíme později).
  15. Použití zdrojů si ukážeme na jednoduché aplikaci. Tentokrát použijeme již hotovou aplikaci Jumping Jack. Tento program ukazuje jednoduchou animaci se zvukovým efektem. Hlavní formulář obsahuje dvě tlačítka, komponentu Image a komponentu Label. V této aplikaci si ukážeme jak zavádět bitovou mapu uloženou jako zdroj, jak zavádět a zobrazovat zdroje řetězců a jak přehrávat zvukové soubory uložené jako zdroje. Aplikaci si stáhněte a vyzkoušejte.

  16. Podívejte se na červené řádky v následujícím výpisu hlavičkového souboru aplikace.
    #ifndef JJMainH
    #define JJMainH
    #include <vcl\Classes.hpp>
    #include <vcl\Controls.hpp>
    #include <vcl\StdCtrls.hpp>
    #include <vcl\Forms.hpp>
    #include <vcl\ExtCtrls.hpp>
    class TMainForm : public TForm
    {
    __published:    // IDE-managed Components
        TButton *Start;
        TButton *Stop;
        TImage *Image;
        TLabel *Label;
        void __fastcall FormCreate(TObject *Sender);

        void __fastcall StartClick(TObject *Sender);
        void __fastcall StopClick(TObject *Sender);
    private:        // User declarations
        bool done;
        void DrawImage(String& name);
    public:         // User declarations
        virtual __fastcall TMainForm(TComponent* Owner);
    };
    extern PACKAGE TMainForm *MainForm;
    #endif
    Na prvním z nich deklarujeme datovou složku typu bool, která je použita k určení zda animaci zastavit. Na druhém řádku je deklarace metody použité k zobrazování bitové mapy v komponentě Image.
    Ve výpisu zdrojového kódu jednotky, si povšimněte dvou funkcí API, které jsou použity k zavádění zdrojů řetězců a zvukových souborů. Na červeném řádku, funkce LoadString zavádí zdroj řetězce do textové vyrovnávací paměti na základě číselné identifikace řetězce. Řetězec je potom přiřazen vlastnosti Caption komponenty Label. Na modrém řádku, funkce PlaySound je použita k přehrání zdroje zvukového souboru. Příznak SND_ASYNC určuje, že zvuk bude přehráván asynchronně, tj. animace bude probíhat současně s přehráváním. Příznak SND_RESOURCE říká Windows, že zvuk je obsažen ve zdroji a ne v souboru na disku. Obě tyto funkce přebírají globální proměnnou HInstance, která říká Windows, kde hledat zdroje ve spustitelném souboru. Pokud počítač není vybaven zvukovou kartou, pak zvuk neuslyšíme.
    #include <vcl\vcl.h>
    // zapotřebí pro funkci PlaySound()
    #include <vcl\mmsystem.hpp>
    #pragma hdrstop
    #include "JJMain.h"
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    // definice pro zdroje řetězců
    #define IDS_UP    101
    #define IDS_DOWN  102
    HINSTANCE hInst;
    TMainForm *MainForm;
    //--------------------------------------------------------------
    __fastcall TMainForm::TMainForm(TComponent* Owner)
      : TForm(Owner), done(false)
    {
    }
    void __fastcall TMainForm::FormCreate(TObject *Sender)
    {
      hInst = (HINSTANCE)HInstance;
      // Zavedení a zobrazení první bitové mapy
      Image->Picture->Bitmap->
        LoadFromResourceName((int)hInst, "ID_BITMAP1");
    }
    void __fastcall TMainForm::StartClick(TObject *Sender)
    {
      String s = "ID_BITMAP";
      char buff[10];
      done = false;
      while (!done) {
        for (int i=1;i<6;i++) {
          String resName = s + String(i);
          DrawImage(resName);
        }
        LoadString(hInst, IDS_UP, buff, sizeof(buff));
        Label->Caption = buff;
        Label->Refresh();
        PlaySound("ID_WAVEUP", hInst, SND_ASYNC | SND_RESOURCE);
        Sleep(200);
        for (int i=5;i>0;i--) {
          String resName = s + String(i);
          DrawImage(resName);
        }
        PlaySound("ID_WAVEDOWN", hInst, SND_ASYNC | SND_RESOURCE);
        LoadString(hInst, IDS_DOWN, buff, sizeof(buff));
        Label->Caption = buff;
        Label->Refresh();
        Sleep(200);
      }
    }
    void __fastcall TMainForm::StopClick(TObject *Sender)
    {
      done = true;
    }
    void TMainForm::DrawImage(String& name)
    {
      Image->Picture->Bitmap->LoadFromResourceName((int)hInst, name);
      Application->ProcessMessages();
      Sleep(20);
    }
    Následuje výpis začátku souboru skriptu zdrojů.
    #define IDS_UP   101
    #define IDS_DOWN 102
    STRINGTABLE
    {
     IDS_UP, "Up"
     IDS_DOWN, "Down"
    }
    ID_WAVEUP   WAVE "up.wav"
    ID_WAVEDOWN WAVE "down.wav"
    ID_BITMAP1 BITMAP LOADONCALL MOVEABLE DISCARDABLE IMPURE
    {
     '42 4D 76 02 00 00 00 00 00 00 76 00 00 00 28 00'
     '00 00 20 00 00 00 20 00 00 00 01 00 04 00 00 00'
    ...
    Červeně označené řádky definují zdroj tabulky řetězců. Tabulku řetězců snadno vytvoříme v libovolném textovém editoru. Žluté řádky jsou zdroje WAVE (pro oba soubory WAV, které byly předem zaznamenány a umístěny do adresáře projektu). Když překladač zdrojů uvidí deklaraci WAVE, přečte určený soubor a přeloží jej do binárního souboru zdrojů. Bitové mapy jsou vytvořeny v tradičním editoru zdrojů. Popis zdrojů pro bitové mapy může být velmi dlouhý. Zbytek našeho souboru jsou zdroje bitových map.

3. Přidání grafiky k ovladačům