6. Seznámení s datovými množinami

Základní jednotkou pro zpřístupňování dat v C++ Builderu je rodina objektů datových množin. Naše aplikace používá datové množiny pro veškerý přístup k databázím. Obecně objekt datové množiny reprezentuje určitou tabulku obsaženou v databázi, nebo reprezentuje dotaz nebo uloženou proceduru, které zpřístupňují databázi. Všechny objekty datových množin, které používáme v našich databázových aplikacích, jsou odvozeny od TDataSet a tedy od TDataSet dědí datové položky, vlastnosti, události a metody. Abychom mohli používat libovolný objekt datové množiny, musíme pochopit sdílenou funkčnost poskytnutou TDataSet.
Na následujícím obrázku je uvedena hierarchie všech komponent datových množin.

TDataSet je předek všech objektů datových množin, které můžeme používat ve svých aplikacích. Definuje množinu datových položek, vlastností, událostí a metod sdílených všemi objekty datových množin. TDataSet je virtuální datová množina, což znamená, že většina jejich vlastností a metod je virtuálních nebo čirých. Tyto virtuální metody mohou být přepsány (a obvykle jsou) ve svých potomcích, ale v každém z nich mohou být implementovány jinak. Čirá virtuální metoda je deklarována bez aktuální implementace. Deklarace je prototyp, který popisuje metodu (a její parametry a návratový typ), která musí být implementována ve všech potomcích.
Protože TDataSet obsahuje čiré virtuální metody, nemůže být v našich aplikacích použita přímo. Musíme vytvářet instance potomků TDataSet jako je TTable, TQuery, TStoredProc a TClientDataSet a použít je ve své aplikaci nebo odvodit svou vlastní třídu datové množiny od TDataSet nebo jeho potomků a zapsat implementaci všech jejich abstraktních metod.
TDataSet definuje vše, co je společné pro všechny objekty datových množin. Např. TDataSet definuje základní strukturu všech datových množin: pole komponent TField, které odpovídají sloupcům v jedné nebo více databázových tabulkách a vyhledávací nebo počitatelné položky poskytnuté naši aplikaci.
V této kapitole se seznámíme s funkčností TDataSet, kterou dědí objekty datových množin používané v našich aplikacích. Tuto sdílenou funkčnost musíme pochopit, abychom mohli používat libovolné objekty datových množin. Je popsána v těchto bodech:

Typy datových množin

K pochopení společné koncepce pro všechny objekty datových množin a jako přípravu pro vývoj svých vlastních přizpůsobených datových množin, které používají BDE, je zapotřebí se seznámit s touto kapitolou.
K vývoji tradičních dvou vrstvových databázových aplikací typu Klient/server používajících BDE se musíme také seznámit s BDE. Úvod do TBDEDataSet a TDBDataSet a seznámení s BDE bude uvedeno později. V této kapitole jsou popsány sdílené služby nejčastěji používaných komponent datových množin TQuery, TStoredProc a TTable.
S některými verzemi C++ Builderu můžeme vyvíjet více vrstvové databázové aplikace používající distribuované datové množiny. S prací klientských datových množin ve více vrstvových aplikacích se seznámíme později.

Otevírání a uzavírání datových množin

Abychom mohli číst nebo zapisovat data tabulky (nebo dotazu), aplikace musí nejprve otevřít datovou množinu. Datovou množinu lze otevřít dvěmi způsoby. Nastavíme vlastnost Active datové množiny na true a to při návrhu v Inspektoru objektů nebo kódem za běhu. Např.
CustTable->Active = true;
Druhou množností je volání metody Open pro datovou množinu za běhu:
CustQuery->Open();
Obdobně, datovou množinu můžeme také uzavřít dvěmi způsoby. Vlastnost Active datové množiny nastavíme na false a to při návrhu v Inspektoru objektů nebo kódem za běhu. Např.
CustQuery->Active = false;
Druhou možností je volání metody Close pro datovou množinu za běhu:
CustTable->Close();
Datovou množinu musíme uzavřít abychom mohli změnit jisté její vlastnosti (např. TableName komponenty TTable). Za běhu můžeme také chtít uzavřít datovou množinu z jistých důvodů specifických pro naši aplikaci.

Zjišťování a nastavování stavu datové množiny

Stav nebo režim datové množiny určuje co lze provádět s jejími daty. Např. když datová množina je uzavřena, pak její stav je dsInactive, což znamená, že není možno nic provádět s jejími daty. Za běhu, můžeme testovat vlastnost datové množiny State (je určená pouze pro čtení) k zjištění jejího současného stavu. Následující tabulka uvádí možné hodnoty vlastnosti State a co tyto stavy znamenají:
 
Hodnota Význam
dsInactive Uzavřená datová množina. Její data jsou nedostupná.
dsBrowse Otevřená datová množina. Její data můžeme prohlížet, ale ne měnit. Je to implicitní stav otevřené datové množiny.
dsEdit Otevřená datová množina. Současný řádek může být modifikován.
dsInsert Otevřená datová množina. Je vkládán nebo přidáván nový řádek.
dsSetKey Pouze pro TTable nebo TClientDataSet. Otevřená datová množina. Umožňuje nastavovat hodnoty rozsahu a klíče použité v operacích rozsahu a GotoKey.
dsCalcFields Otevřená datová množina. Indikuje, že množina je pod řízením události OnCalcFields. Zabraňuje změnám položek, které nejsou počitatelné.
dsCurValue Pouze pro interní použití.
dsNewValue Pouze pro interní použití.
dsOldValue Pouze pro interní použití.
dsFilter Otevřená datová množina. Indikuje, že množina je řízena operací filtru. Filtrovaná množina dat může být prohlížena a žádná data nemohou být změněna.

Když aplikace otevře datovou množinu, pak je implicitně uvedena do stavu dsBrowse. Stav datové množiny se mění podle toho jak aplikace zpracovává data. Otevřenou datovou množinu převedeme z jednoho stavu do jiného, kódem naší aplikace nebo vestavěným chováním datových komponent. Pro převedení datové množiny do stavu dsEdit, dsBrowse, dsInsert nebo dsSetKey voláme metodu odpovídající jménu stavu. Např. následující kód uvádí CustTable do stavu dsInsert, akceptuje vstup uživatele jako nový záznam a zapisuje nový záznam do databáze:
CustTable->Insert(); // uvádí datovou množinu do stavu dsInsert
AddressPromptDialog->ShowModal();
if (AddressPromptDialog->ModalResult == mrOK)
  CustTable->Post(); // při úspěchu přechod do stavu dsBrowse
else
  CustTable->Cancel(); // přechod do stavu dsBrowse
Tento příklad také ukazuje, že stav datové množiny se automaticky změní na dsBrowse, když metoda Post úspěšně zapíše záznam do databáze (při chybě Post se stav datové množiny nemění) nebo když je volána metoda Cancel.
Některé stavy nemohou být nastavovány přímo. Např. k uvedení datové množiny do stavu dsInactive, nastavíme její vlastnost Active na false nebo voláme metodu Close datové množiny. Následující příkazy jsou ekvivalentní:
CustTable->Active = false;
CustTable->Close();
Zbývající stavy (dsCalcField, dsCurValue, dsNewValue, dsOldValue a dsFilter) nemohou být nastavovány naší aplikací. Do těchto stavů datová množina přejde automaticky, když je to nutné. Např. stav dsCalcField je nastaven, když je generována událost OnCalcField. Když událost OnCalcField skončí, pak datová množina se vrátí do předchozího stavu.
Poznámka: Když se změní stav datové množiny, pak je generována událost OnStateChange pro všechny komponenty datových zdrojů přiřazených k datové množině.
V následujících bodech je provedeno seznámení s těmito stavy, jak a kdy jsou stavy nastaveny, jaké jsou vztahy mezi jednotlivými stavy a kde najdeme potřebné informace. Jsou zde uvedeny tyto body:

Deaktivování datové množiny
Datová množina je neaktivní, když je uzavřena. Nemůžeme přistupovat k záznamům v uzavřené datové množině. Při návrhu, datová množina je uzavřena, dokud nenastavíme její vlastnost Active na true. Za běhu je datová množina uzavřena dokud ji aplikace neotevře voláním metody Open nebo nastavením vlastnosti Active na true. Když otevřeme neaktivní datovou množinu, pak její stav se automaticky změní na dsBrowse.
K udělání datové množiny neaktivní voláme její metodu Close. Můžeme zapsat obsluhy událostí OnBeforeClose a OnAfterClose, které reagují na metodu Close pro datovou množinu. Např. pokud datová množina je ve stavu dsEdit nebo dsInsert a aplikace volá Close, pak se aplikace může dotázat uživatele, zda změny zapsat nebo zrušit a to před uzavřením datové množiny (v obsluze události OnBeforeClose):
void __fastcall TForm1::VerifyBeforeClose(TDataSet *DataSet)
{
  if (DataSet->State == dsEdit || DataSet->State == dsInsert)
  {
    TMsgDlgButtons btns;
    btns << mbYes << mbNo;
    if (MessageDlg("Post changes before closing?",mtConfirmation,btns,0)==mrYes)
      DataSet->Post();
    else
      DataSet->Cancel();
  }
}
Prohlížení datové množiny
Když aplikace otevře datovou množinu, pak datová množina automaticky přejde do stavu dsBrowse. Tento stav umožňuje prohlížet záznamy v datové množině, ale neumožňuje jejich editaci nebo vkládání nových záznamů. Ze stavu dsBrowse můžeme přejít do všech ostatních stavů. Např. voláním metody Insert nebo Append pro datovou množinu změníme její stav z dsBrowse na dsInsert (pokud to ostatní vlastnosti datové množiny umožňují). Volání SetKey uvádí datovou množinu do stavu dsSetKey.
Dvě metody přiřazené ke všem datovým množinám, mohou vrátit datovou množinu do stavu dsBrowse. Cancel ukončuje současnou editační, vkládací nebo vyhledávací úlohu a vždy vrací datovou množinu do stavu dsBrowse. Post se pokusí zapsat změny do databáze a v případě úspěchu také vrací datovou množinu do stavu dsBrowse. Při neúspěchu se současný stav datové množiny nemění.
Umožnění editace datové množiny
Datová množina musí být ve stavu dsEdit, aby aplikace mohla modifikovat její záznamy. V našem kódu můžeme použít metodu Edit k uvedení datové množiny do stavu dsEdit, pokud vlastnost CanModify (určená pouze pro čtení) datové množiny je true. Vlastnost CanModify je true, pokud k databázi spojené s datovou množinou máme čtecí a zápisová privilegia.
Některé datové ovladače na formuláři v naši aplikaci mohou automaticky uvést datovou množinu do stavu dsEdit, pokud: Důležité: Pro komponentu TTable, pokud vlastnost ReadOnly je true, pak CanModify je false, což zabraňuje editaci záznamů. Podobně pro komponentu TQuery, pokud vlastnost RequestLive je false, pak CanModify je false.
Poznámka: I když datová množina je ve stavu dsEdit, pak pro SQL databáze, editace záznamů nemusí být úspěšná, pokud uživatel naší aplikace nemá potřebná přístupová privilegia.
Datovou množinu můžeme vrátit ze stavu dsEdit do stavu dsBrowse v kódu voláním metod Cancel, Post nebo Delete. Cancel zruší editaci současné položky nebo záznamu. Post se pokusí zapsat modifikovaný záznam do datové množiny a v případě úspěchu vrací datovou množinu do stavu dsBrowse (při neúspěchu se stav nemění). Delete se pokusí odstranit současný záznam z datové množiny a v případě úspěchu vrací datovou množinu do stavu dsBrowse (v případě neúspěchu datová množina zůstává ve stavu dsEdit).
Datové ovladače pro které editace je povolena, automaticky volají Post, když uživatel provede libovolnou akci, která mění současný záznam (přesun na jiný záznam) nebo která způsobí, že ovladač ztrácí zaostření (přesun na jiný ovladač na formuláři).
Umožnění vkládání nových záznamů
Datová množina musí být ve stavu dsInsert, aby aplikace mohla k ní přidat nový záznam. V našem kódu můžeme použít metodu Insert nebo Append k uvedení datové množiny do stavu dsInsert, pokud vlastnost určená pouze pro čtení CanModify datové množiny je false. Pro stav dsInsert platí vše, co je výše uvedeno pro stav dsEdit.
Umožnění prohledávání datových množin
Libovolnou datovou množinu můžeme prohledávat pomocí metod Locate a Lookup. Komponenty TTable poskytují další metody (GotoKey a FindKey), která umožňují vyhledávat záznamy na základě indexu tabulky. Pro použití těchto metod pro komponentu tabulky, komponenta musí být ve stavu dsSetKey. Stav dsSetKey je aplikovatelný pouze na komponenty TTable. Datovou množinu uvedeme do stavu dsSetKey za běhu voláním metody SetKey. Metody GotoKey, GotoNearest, FindKey a FindNearest ukončující současné hledání vracejí datovou množinu do stavu dsBrowse po dokončení vyhledávání.
Můžeme zobrazit a editovat podmnožinu dat pro libovolnou datovou množinu pomocí filtrů. Komponenta TTable také podporuje další způsob přístupu k podmnožině záznamů, nazvaný rozsah. K vytvoření a aplikování rozsahu na tabulku, tabulka musí být ve stavu dsSetKey.
Počitatelné položky
C++ Builder uvádí datovou množinu do režimu dsCalcFields, když aplikace volá obsluhu události OnCalcFields datové množiny. Tento stav zabraňuje modifikacím nebo přidávání záznamů do datové množiny, mimo modifikace počitatelných položek modifikovaných obsluhou. Všechny ostatní modifikace jsou zakázány, protože OnCalcFields používá hodnoty ostatních položek k odvození hodnot počitatelných položek. Změny na těchto ostatních položkách by mohli znehodnotit hodnoty přiřazené počitatelným položkám. Když obsluha OnCalcFields skončí, pak datová množina se vrátí do stavu dsBrowse.
Filtrování záznamů
Když aplikace volá obsluhu události OnFilterRecord datové množiny, pak C++ Builder uvádí datovou množinu do stavu dsFilter. Tento stav zabraňuje modifikacím nebo přidávání záznamů do datové množiny během procesu filtrování a tedy filtr nemůže být znehodnocen. Po dokončení obsluhy OnFilterRecord, se datová množina vrací do stavu dsBrowse.
Aktualizování záznamů
Když provádíme operace odložených aktualizací, pak C++ Builder může uvést datovou množinu dočasně do stavu dsNewValue, dsOldValue nebo dsCurValue. Tyto stavy indikují, že odpovídající vlastnosti položkových komponent (NewValue, OldValue a CurValue) mohou být zpřístupněny a to obvykle v obsluze události OnUpdateError. Naše aplikace nemůže vidět nebo nastavovat tyto stavy.

Procházení datovými množinami

Každá aktivní datová množina má kurzor (ukazatel na současný řádek v datové množině). Současný řádek v datové množině je ten, jehož hodnoty mohou být zpracovány metodami editace, vkládání a rušení a ten jehož hodnoty položek jsou právě zobrazeny v jednotlivých položkových datových ovladačích na formuláři, jako je např. TDBEdit, TDBLabel nebo TDBMemo.
Současný řádek můžeme změnit přesunem kurzoru k ukazování na jiný řádek. Následující tabulka uvádí metody, které můžeme použít v kódu aplikace k přesunu na jiný záznam:
 
Metoda Popis
First Přesouvá kurzor k ukazování na první řádek v datové množině.
Last Přesouvá kurzor k ukazování na poslední řádek v datové množině.
Next Přesouvá kurzor k ukazování na následující řádek v datové množině.
Prior Přesouvá kurzor k ukazování na předchozí řádek v datové množině.
MoveBy Přesouvá kurzor o specifikovaný počet řádků dopředu nebo zpět v datové množině.

Vizuální datový ovladač TDBNavigator zaobaluje tyto metody jako tlačítka, která uživatel může stisknout k pohybu po záznamech za běhu aplikace.
Mimo těchto metod, další tabulka popisuje dvě logické vlastnosti datové množiny, které poskytují užitečné informace při procházení záznamy datové množiny:
 
Vlastnost Popis
Bof true pokud kurzor je na prvním řádku v datové množině; false v opačném případě
Eof true pokud kurzor je na posledním řádku v datové množině; false v opačném případě

V této části jsou uvedeny ještě tyto body:

Používání metod First a Last
Metoda First přesouvá kurzor na první řádek v datové množině a nastavuje vlastnost Bof na true. Pokud kurzor je již na prvním řádku datové množiny, pak First nic nedělá. Např. následující kód přesouvá kurzor na první záznam v CustTable:
CustTable->First();
Metoda Last přesouvá kurzor na poslední řádek v datové množině a nastavuje vlastnost Eof na true. Pokud kurzor je již na posledním řádku datové množiny, pak Last nic nedělá. Např. následující kód přesouvá kurzor na poslední záznam v CustTable:
CustTable->Last();
Poznámka: I když mohou být různé důvody pro přechod na první nebo poslední řádek v datové množině bez intervence uživatele, můžeme povolit uživateli přechod ze záznamu na záznam pomocí komponenty TDBNavigator. Tato komponenta obsahuje tlačítka, která umožňují uživateli přesun na první nebo poslední řádek aktivní datové množiny. Událost OnClick těchto tlačítek volá metody First nebo Last datové množiny.
Používání metod Next a Prior
Metoda Next přesouvá kurzor dopředu o jeden řádek v datové množině a nastavuje Bof na false, pokud datová množina není prázdná. Pokud kurzor je již na posledním řádku datové množiny, pak volání Next nic nedělá. Např. následující kód přesouvá kurzor na následující záznam v CustTable:
CustTable->Next();
Metoda Prior přesouvá kurzor zpět o jeden řádek v datové množině a nastavuje Eof na false, pokud datová množina není prázdná. Pokud kurzor je již na prvním řádku datové množiny, pak volání Prior nic nedělá. Např. následující kód přesouvá kurzor na předcházející záznam v CustTable:
CustTable->Prior();
Požívání metody MoveBy
MoveBy umožňuje specifikovat o kolik řádků dopředu nebo dozadu přesunout kurzor v datové množině. Přesun je relativní k současnému záznamu v době, kdy MoveBy je voláno. MoveBy také nastavuje vlastnosti Bof a Eof datové množiny podle skutečného stavu. Tato metoda přebírá celočíselný parametr určující o kolik řádků se přesouváme. Kladná čísla indikují směr dopředu a záporná přesun zpět.
MoveBy vrací počet řádků o který byl proveden přesun. Pokud se pokusíme provést přesun o více řádků než kolik jich je k začátku nebo konci datové množiny, pak počet řádků vrácený MoveBy se liší od počtu řádků požadovaného přesunu. Přesun končí při dosažení prvního nebo posledního řádku v datové množině. Následující kód přesouvá kurzor o dva záznamy zpět v CustTable:
CustTable->MoveBy(-2);
Poznámka: Pokud používáme MoveBy ve své aplikaci a pracujeme ve víceuživatelském databázovém prostředí, nesmíme zapomenout, že databáze se mění. Záznam, který byl o pět záznamů zpět může být nyní o čtyři, šest nebo neznámý počet záznamů zpět, protože několik uživatelů může současně přistupovat k databázi a měnit její data.
Používání vlastností Eof a Bof
Běhové vlastnosti (určené pouze pro čtení) Eof (End of file) a Bof (Begin of file) jsou užitečné pro procházení datovou množinou, obzvláště když chceme procházet všemi záznamy v datové množině.
Když Eof je true, pak kurzor je na posledním řádku v datové množině. Eof je true, pokud aplikace otevře prázdnou datovou množinu, volá metodu Last datové množiny, volá metodu Next datové množiny a metoda je neúspěšná neboť kurzor je na posledním řádku datové množiny nebo volá SetRange na prázdný rozsah nebo datovou množinu. Eof je v ostatních případech nastaveno na false.
Eof je často testováno v podmínkách cyklů k řízení procesu iterace všemi záznamy v databázi. Pokud otevřeme datovou množinu obsahující záznamy (nebo voláme First), pak Eof je false. K procházení záznamy datové množiny vytvoříme cyklus, který je ukončen, když Eof je true. Uvnitř cyklu voláme Next pro každý záznam v datové množině. Eof je false dokud Next nepřejde na poslední záznam. Následující příklad kódu ukazuje jeden způsob procházení záznamy datové množiny nazvané CustTable:
CustTable->DisableControls();
try
{
  CustTable->First();     // přechod na první záznam
  while (!CustTable->Eof) // cyklus dokud Eof není true
  (
    // Zpracování každého záznamu
    ...
    CustTable->Next();
    // když jsme na posledním záznamu, pak Eof je true; jinak je false
  }
}
__finally
{
  CustTable->EnableControls();
}
Tip: Tento příklad také ukazuje jak zakázat a povolit vizuální datové ovladače spojené s datovou množinou. Pokud vizuální datové ovladače během procházení datovou množinou zakážeme, pak proces iterace je urychlen, neboť C++ Builder nemusí aktualizovat obsah těchto ovladačů při změně aktuálního záznamu. Po dokončení iterace, ovladače opět povolíme, aby obsahovaly hodnoty pro nový současný řádek. Povšimněte si, že povolení ovladačů je vloženo do klauzule __finally příkazu try ... __finally. Tím zajistíme, že i když výjimka ukončí cyklus předčasně, ovladače nezůstanou zakázané.
Bof je true, když kurzor je na prvním řádku datové množiny. Bof je nastaveno na true, když aplikace otevře datovou množinu, volá metodu First datové množiny, volá metodu Prior datové množiny když kurzor je na jejím prvním řádku nebo volá SetRange na prázdný rozsah nebo datovou množinu. V ostatních případech je Bof nastaveno na false.
Podobně jako Eof i Bof může být použito v podmínkách cyklů při řízení iteračního zpracování záznamů v datové množině. Ukazuje to následující kód:
CustTable->DisableControls(); // urychlení zpracování
try
{
  while (!CustTable->Bof)     // cyklus dokud Bof je true
  (
    // zpracování každého záznamu
    ...
    CustTable->Prior();
  }
}
catch (...)
{
  CustTable->EnableControls();
  throw;
}
CustTable->EnableControls();
Označování a návrat na záznamy
Mimo přesunu ze záznamu na záznam v datové množině, je často také užitečné označit jisté místo v datové množině, abychom se na ně mohli rychle vrátit, když je potřeba. TDataSet a jeho potomci implementují službu záložek, která umožňuje označit záznamy a později se na ně vracet. Služba záložek se skládá z vlastnosti Bookmark a pěti metod pro práci se záložkami.
Vlastnost Bookmark indikuje, která záložka z mnoha záložek v naši aplikaci je aktuální. Bookmark je řetězec, který identifikuje aktuální záložku. Kdykoliv můžeme předat další záložku, která se stává aktuální záložkou.
TDataSet implementuje virtuální metody záložek. I když tyto metody zajišťují, že libovolný objekt odvozený od TDataSet vrací při volání metody záložky hodnotu, vrácená hodnota je implicitní a neurčuje současné místo. Potomci TDataSet, jako je TBDEDataSet, reimplementují metody záložek k návratu rozumných hodnot podle následujícího seznamu: Pro vytvoření záložky v naši aplikaci musíme deklarovat proměnnou typu TBookmark, pak volat GetBookmark k alokování místa pro proměnnou a nastavení její hodnoty na jisté místo v datové množině. Proměnná TBookmark je ukazatel (void *).
Před voláním GotoBookmark k přesunu na specifikovaný záznam, můžeme volat BookmarkValid k určení zda záložka ukazuje na záznam. BookmarkValid vrací true, pokud specifikovaná záložka na záznam ukazuje. V TDataSet, BookmarkValid vždy vrací false (indikace, že záložka není přípustná). Potomci TDataSet metodu reimplementují k poskytnutí smysluplné návratové hodnoty.
Můžeme také volat CompareBookmarks k zjištění zda se chceme přesunout na jinou (ne současnou) záložku. TDataSet::CompareBookmarks vždy vrací 0 (indikace, že záložky jsou identické). Potomci TDataSet tuto metodu reimplementují k návratu smysluplné hodnoty.
Když záložku předáme GotoBookmark, pak kurzor datové množiny je přesunut na místo specifikované v záložce. TDataSet::GotoBookmark je čirá abstraktní metoda. Potomci TDataSet ji smysluplně reimplementují.
FreeBookmark uvolňuje paměť alokovanou pro specifikovanou záložku, když již není zapotřebí. FreeBookmark můžeme také volat před opětovným použitím existující záložky.
Následující kód ukazuje jedno z možných použití záložky:
void DoSomething (const TTable *Tbl)
{
  TBookmark Bookmark=Tbl->GetBookmark();//Alokování paměti a přiřazení hodnoty
  Tbl->DisableControls(); // Vypnutí zobrazování hodnot v datových ovladačích
  try
  {
    Tbl->First(); // Přechod na první záznam v tabulce
    while (!Tbl->Eof) // Procházení všemi záznamy tabulky
    {
      // Naše zpracování záznamů
      ...
      Tbl->Next();
    }
  }
  __finally
  {
    Tbl->GotoBookmark(Bookmark);
    Tbl->EnableControls(); // Zapnutí zobrazování hodnot v datových ovladačích
    Tbl->FreeBookmark(Bookmark); // Dealokace paměti pro záložku
    throw;
  }
}
Před procházením záznamy jsou ovladače zakázány. Pokud v průběhu procházení záznamy vznikne chyba, pak klauzule __finally zajistí, že ovladače jsou povoleny a záložka uvolněna a to i při předčasném ukončení cyklu.

Prohledávání datové množiny

V libovolné datové množině můžeme hledat určitý záznam pomocí obecných prohledávacích metod Locate a Lookup. Tyto metody umožňují hledání v libovolném typu sloupce v libovolné datové množině. Budou popsány v těchto bodech:
Používání Locate
Locate přesouvá kurzor na první řádek odpovídající specifikovaným vyhledávacím kritériím. V nejjednodušším tvaru, předáme Locate jméno prohledávaného sloupce, hledanou hodnotu a příznaky voleb určující zda při hledání rozlišovat velikost písmen nebo zda používat částečné vyhledávání. Např. následující kód přesouvá kurzor na první řádek, kde hodnota ve sloupci Company je Professional Divers, Ltd.:
TLocateOptions SearchOptions;
SearchOptions.Clear();
SearchOptions << loPartialKey;
bool LocateSuccess=CustTable->Locate("Company","Professional Divers, Ltd.",
                                     SearchOptions);
Jestliže Locate nalezne vyhovující záznam, pak tento záznam se stane současným a Locate vrací true. Není-li vyhovující záznam nalezen, pak Locate vrací false a současný záznam se nemění.
Locate můžeme použít i pro hledání ve více sloupcích a můžeme specifikovat více hodnot pro hledání. Hledané hodnoty jsou variantami (datový typ umožňující používání libovolného datového typu), což umožňuje specifikovat ve vyhledávacích kritériích různé datové typy. Při specifikaci více sloupců ve vyhledávacím řetězci, oddělujeme jednotlivé prvky v řetězci středníky. Protože vyhledávané hodnoty jsou variantami, pak při předávání více hodnot musíme jako parametr předat pole typu variant (např. hodnoty vrácené metodou Lookup) nebo musíme vytvořit pole variant pomocí funkce VarArrayOf.
Následující kód ukazuje hledání ve více sloupcích pomocí více vyhledávaných hodnot a použití vyhledávání typu částečného klíče:
TLocateOptions Opts;
Opts.Clear();
Opts << loPartialKey;
Variant locvalues[2];
locvalues[0] = Variant("Sight Diver");
locvalues[1] = Variant("P");
CustTable->Locate("Company;Contact", VarArrayOf(locvalues, 1), Opts);
Locate používá nejrychlejší možnou metodu k lokalizaci hledaného záznamu. Pokud prohledávaný sloupec je indexován a index je kompatibilní s námi určenou volbou hledání, pak Locate použije index.
Používání Lookup
Lookup hledá první řádek, který vyhovuje specifikovaným vyhledávacím kritériím. Při nalezení řádku, jsou provedena přepočítání všech počitatelných položek a vyhledávacích položek přiřazených k datové množině a potom metoda vrátí jednu nebo více položek z nalezeného řádku. Lookup nepřesouvá kurzor na nalezený řádek, pouze z něj vrací hodnoty.
V nejjednodušší formě, předáme Lookup jméno prohledávaného sloupce, hledanou hodnotu a vracenou položku nebo položky. Např. následující kód hledá první řádek v CustTable, kde ve sloupci Company je hodnota Professional Divers, Ltd. a vrací jméno podniku, kontaktní osobu a telefonní číslo:
Variant LookupResults = CustTable->Lookup("Company",
        "Professional Divers, Ltd", "Company;Contact;Phone");
Lookup vrací hodnoty specifikovaných položek z prvního vyhovujícího záznamu. Hodnoty jsou vraceny jako varianty. Jestliže je požadováno vracení více hodnot, pak Lookup vrací pole typu Variant. Pokud žádný záznam nevyhovuje, pak Lookup vrací prázdnou variantu. Více informací o polích variant naleznete v nápovědě.
Lookup také umožňuje hledání ve více sloupcích a specifikovat více hodnot pro hledání. Ve specifikaci řetězců obsahujících více sloupců nebo výsledkových položek, oddělujeme jednotlivé položky středníky. Protože hledané hodnoty jsou variantami, pak při předávání více hodnot, musíme předat jako parametr pole typu Variant (např. vrácené hodnoty z metody Lookup) nebo musíme vytvořit pole variant pomocí funkce VarArrayOf. Následující kód ukazuje hledání ve více sloupcích:
Variant LookupResults;
Variant locvalues[2];
Variant v;
locvalues[0] = Variant("Sight Diver");
locvalues[1] = Variant("Kato Paphos");
LookupResults = CustTable->Lookup("Company;City", VarArrayOf(locvalues, 1),
                "Company;Addr1;Addr2;State;Zip");
// výsledek vložíme do globálního seznamu řetězců (vytvořeného někde jinde)
pFieldValues->Clear();
for (int i = 0; i < 5; i++) // Lookup požadoval 5 položek
{
  v = LookupResults.GetElement(i);
  if (v.IsNull())
    pFieldValues->Add("");
  else
    pFieldValues->Add(v);
}
Lookup používá nejrychlejší možnou metodu k nalezení vyhovujícího záznamu. Jestliže prohledávané sloupce jsou indexovány, pak Lookup index použije.

Zobrazování a editace podmnožiny dat pomocí filtrů

Aplikaci často zajímá pouze podmnožina záznamů v datové množině. Např. nás mohou zajímat pouze záznamy obsahující jistou množinu hodnot položek. V těchto případech můžeme použít filtry k omezení přístupu aplikace k podmnožině všech záznamů v datové množině.
Filtr specifikuje podmínku, kterou záznam musí splňovat, aby byl zobrazen. Podmínka filtru může být zadána ve vlastnosti Filter datové množiny nebo zakódována v její obsluze události OnFilteredRecord. Podmínky filtru jsou založeny na hodnotách v několika specifikovaných položkách v datové množině a to bez ohledu na to, zda tyto položky jsou indexovány. Např. k zobrazení pouze těch záznamů, které se týkají Kalifornie, můžeme použít filtr, kde budeme ve sloupci State požadovat hodnotu "CA".
Poznámka: Filtry jsou aplikovány na všechny záznamy získané z datové množiny. Když chceme filtrovat velké množství dat, pak je výhodnější použít k omezení získaných záznamů dotaz nebo nastavit rozsah pro indexovanou tabulku.
V této sekci jsou uvedeny následující body:
Povolení a zakazování filtrování
Povolování filtrování datové množiny je tříkrokový proces:
  1. Vytvoříme filtr.
  2. Nastavíme volby filtru pro testování založeném na řetězcích (je-li potřeba).
  3. Nastavíme vlastnost Filtered na true.
Když je filtrování povoleno, pak aplikace získává pouze ty záznamy, které splňují filtrovací kriteria. Filtrování je vždy dočasný proces. Můžeme jej vypnout nastavením vlastnosti Filtered na false.

Vytvoření filtru
Jsou dva způsoby vytváření filtrů pro datovou množinu:

Specifikujeme podmínku ve vlastnosti Filter
Hlavní výhodou vytváření filtrů pomocí vlastnosti Filter je to, že naše aplikace může vytvářet, měnit a aplikovat filtr dynamicky (např. v závislosti na vstupu uživatele). Hlavní nevýhodou je to, že filtrovací podmínka musí být vyjádřena jako jednoduchý textový řetězec, nemůže používat konstrukce větvení a cyklů a nemůže testovat nebo porovnávat své hodnoty s hodnotami, které nejsou v datové množině.
Výhodou události OnFilterRecord je to, že filtr může být složitý a proměnný, může být vytvořen více řádky kódu používající větvení a cykly a může testovat hodnoty datové množiny s hodnotami mimo datovou množinu (např. textem v editačním ovladači). Nevýhodou je to, že filtr je vytvořen během návrhu a nemůže být modifikován v závislosti na vstupu uživatele. Můžeme ale vytvořit několik obsluh událostí a přepínat se mezi nimi.
K vytvoření filtru pomocí vlastnosti Filter, nastavíme hodnotu této vlastnosti na řetězec obsahující filtrovací podmínku. Např. následující příkaz vytváří filtr, který testuje položku State datové množiny na hodnotu pro stát Kalifornie:
Dataset1->Filter = "State = 'CA'";
Můžeme také dodat hodnotu pro Filter na základě textu zadaného v ovladači. Např. následující příkaz přiřazuje vlastnosti Filter text z editačního ovladače:
Dataset1->Filter = Edit1->Text;
Řetězec můžeme také vytvořit z pevného textu a dat zadaných uživatelem v ovladači:
Dataset1->Filter = AnsiString("State = '") + Edit1->Text + "'";
Po specifikaci hodnoty pro Filter, aplikujeme filtr na datovou množinu nastavením vlastnosti Filtered na true. Můžeme porovnávat hodnoty položek s konstantami a používat následující operátory: < > >= <= = <> AND NOT OR. Pomocí kombinace těchto operátorů, můžeme vytvářet složitější filtry. Např. následující podmínka používá operátor AND:
(Custno > 1400) AND (Custno < 1500);
Poznámka: Při zapnutém filtrování, editace záznamů může způsobit, že záznam přestává splňovat podmínku filtru. Tento záznam tedy již není dále získáván z datové množiny. Pokud toto nastane, pak současným záznamem se stane další záznam splňující podmínku filtru.

Zápis obsluhy události OnFilterRecord
Filtr pro datovou množinu může být také obsluha události OnFilterRecord generovaná pro každý záznam získaný z datové množiny. Jádrem každé obsluhy filtru je test, který určuje zda záznam bude zařazen mezi ty, které jsou viditelné aplikací.
K indikaci, zda záznam splňuje podmínku filtru, naše obsluha filtru musí nastavit parametr Accept na true k akceptování záznamu, nebo na false k odmítnutí záznamu. Např. následující filtr propouští pouze ty záznamy, kde položka State má hodnotu "CA".
void __fastcall TForm1::Table1FilterRecord(TDataSet *DataSet; bool &Accept)
{
  Accept = DataSet->FieldByName["State"]->AsString == "CA";
}
Když filtrování je povoleno, pak událost OnFilterRecord je generována pro každý získaný záznam. Obsluha události testuje každý záznam a pouze ty, které splňují podmínku filtru jsou propouštěny. Protože událost OnFilterRecord je generována pro každý záznam v datové množině, musíme se snažit, aby kód obsluhy byl co nejkratší aby nebyla snížena výkonnost aplikace.
Můžeme kódovat několik obsluh událostí filtrů a přepínat se mezi nimi za běhu. K přepnutí na jinou obsluhu události za běhu, přiřadíme novou obsluhu události vlastnosti OnFilterRecord datové množiny. Např. následující příkaz přepne OnFilterRecord na obsluhu události nazvanou NewYorkFilter:
DataSet1->OnFilterRecord = NewYorkFilter;
Refresh;

Nastavování voleb filtru
Vlastnost FilterOptions umožňuje specifikovat zda se bude provádět úplné porovnávání řetězců a zda se bude rozlišovat při porovnávání velikost písmen. FilterOptions je množina, která může být prázdná (implicitně) nebo může obsahovat jednu nebo obě z následujících hodnot:
 
Hodnota Význam
foCaseInsensitive  Při porovnávání řetězců je ignorována velikost písmen.
foPartialCompare  Částečné porovnávání řetězců (řetězec končící hvězdičkou).

Např. následující příkaz nastavuje filtr, který ignoruje velikost písmen při porovnávání hodnot v položce State:
TFilterOptions FilterOptions;
FilterOptions.Clear();
FilterOptions << foCaseInsensitive;
Table1->FilterOptions = FilterOptions;
Table1->Filter = "State = 'CA'";

Procházení záznamy ve filtrované datové množině
Jsou čtyři metody datových množin, které umožňují procházet záznamy ve filtrované datové množině. Tyto metody jsou popsány v následující tabulce.
 
Metoda Činnost
FindFirst Přesun na první záznam v datové množině, který splňuje zadaná filtrovací kritéria. Hledání vždy začíná prvním záznamem v nefiltrované datové množině.
FindLast Přesun na poslední záznam v datové množině, který splňuje zadaná filtrovací kritéria.
FindNext Přesun na následující záznam v datové množině, který splňuje zadaná filtrovací kritéria.
FindPrior Přesun na předchozí záznam v datové množině, který splňuje zadaná filtrovací kritéria.

Např. následující příkaz nalezne první vyhovující záznam v datové množině:
DataSet1->FindFirst();
Pokud máme nastavenu vlastnost Filter nebo vytvořenou obsluhu události OnFilterRecord pro naši datovou množinu, pak tyto metody přemístí kurzor na specifikovaný záznam a to bez ohledu na to, zda filtrování je povoleno. Když voláme tyto metody a filtrování není povoleno, pak filtrování je dočasně povoleno, kurzor umístěn na odpovídající záznam a filtrování je zakázáno.
Poznámka: Jestliže vlastnost Filter není nastavena a není vytvořena obsluha události OnFilterRecord, pak tyto metody pracují stejně jako metody First, Last, Next a Prior.
Všechny tyto metody umístí kurzor na vyhovující záznam (pokud existuje) a vracejí true. Pokud vyhovující záznam neexistuje, pak pozice kurzoru se nemění a metoda vrací false. Např. pokud kurzor je na posledním vyhovujícím záznamu v datové množině a voláme FindNext, pak metoda vrací false a současný záznam se nemění.

Modifikování dat

Můžeme používat následující metody datové množiny pro vkládání, aktualizaci a rušení dat:
 
Metoda Popis
Edit Uvádí datovou množinu do stavu dsEdit, pokud již není ve stavu dsEdit nebo dsInsert.
Append Zapisuje nezapsaná data, přesouvá kurzor na konec datové množiny a převádí datovou množinu do stavu dsInsert.
Insert Zapisuje nezapsaná data a převádí datovou množinu do stavu dsInsert.
Post Pokusí se zapsat nový nebo změněný záznam do databáze. V případě úspěchu vrací datovou množinu do stavu dsBrowse, jinak se stav nemění.
Cancel Ukončuje současnou operaci a vrací datovou množinu do stavu dsBrowse.
Delete Ruší současný záznam a převádí datovou množinu do stavu dsBrowse.

Tyto metody jsou podrobněji popsány v následujících bodech:

Editace záznamů
Aby aplikace mohla modifikovat záznamy, musí být datová množina ve stavu dsEdit. V našem kódu můžeme použít metodu Edit k uvedení datové množiny do stavu dsEdit, pokud vlastnost CanModify datové množiny je true. CanModify je true, pokud k tabulce spojené s datovou množinou máme čtecí a zápisová privilegia. Jak jsme se již dozvěděli, datová množina při splnění jistých podmínek může do stavu dsEdit přejít automaticky.
Po uvedení datové množiny do stavu dsEdit uživatel může modifikovat hodnoty položek současného záznamu, které jsou zobrazeny v datových ovladačích na formuláři. Datové ovladače pro které je automaticky povolená editace, volají Post, když uživatel provede nějakou akci měnící současný záznam (přesun na jiný záznam). Pokud náš formulář obsahuje navigační komponentu, pak uživatel může zrušit provedené změny stisknutím tlačítka Cancel navigátora. Tím přejde datová množina do stavu dsBrowse.
V kódu musíme zapsat nebo zrušit provedené změny voláním příslušných metod. Změny zapisujeme voláním Post a rušíme je voláním Cancel. Edit a Post jsou často používány společně. Např.
Table1->Edit();
Table1->FieldValues["CustNo"] = 1234;
Table1->Post();
V předchozím příkladě, první řádek kódu uvádí datovou množinu do stavu dsEdit. Další řádek kódu přiřazuje číslo 1234 položce CustNo současného záznamu. Poslední řádek zapisuje (odešle) modifikovaný záznam do databáze.
Poznámka: Pokud vlastnost CachedUpdates datové množiny je true, pak odesílané modifikace jsou zapsány do dočasné vyrovnávací paměti. K jejich zápisu do databáze voláme metodu ApplyUpdates datové množiny.
Přidávání nových záznamů
Dříve než aplikace může k datové množině přidat další záznam, musíme ji uvést do stavu dsInsert. Pro uvedení datové množiny do stavu dsInsert voláme v kódu metodu Insert nebo Append, pokud vlastnost CanModify datové množiny je nastavena na true. CanModify je true, pokud k databázi spojené s datovou množinou máme čtecí a zápisová privilegia. Datová množina při splnění určitých podmínek může být do stavu dsInsert převedena automaticky.
Po uvedení datové množiny do stavu dsInsert, uživatel nebo aplikace může zadávat hodnoty do položek nového záznamu. Mimo místa uložení není rozdíl mezi metodami Insert a Append. Insert používáme pro vložení nového řádku před současný záznam a Append pro vložení nového řádku za poslední záznam datové množiny.
Datové ovladače, pro které je povoleno vkládání, automaticky volají Post, když uživatel provede nějakou akci, která mění současný záznam (přesun na jiný záznam). Jinak musíme volat Post ve svém kódu. Post zapisuje nový záznam do databáze nebo při CachedUpdates nastaveným na true, do dočasné vyrovnávací paměti (pro zápis do databáze je nutno ještě volat ApplyUpdates).
Insert otevírá nový prázdný záznam před současným záznamem a tento prázdný záznam se stává současným záznamem a tedy hodnoty položek tohoto záznamu mohou být zadávány uživatelem nebo kódem naší aplikace.
Když aplikace volá Post (nebo ApplyUpdates při povolených odložených aktualizacích), nově vkládaný záznam je zapsán do databáze jedním z těchto způsobů: Append otevírá nový prázdný záznam na konci datové množiny. Jinak je činnost stejná jako při Insert (při zápisu do neindexované tabulky je záznam vložen na konec datové množiny).
Rušení záznamů
Aby aplikace mohla zrušit záznam, musí být datová množina aktivní. Delete ruší aktivní záznam v datové množině a převádí ji do stavu dsBrowse. Záznam, který následuje za zrušeným záznamem se stává současným záznamem. Pokud jsou povoleny odložené aktualizace, pak zrušený záznam je pouze odstraněn z dočasné vyrovnávací paměti, dokud nevoláme ApplyUpdates.
Pokud na svém formuláři používáme navigační komponentu, pak uživatel může zrušit současný záznam stisknutím tlačítka Delete navigátora. V kódu musíme explicitně volat k odstranění současného záznamu metodu Delete.
Odesílání dat do databáze
Metoda Post centralizuje v aplikacích C++ Builderu interakce s databázovou tabulkou. Post zapisuje změny současného záznamu do databáze, ale chování se liší v závislosti na stavu datové množiny: Odeslání může být prováděno explicitně nebo implicitně jako část jiné procedury. Když aplikace přesouvá současný záznam, pak Post je voláno implicitně. Volání metod First, Next, Prior a Last provádí Post, pokud datová množina je ve stavu dsEdit nebo dsInsert. Metody Append a Insert také implicitně odesílají všechna nevyřízená data.
Poznámka: Metoda Close nevolá Post implicitně. Použijeme obsluhu události BeforeClose k odeslání všech nevyřízených aktualizací.
Zrušení změn
Aplikace může zrušit změny provedené na současném záznamu, pokud ještě nebyly zapsány pomocí Post. Např. pokud datová množina je ve stavu dsEdit a uživatel změnil data jedné nebo více položek, pak aplikace se může vrátit k původním hodnotám záznamu voláním metody Cancel datové množiny. Volání Cancel vždy vrací datovou množinu do stavu dsBrowse. Pokud na formuláři máme umístěnu navigační komponentu, pak můžeme pro zrušení změn připojené datové množiny stisknout tlačítko Cancel této komponenty.
Modifikace celých záznamů
Všechny datové ovladače na formuláři mimo mřížky a navigační komponenty poskytují přístup k jednotlivým položkám v záznamu. V kódu ale můžeme používat následující metody, které pracují s celými záznamy a to za předpokladu, že struktura databázových tabulek připojené datové množiny je stabilní a nemění se.
 
Metoda Popis
AppendRecord([pole hodnot])  Předává záznam se specifikovanými hodnotami sloupců na konec tabulky. Provádí implicitně Post.
InsertRecord([pole hodnot])  Vkládá specifikované hodnoty jako záznam před současnou pozici kurzoru tabulky. Provádí implicitně Post.
SetFields([pole hodnot])  Nastavuje hodnoty specifikovaných položek (odpovídá přiřazení hodnot do TFields). Aplikace musí provést explicitně Post.

Tyto metody přebírají pole hodnot TVarRec jako parametr, kde každá hodnota odpovídá sloupci v připojené datové množině. K vyplnění těchto polí použijeme makro ARRAYOFCONST. Hodnoty mohou být konstanty, proměnné nebo NULL. Pokud počet hodnot v parametru je menší než počet sloupců v datové množině, pak zbývajícím sloupcům je přiřazeno NULL.
Pro neindexované datové množiny, AppendRecord přidává záznam na konec datové množiny a InsertRecord vkládá záznam před současný záznam. Pro indexované tabulky, obě metody umisťují záznam do tabulky na základě indexu. V obou případech metody přemístí kurzor na vložený záznam.
SetFields přiřazuje hodnoty specifikované v poli parametrů do položek v datové množině. Pro použití SetFields, aplikace musí nejprve volat Edit k uvedení datové množiny do stavu dsEdit. Aplikování změn na současný záznam pak provedeme Post.
Jestliže použijeme SetFields k modifikaci některých, ale ne všech položek v existujícím záznamu, pak můžeme předat hodnotu NULL pro položky, které nechceme měnit. Pokud nepředáme dostatek hodnot pro všechny položky v záznamu, pak SetFields přiřadí hodnoty NULL pro zbývající položky (tyto NULL přepíší existující hodnoty v těchto položkách).
Např. předpokládejme, že v databázi je tabulka COUNTRY se sloupci Name, Capital, Continent, Area a Population. Pokud komponenta TTable nazvaná CountryTable je spojená s touto tabulkou, pak následující příkaz vloží záznam do tabulky COUNTRY:
CountryTable->InsertRecord(ARRAYOFCONST(("Japan", "Tokyo", "Asia")));
Tento příkaz neobsahuje hodnoty pro sloupce Area a Population a jsou tedy do nich vloženy hodnoty NULL. Tabulka je indexována podle sloupce Name a příkaz tedy vloží záznam na místo na základě abecedního umístění "Japan". K aktualizaci záznamu, aplikace může použít tento kód:
TLocateOptions SearchOptions;
SearchOptions.Clear();
SearchOptions << loCaseInsensitive;
if (CountryTable->Locate("Name", "Japan", SearchOptions))
{
  CountryTable->Edit();
  CountryTable->SetFields(ARRAYOFCONST(
     ((void *)NULL, (void *)NULL, (void *)NULL, 344567, 164700000)));
  CountryTable->Post();
}
Tento kód přiřazuje hodnoty položek Area a Population a potom je odesílá do databáze. Tři ukazatelé na NULL drží místo pro první tři sloupce k zabránění změně jejího obsahu (musíme provést přetypování na void *).
Varování: Pokud použijeme NULL bez přetypování, pak položku nastavíme na prázdnou hodnotu.

Používání událostí datových množin

Datové množiny mají několik událostí, které umožňují aplikaci provádět kontroly, výpočty součtů a další úlohy. Tyto události jsou uvedeny v následující tabulce:
 
Událost Popis
BeforeOpen, AfterOpen Je generována před/po otevření datové množiny.
BeforeClose, AfterClose Je generována před/po uzavření datové množiny.
BeforeInsert, AfterInsert Je generována před/po uvedení datové množiny do stavu dsInsert.
BeforeEdit, AfterEdit Je generována před/po uvedení datové množiny do stavu dsEdit.
BeforePost, AfterPost Je generována před/po odeslání změn.
BeforeCancel, AfterCancel Je generována před/po zrušení provedených změn.
BeforeDelete, AfterDelete Je generována před/po zrušení záznamu.
OnNewRecord Je generována při vytvoření nového záznamu; používá se k nastavení implicitních hodnot.
OnCalcFields Je generována, když počitatelné položky jsou počítány.

Pro zrušení provádění metod, jako je např. Open nebo Insert, voláme funkci Abort v některé metodě Before... (BeforeOpen, BeforeInsert apod.). Např. následující kód vyžaduje potvrzení uživatele před zrušením záznamu.
void __fastcall TForm1::TableBeforeDelete (TDataSet *Dataset)
{
  if (MessageBox(0, "Delete This Record?", "CONFIRM", MB_YESNO) != IDYES)
   Abort();
}

Použití OnCalcFields
Událost OnCalcFields je používána k nastavování hodnot počitatelných položek. Vlastnost AutoCalcFields určuje zda je generována událost OnCalcFields. Jestliže hodnota této vlastnosti je true, pak OnCalcFields je generována když: OnCalcFields je vždy generována při změně hodnoty některé nepočitatelné položky a to bez ohledu na nastavení AutoCalcFields.
Varování: Jelikož OnCalcFields je volána velmi často, je vhodné, aby obsluha této události byla krátká. Pokud AutoCalcFields je true, pak OnCalcFields by neměla provádět akce modifikující datovou množinu neboť to může vést k rekurzi (pokud OnCalcFields volá Post, pak je generována OnCalcFields). Když AutoCalcFields je false, pak OnCalcFields je generována pouze při volání Post.
Při provádění OnCalcFields je datová množina ve stavu dsCalcFields a nemohou být tedy měněny hodnoty jiných než počitatelných položek. Po dokončení OnCalcFields se datová množina vrátí do stavu dsBrowse.

  1. Další aplikace, kterou vytvoříme, bude zobrazovat informace o našich podmořských přátelích a umožní ukládat tyto informace do textového souboru. Aplikace bude používat již vytvořený databázový soubor BIOLIFE.DB. Ukážeme si v ní použití komponent TDBGrid, TDBText, TDBImage, TDataSource, TTable a TDBMemo, procházení daty pomocí mřížky, extrakci dat z datového ovladače a zápis do textového souboru.

  2. Začneme vývoj nové aplikace. Na formulář umístíme komponentu panel, jejíž výšku nastavíme asi 72 bobů a zarovnáme ji se spodním okrajem formuláře (Align nastavíme na alBottom). Na formulář přidáme další panel, který tentokrát bude zabírat levou polovinu zbývající části formuláře. Do spodní části tohoto panelu přidáme komponentu TDBText (použijeme červené písmo, tučné a kurzívou velikosti 14) a na jeho zbývající plochu komponentu DBImage. Na zbývající plochu formuláře umístíme komponentu TDBMemo. Data z databáze budeme získávat komponentou TTable. Umístíme ji na formulář a nastavíme u ní vlastnost DatabaseName na BCDEMOS, vlastnost TableName na BIOLIFE a vlastnost Active na true. Dále je nutno na formulář umístit komponentu TDataSource a nastavíme u ní vlastnost DataSet na Table1. Na panel umístěný u spodního okraje formuláře na pravém okraji nad sebe dvě tlačítka BitBtn. Spodní bude mít nastavenu vlastnost Kind na bkClose (bude sloužit k ukončení aplikace) a horní bude mít tuto vlastnost nastavenu na bkCustom (bude sloužit k zápisu dat z ovladače do souboru; jeho text změníme na Save a vložíme na něj vhodný obrázek). Zbytek plochy spodního panelu zaplníme komponentou TDBGrid (její výsku nastavíme tak, aby mimo hlavičky zde byl zobrazen pouze jeden řádek).
    Vlastnosti DataSource u komponent TDBGrid, TDBText, TDBImage a TDBMemo nastavíme na DataSource1 a vlastnosti DataField u TDBText na Common_Name, u TDBImage na Graphic a u TDBMemo na Notes. Nyní již aplikaci můžeme přeložit a spustit. Rolováním mřížky procházíme záznamy tabulky. Mřížka obsahuje zbytečné sloupce. Později si ukážeme jak je nezobrazovat a jak umožnit definovat způsob zobrazení.
  3. Zatím jsme se nezabývali ukládáním dat z ovladače do souboru. Na formulář umístíme komponentu TSaveDialog a nastavíme u ní vlastnosti DefaultExt na .txt, FileName na Fish.txt a Filter na Textový soubor (*.txt). Obsluha stisknutí tlačítka Save bude tvořena příkazy:

  4. FILE *outfile;
    char buff[100];
    sprintf(buff, "Save Info For: %s", DBText1->Field->AsString.c_str());
    SaveDialog1->Title = buff;
    if (SaveDialog1->Execute()){
      outfile = fopen(SaveDialog1->FileName.c_str(), "wt");
      if (outfile) {
        fprintf(outfile, "Facts on the %s\n\n",
                (LPSTR)DBText1->Field->AsString.c_str());
        for (int i=0; i < DBGrid1->FieldCount; i++)
          fprintf(outfile, "%s: %s\n",
                  (LPSTR)DBGrid1->Fields[i]->FieldName.c_str(),
                  (LPSTR)DBGrid1->Fields[i]->AsString.c_str());
        fprintf(outfile, "\n%s\n", (LPSTR)DBMemo1->Text.c_str());
      }
      fclose(outfile);
    }
    Tato obsluha zapíše do textového souboru informace o současné rybě. Podívejte se, jak se tyto informace získávají a zapisují. Tím je vývoj aplikace ukončen. Další příklady budou uvedeny v následující kapitole.
6. Seznámení s datovými množinami