07.02.2020, Vladimír Klaus, navštíveno 1946x

Delphi

Komponenta TcxGrid do Developer Express je neuvěřitelně mocná, ale také velmi složitá. Používám ji v podstatě ve všech svých Delphi aplikacích už velice dlouho. Přesto, že na řadu věcí jsou návody, tento jednoduchý a zároveň komplexní mi chyběl, tak jsem ho sestavil a trochu okomentoval.

Jak pracovat se záznamy v mřížce TcxGrid od Developer Express, obr. 1

Doporučuji přečíst všechny body a až podle konkrétní situace se rozhodnout pro nejvhodnější postup, případně přístupy kombinovat.

Článek bude postupně doplňován a aktualizován dle využití dalších komponent a situací, na které při programování narazím.

Většina operací níže uvedených je založena na fokusování dané řádky a dále na tom, že mřížka je synchronizována s daty. Tedy, že fokusování řádky znamená, že se i v DB tabulce nastaví aktuální záznam odpovídající mřížce, a pak bude možné s ním "datově" pracovat. Aby toto fungovalo, musí být splněno:

//nastavení synchronizace mřížky a databáze
DataController.DataModeController.SyncMode=true;

//A hlavně se NESMÍ použít toto, protože pak fokusování nefunguje!!!
//--- TableFirmy.DisableControls;

Pokud jste zvyklí, že se mřížka během operací neaktualizuje (a tudíž probíhají operace velmi rychle), můžete zde zvolit jinou metodu:

// --- Vypnutí a zapnutí interakce mřížky ---
FirmyGridView.BeginUpdate(lsimImmediate);
FirmyGridView.EndUpdate(lsimImmediate);

1. Všechny záznamy z DataSetu

Pokud potřebujete přistupovat ke všem záznamům, je zbytečné to řešit přes mřížku (nemusí aktuálně zobrazovat všechny záznamy).

while not TableFirmy.Eof do begin
  TableFirmy.Next;
end;

Ale pochopitelně je možné, že nevíte, odkud jsou data zobrazená v mřížce nebo zkrátka neexistuje přímý přístup, pak je možné se odkázat na datový zdroj takto:

FirmyGridView.DataController.DataSet
FirmyGridView.DataController.DataSource.DataSet

2. Označené/vybrané záznamy v mřížce

Tímto "fokusováním" se vlastně přesunu na záznam přímo v Datasetu a mohu s ním normálně pracovat, jako by to s mřížkou nijak nesouviselo.

PocetOznacenych:=FirmyGridView.Controller.SelectedRecordCount;
for i:=0 to PocetOznacenych-1 do begin
  FirmyGridView.Controller.SelectedRecords[i].Focused:=true;
end;

3. Všechny zobrazené záznamy z mřížky (respektuje se i filtr)

Je to podobné, jako u předchozího případu - je třeba postupně jeden po druhém zobrazený záznam "fokusovat". Jen se to bohužel dělá jinak.

Přesto, že se pracuje s filtrovanými záznamy, tak filtr vůbec nemusí být aplikován. A tato metoda si korektně poradí i s tím, když je v mřížce použito seskupení.

PocetZobrazenych:=FirmyGridView.DataController.FilteredRecordCount;
for i:=0 to PocetZobrazenych-1 do begin
  ARecIndex:=FirmyGridView.DataController.FilteredRecordIndex[i];
  FirmyGridView.DataController.FocusedRecordIndex:=ARecIndex;
end;

Určitě se vyhněte tomuto přístupu, protože to sice funguje, ale při použití seskupení už ne - přistupuje se opravdu k zobrazeným záznamům, ne k těm, co mohou být schovány v zabalené skupině!

// Nepoužívat!!!
//--- FirmyGridView.ViewData.Records[ARecIndex].Focused:=true;

4. Aktuální záznam

Aktuální záznam může být fokusovaný, ale nemusí být vybraný. Proto se to tímto trikem zajistí a pak se už může pokračovat podle bodu 1. Samozřejmě musíte hlídat, zda v té mřížce vůbec něco je zobrazeno.

if FirmyGridView.Controller.SelectedRecordCount=0
  then FirmyGridView.Controller.FocusedRecord.Selected:=true

5. Odstraňování či změna záznamů

Zcela specifickou kapitolou je pak odstraňování záznamů, protože se tím může měnit počet záznamů a může tak lehce dojít k odkazování na již neexistující. Zde je proto naopak velmi důležité použít DisableControls a pak totiž i FilteredRecordCount zachová původní hodnotu (před počátkem mazání) a je tedy bezpečné se na dané záznamy odkazovat.

Pokud je mřížka filtrována dle nějakého pole a toto pole se nastavuje, tak opět dochází k výše uvedenému problému a DisableControls je nutné použít. Bez použití dochází k chybě "List index out of bounds", protože změněné záznamy mizí po rukama a neodpovídá pak zjištěný počet apod.

Také se při odstraňování může hodit zachovat zobrazení, k čemuž mohou pomoci tyto informace.

//co je v mřížce jako první záznam
TopRow:=FirmyViewSimple.Controller.TopRowIndex;
//co je fokusováno
FocusedRow:=FirmyViewSimple.DataController.FocusedRowIndex;

Také bývá potřeba nastavit správný klíč...

//nastavím klíč k hledáni - důležité, jinak nefunguje GetRecordId
OldKeyFieldNames:=DataController.KeyFieldNames;
DataController.KeyFieldNames:='Id';

...tedy především v situaci, že využijete:

var AKey: Variant;
AKey:=DataController.GetRecordId(i)
AKey:=DataController.GetRecordId(DataController.FilteredRecordIndex[i])

Řada dalších specifik se pak pojí s TcxGridServerModeTableView...

6. SmartRefresh

Pokud v mřížce máte více záznamů a zapnete si SmartRefresh, je nutné pro opravdu rychlejší práci mít nastaven také KeyFieldNames. Když není, SmartRefresh se nebrání, ale funguje to stejně (pomalu), jako kdyby byl vypnutý.

Další velmi důležitá věc je způsob, jakým z kódu přidáváte záznamy. Pokud totiž použijete něco jako Table.Append, tak se záznam sice založí, můžete mu vyplnit údaje, ale po potvrzení z mřížky zmizí! Pro jeho objevení je pak třeba zavolat Refresh, což není rozhodně ideální a ztrácí se tím výhoda vyšší rychlosti.

Z nápovědy je pak patrné, že jediný správný způsob je vkládání záznamů dělat buď ručně (tlačítkem v liště Navigatoru) nebo tedy z kódu, ale přes metodu DataControlleru.

//dobře:
KatalogDiluGridView.DataController.Append;
//...
KatalogDiluGridView.DataController.Post; //nebo Cancel

//špatně:
//--- QueryKatalogDilu.Append;
//...
//--- QueryKatalogDilu.Post/Cancel;

A samozřejmě úplně stejný problém je při volání Query.Refresh - také to nic nedělá a buď se tedy musí vypnout SmartRefresh a nebo to řešit přes Close/Open a přelisování na Bookmark.