Formuláře - co, kdo, proč a jak

(malý tutorial)
...formulář je okno ve kterém se nacházejí ovládací prvky a slouží k interakci s uživatelem...

Základ

Na formulář můžeme nahlížet buď jako na objekt nebo jako na třídu. Objektem je myšlen formulář uložený v souboru SCX (příklad example1a.txt). Třídou je myšlena třída formuláře uložená v souboru VCX - interaktivní návrh nebo PRG - programový návrh (příklad example1b.txt). Rozdíl mezi třídou a objektem je, že třída automaticky nezahrnuje do své definice objekt DataEnvironment.

Modální a nemodální

Formulář má dvoje základní chování: modální a nemodální.
modální (modal)
Jeli zobrazen (ne spuštěn) modální formulář, pak se VFP automaticky zastaví a nepokračuje dále dokud takovýto formnulář není ukončen či skryt. Tento typ formulářů je vhodný na různá dialogová okna jako jsou nastavení možností programu, zadávání parametrů atd.
nemodální (modeless)
Jeli zobrazen nemodální formulář VFP dále pokračuje ve zpracování programu dokud nenarazí na konec programového kódu nebo dokud nenarazí na příkaz READ EVENTS. Tento příkaz způsobí zastavení VFP a čeká zde tak dlouho, dokud tento příkaz není zrušen pomocí příkazu CLEAR EVENTS.

Rodič formuláře

Formulář jako takový je zobrazen vždy v hlavním okně VFP. Nicméně pomocí vlastnosti ShowWindow lze toto chování změnit.
0
Zobrazí vždy formulář v hlavním okně VFP.
1
Formulář se zobrazí v aktivním formuláři který je "As Top Level". Pokud takový formulář není aktivní, zobrazí se v hlavním okně VFP.
2
Zobrazí formulář mimo hlavní okno VFP, rodičem je tedy samotná plocha Windows.

Sada formulářů

Soubor SCX nebo třída ve VCX nemusí obsahovat jen jeden formulář, ale celou sadu formulářů združených do objektu zvaného formset (příklad example1c.txt). Taktéž může být součástí takovéto definice i jeden či více objektů třídy toolbar (příklad example1d.txt).
Formset se vytvoří tak, že se během návrhu:
  • z nabídky "Form" vybere volba "Create FormSet"
  • se z panelu "Form controls" vybere třída jejiž základní třída je toolbar a vloží se přímo na formulář - VFP se potom zeptá, zda se má automaticky vytořit formset

Vytvoření formuláře

Spuštění formulář je závislé na tom, zda je to objekt nebo třída.
* vytvoření formuláře založeného na základní třídě
CREATE FORM test.scx 

* vytvoření formuláře založeného na třídě _form
* uloženého v knihovně example.vcx
CREATE FORM test.scx AS _form FROM example.vcx
 
CREATE CLASS _frmtest OF test.vcx AS form

Spuštění formuláře

Objekt

Formulář se spouští pomocí příkazu DO FORM (příklad example2a.prg).
LOCAL loForm
DO FORM anyform.scx [LINKED] [NAME loForm] [NOSHOW]
bez NAME
VFP automaticky vytvoří proměnnou stejného jména jako je název formuláře.
NAME
Proměnná bude obsahovat odkaz na spuštěný formulář, pokud neexistuje VFP ji sama vytvoří.
LINKED
Formulář bude pevně svázán s proměnnou uvedenou za NAME, tj. při uvolnění proměnné bude uvolněn i formulář pokud neexistuje jiná reference formuláře nebo jeho části.
NOSHOW
Zajistí, že formulář je po spuštění skrytý (hodí se modální formuláře).

Třída

Pro spuštění formuláře coby třídy se používají funkce CREATEOBJECT() nebo NEWOBJECT() (rozdíl je popsaný v helpu, nicméně NEWOBJECT() je asi 500x pomalejší než CREATEOBJECT()), ve formsetu lze využít metod AddObject() a NewObject() (příklad example2b.prg).
LOCAL loObject
loObject=CREATEOBJECT("_frmMy")
*loObject=NEWOBJECT("_frmMy")
Pokud dojde přirovnání CREATEOBJECT() k DO FORM pak tomu odpovídá toto:
DO FORM anyform.scx LINKED NAME loForm NOSHOW

Vychozí reference na spuštěné formuláře (příklad example2c.prg)

VFP má možnost procházet kolekci spuštěných formulářů. Ta to kolekce je na systémovém objektu _Screen. Počet prvků v kolekci je definuje vlastnost FormCount a samotná kolekce má název Forms[]. Tato kolekce také obsahuje i reference na spuštěné panelové nabídky (toolbary).
 
DO FORM example1c
FOR lii=1 TO _Screen.FormCount
    ?_Screen.Forms(lii).BaseClass,_Screen.Forms(lii).Name
NEXT
Tato kolekce je dynamická a obsah se mění i pouhou aktivací formuláře. Obsah může být následovný: Jako první jsou v kolekci panely nabídek od poslední po první spuštěný. Pak následují formuláře od posledně aktivovaného po první.

Pořadí prováděných událostí

Neznalost pořadí prováděných událostí vede k dalšímu matení a nechuti a na vinně je jen vždy programátor...

Co je v nápovědě...

Základní seznam pořadí událostí je v nápovědě v kapitole "Event Sequence in Visual FoxPro" pro formset s jedním formuláře s dvěma objekty a DataEnvironmentem. Od startu až po ukončení.
ObjektUdálost
Data environmentBeforeOpenTables
Form setLoad
FormLoad
Data environment cursor(s)Init
Data environmentInit
Objects (1)Init
FormInit
Form setInit
Form setActivate
FormActivate
Object1 (2)When
FormGotFocus
Object1GotFocus
Object1Message
Object1Valid (3)
Object1LostFocus
Object2 (3)When
Object2GotFocus
Object2Message
Object2Valid (4)
Object2LostFocus
FormQueryUnload
Form SetDestroy
FormDestroy
Object (5)Destroy
FormUnload
Form setUnload
Data environmentAfterCloseTables
Data environmentDestroy
Data environment cursor(s)Destroy
(1) Pro každý objekt, od prvního po posledního (vytvořeného) v konteineru
(2) První objekt dle pořadí uvedeného ve vlastnosti TabOrder
(3) Další objekt získá fokus
(4) Object ztratí fokus
(5) Pro každý objekt, od posledního po prvního (vytvořeného) v konteineru
Pokud chcete vědět jaké události se spouštějí za běhu vaší aplikace, podívejte se do nápovědy na příkaz SET EVENTTRACKING.

Jak z výše uvedené tabulky vyplývá, některé události dokonce probíhají ještě dříve než by se na první pohled zdálo. Proto je nutné rozlišit události mezi DE a ostatními objekty. Pokud se tak stane, je vidět že události DE a jeho objektů se řídí stejnými pravidly jako události ostatních objektů (formset, formulář...) akorát jsou vmíšeny mezi ně.
Dalším problém způsobuje že některé události mají opačný průběh než ostatní... Události jako Load(), Activate(), Destroy() probíhají od nejvýše postaveného objektu ve stromu až po ty nejníže postavené objekty: Formset->Form->Objekt. Kdežto událost Init() probíhá opačným směrem: Objekt->Form->Formset.

Předání parametrů

U formulářů se parametry předávají obdobným způsobem jako u volání procedur a u tříd stejně jako při vytváření objektů. Samotné hodnoty parametrů se přijímají až v události Init() formuláře. Proměnné jsou viditelené pouze v události Init() formuláře/třídy v případě použítí LPARAMETERS. Je-li použito PARAMETERS, jsou proměnné viditelné i ve volaných P/F/M z události Init(). Po opuštění Initu jsou proměnné uvolněny. Proto je vhodné proměnné v události Init() ihned pro přijmutí uložit do příslušných vlastností formuláře/třídy.

Objekt (příklad example3a.prg)

LOCAL loObject
DO FORM anyform.scx WITH 'Hodnota1',2

* form::Init()
LPARAM lcParam1,lnParam2
?lcParam1,lnParam2
This.Param1=lcParam1
This.Param2=lnParam2

Třída (příklad example3b.prg)

LOCAL loObject
loObject=CREATEOBJECT("_frmMy",'Hodnota1',2)
* form::Init()
LPARAM lcParam1,lnParam2
?lcParam1,lnParam2
This.Param1=lcParam1
This.Param2=lnParam2

Využití objektu (příklad example3c.prg)

Různé verze VFP mají různá omezení pro maximální počet parametrů P/F/M/E. Vzhledem k tomu, že VFP interně předává parametry přes registry je tato operace rychlá ale je zároveň omezena maximální počtem registrů které lze využít. Proto, a nejen proto, se doporučuje místo sady parametrů poslat jen jeden a to objekt. Mezi další výhody patří:
LOCAL loObject,loPar
loPar=CREATEOBJECT("_Param")
loPar.Par1='Hodnota1'
loPar.Par2=2
loObject=CREATEOBJECT("_frmMy",loPar)

* form::Init()
LPARAM loPar
?loPar.Par1,loPar.Par2
This.Par=loPar


* definice třídy parametru
DEFINE CLASS _Param AS CUSTOM
 Par1=""
 Par2=0
ENDDEFINE

Přístup na parametry v události Load()

Někdy je však potřeba znát parametry již v události Load(). Je-li volán formulář z formuláře lze využít vlastnosti, že původní formulář je v této chvíli stále aktivní. Krátká ukázka vyšla v Softwarovém Quasu č. 2002/40 (přepis) (příklad example3d.prg). Není li však tato podmínka splněna, lze si vytvořit jednoduchý zásobník na parametry (příklad example3e.prg).

Vrácení hodnoty

Vrácení jedné hodnoty (příklad example4a.prg)

Formulář, ne však třída umožnuje vrátit jednu hodnotu. Tato vlastnost se týká pouze modálních formulářů. Vrácení hodnoty je realizováno příkazem RETURN v události Unload(). Vzhledem k tomu, že v době provádění této události, jsou veškeré objekty formuláře odstraněné, je nutné nejdříve hodnotu zapsat do nějaké vlastnosti formuláře a teprve v události Unload() hodnotu oné vlastnosti vrátit.
Pro vrácení více hodnot je možné využít objekt.
Domnívám se, že poslat objekt jako parametr při spuštění formuláře je výhodnější než ho vytvořit až v cílovém formuláři a pak vrátit v události Unload(). Hlavně proto, že tento způsob nelze použít pro třídy formuláře.
LOCAL loObject,luReturn
DO FORM anyform.scx WITH 'Hodnota1',2 TO luReturn

* form::Release()
This.Value=This.textbox1.Value

* form::QueryUnload()
This.Value=This.textbox1.Value

* form::Unload()
RETURN This.Value

Vrácení více hodnot (příklad example4b.prg)

Pro vrácení více hodnot lze využít objekt. Je to vhodné jak pro formuláře tak třídy formulářů, modální nebo modeless. Objekt se pošle jako parametr formuláři s naplněnýma vstupníma hodnotama a zároveň jeho další vlastnosti slouží pro zpětný přenos nových hodnot.

Ukončení formuláře

Při ukončování formuláře platí vždy jedno pravidlo: Formulář je ukončen jen tehdy, jeli uvolněná poslední reference formuláře a jeho části.

Formulář se dá ukončit několika způsoby:

1) RELEASE WINDOW/ CLEAR WINDOWS/ QUIT
Název okna je dán obsahem vlastnosti Name formuláře (příklad example5a.prg).
2) RELEASE oVariable
Pouze je-li formulář svázán s danou proměnnou (příklad example5b.prg).
3) loForm.Release
Explicitní volaní metody Release() dojde k ukončení formuláře (příklad example5c.prg).
4) KEYB "{CTRL+F4}"/ KEYB "{CTRL+W}"; Kliknutím na ikonu vpravo nahoře; Z nabídky okna, kliknutím na volbu "Close"
Tyto tři akce (klávesnice, tlačítko, nabídka) jsou ekvivaletní (příklad example5d.prg).
Způsob ukončeníHodnota vlastnosti ReleaseTypeProběhlé metody a události
1)1, 2 pro QUITQueryUnload
Destroy
2)0Destroy
3)0Release
Destroy
4)1QueryUnload
Destroy

Přerušení ukončování formuláře (příklad example5e.prg)

Ne vždy je přijatelné ukončit formulář aniž by na to měl uživatel vliv. V případě kdy je nutné se uživatele zeptat, zda souhlasí např. se změnami a to i tehdy když uživatel využije nabídky okna pro uzavření formuláře. Odpovíli uživatel že ne, je nutné zastavit ukončování formuláře. Toto je možné pouze v události QueryUnload() kde použití klíčového slova NODEFAULT způsobí nevykonání výchozího kódu, tj. přeruší se ukončování formuláře. Z výše uvedené tabulky je vidět, že toto chování je možné v případech 1) a 4). Případy 2) a 3) tedy slouží k ukončení formuláře bez uživatelského zásahu.
* událost QueryUnload()
liEnd=MESSAGEBOX("Chcete uložit změny?",3+32,"Example 5E")
DO CASE
   CASE liEnd=6 && Yes
        DEBUGOUT "Save: Ano"

   CASE liEnd=7 && No
        DEBUGOUT "Save: Ne"

   CASE liEnd=2 && Cancel
        DEBUGOUT "Save: Storno"
        NODEFAULT && Tento příkaz zabrání vykonání původní činnosti - VFP nativního kódu

ENDCASE

Datové prostředí/Datová oblast

Datové prostředí, dále jen DE (DataEnvironment), umožňuje soustředit potřebné datové zdroje přímo do formuláře. Je to tedy jen definice jaké tabulky či pohledy se mají otevřít a nemůže být součástí definice třídy formuláře. Samotný objekt DataEnvironment (výchozí název) není v objektovém stromu vidět a musí aktivovat přes nabídku: "View"->"Data Environment..."
Teprve až s příchodem VFP 8.0 přibyla nejen možnost definovat datové prostředí jako třídu (vcx/prg), ale i pro třídu formuláře nastavit pomocí vlastností DEClass a DEClassLibrary tuto definici jako vychozí. Což je výhodné, pokud potřebujeme mít data po ruce i při vytvoření objektu ze třídy formuláře nebo pokud chceme toto prostředí sdílet mezi více formuláři.
Datová oblast, dále jen DO (Session), je ekvivaletní k DE s tím, že nepodporuje visualní definici tj. ve VCX. Tato třída (Session) byla přidána do SP 3 pro VFP 6.0.

Základní využití DE

Příklad example6a.prg ukazuje vložení dvou tabulek do DE, z toho jedna je volaná tabulka a jsou provázány relací. Obdobnou variantou je vložení pohledu z databázového konteineru do DE - příklad example6b.prg.

Přesměrování složek kurzorů v DE - všechny verze VFP

Přesměrování složek souborů se musí zajistit dříve než VFP vytvoří první ovládací prvek. Protože pokud je nějaký ovladač svázán s tabulkou či polem tabulky a v době jeho vytvoření zdroj neexistuje, pak VFP vyvolá chybu. Projde se kolekce členů DE a pro všechny objekty základní třídy Cursor se provede oprava složky buď ve vlastnosti ControlSource - volná tabulka nebo Database - tabulky či pohled v databázovém konteineru. Viz. příklad example6c.prg
Toto reší VFP 8.0 novou vlastností BindCotrols

Veřejný či privátní DE?

DE má dva módy - veřejný (public) a privátní (private). Přičemž každá (DE) má vlastní identifikátor, ale veřejná (DE) má vždy 1. Tento identifikátor je uložen ve vlastnosti DataSasseionID a příznak zda je DE veřejná či privátní určuje vlastnost DataSession. Obě dvě vlastnosti jsou součástí formuláře (formsetu, toolbaru, atd.).
Veřejný
Veřejný DE zajišťuje zpětnou kompatibilitu s předchozími verzemi FP DOS/WIN a je to vlastně hlavní DE, který nelze nikdy uvolnit.
Privátní
Privátní DE slouží k oddělení datových oblastí jednotlivých formulářů, tak aby nebylo nutno otevírat tabulky a pohledy s dynamickými aliasy. Použití privátních DE má však jednu nevýhodu: VFP nemá žádný jednoduchý mechanismus pro přístup dat v rozdílných privátních DE. Nelze si tedy otevřít v jedné privátní DE jednu tabulku, druhou tabulku v dalším privátním DE pak obě použít v jednom SQL SELECTu. Ovšem při běžném procházení záznamů v tabulce pomocí SCAN...ENDSCAN, DO WHILE...SKIP...ENDO atd. je možné dočasně přepínat datové oblasti.
Dalším možným problémem je, že některá systémová nastavení (SETy) nejsou globální a jsou specifická pro danou datovou oblast viz. nápověda pro příkaz SET DATASESSION TO. Další drobnou výhodou privátní DE je možnost emulovat víceuživatelské prostředí v jedné instanci VFP.
Příklad example6d.prg ukazuje využití tříd Session a privátního módu. V nižších verzi VFP se třída Session dá emulovat pomocí skrytého formuláře s privátní DE.

Nabídka

Nabídka ve formuláři je možná pouze pro formulář kde vlastnost ShowWindow=2 (As Top Level). Pro ostatní varianty ShowWindow {0,1} se využívá buď vlastní nabídky nebo modifikace systémové nabídky.

As Top Level formulář

Zde platí jedno pravidlo aby byla nabídka vidět - musí se vytvořit v události Init(). Návrhář nabídky umožňuje v nastavení obecném nastavení zatrhnout generování pro AS Top Level formulář. Součástí vygenerovaného MPR je i ukázka zavolání nabídky (příklad example7a.prg).
*form::Init()
 * Vytvoření nabídky
 DO mymenu.mpr WITH THIS,.T.

*form::Destroy()
 * Uvolnění nabídky
 RELEASE MENU (THIS.Name) EXTENDED
Nikomu nic nebrání si v události Init() vytvořit celou vlastní nabídku bez použití návrháře nabídek (příklad example7b.prg).

Při užití nabídky ve formuláři je nutné pamatovat na jednu věc: užitná výška formuláře je zmenšena o výšku nabídky.
Při používání MPR je nutné pamatovat na:

Ostatní varianty

I v tomto případě lze využít jak návrháře nabídky, tak i programové definice nabídky. Jako nejčastější varianty programové definice jsou Nejhorší varianta je:

Kontextová nabídka

Kontextová nabídká formuláře se aktivuje na kliknutí pravého tlačítko myši (událost RightClick()) a je možné ji zobrazit bud uvnitř formuláře nebo s přesahem mimo formulář. Pozor se musí dát na: Pro zajištění zobrazení kontextové nabídky v hluchých místech formuláře, tj. záložky, konteinery atd. se dá využít tří cest:

ON KEY LABEL RIGHTMOUSE... (VFP 3.0-9.0)

Ačkoliv mnozí programátoři tento příkaz považují za anachronismus z DOS verze, protože "nepodporuje" objektový model chápání VFP i když to samé ignorují u příkazů ON SHUTDOWN, ON ERROR, ON ESCAPE, umožňuje nám tento příkaz se zahákovat na událost stisknutí pravého tlačítka myši. Tímto jednoduchým způsobem s minimem programového kódu lze přesměrovat veškerá volání události RightClick() do jedno centrálního místa a má vždy přednost před nativními OOP událostmi. Vzhledem k tomu, že ON KEY LABEL RIGHTMOUSE automaticky vyvolává jak události RightClick() na formuláři tak na objektu pod myší, tak stačí, když se v události RightClick() provede otestovaní, zda objekt pod myší má vlastní zpracování události RightClick(). Pokud ano, z RightClick() formu se vyskočí a VFP sama zajistí zbytek. Celé to ukazuje příklad example8a.prg.
Navíc je pro VFP 3.0-7.0 toto jeden ze dvou způsobů jak se zahákovat na událost RightClick() hlavního okna VFP (_Screen), druhý je popsán v článku Přidání metod k _Screen.

Přesměrovat událost RightClick() do stejné události nadřazeného objektu

VFP 3.0-9.0

Pokud daný objekt nezpracovává danou dálost, může ji přesměrovat do stejné události nadřazeného objektu a tak dále až po formulář. To se dá zařídit pomocí vlastní knihovny odvozených základních třídy, kde se provede přesměrování (příklad example8b.scx).

VFP 3.0 - 7.0

Pouze u některých konteinerových tříd jako PageFrame lze narazit na problém (pouze VFP 3-7), jak provést přesměrování u záložek když ještě neexistují. Lze to provést např. až na formuláři/třídy formuláře, kde se každé záložce natvrdo předefinuje událost RightClick() (příklad example8c.scx). A problém přetrvává u záložek generovaných dynamicky - lze jej vyřešit pomocí ON KEY LABEL RIGHTMOUSE.

VFP 8.0-9.0

Použitím vlastností MemberClass a MemberClasslibrary u PageFrame lze definovat jaká třída se má použít pro vytvoření objektu základní třídy Page a právě v defici třídy udané těmito vlastnosti lze mít přesměrování.

Přesměrovat událost RightClick() pomocí funkce BINDEVENT() (VFP 8.0-9.0)

BINDEVENT() je mocná funkce, umožňující zahákovat se téměř na libovolnou událost či metodu objektu (příklad example8d.scx), ale pozor na její použití:

Konstanty

Soubory s definicemi konstant lze přiřadit buď pro celý formulář nebo pro jednotlivé metody. Systémová proměnná _INCLUDE neslouží ke globálnímu přiřazení souboru konstant během kompilace, ale automaticky se přiřazuje při vytvoření nové třídy nebo nového formuláře. Dále není možno použít konstanty při přizazení coby hodnoty vlastnosti v "Object Inspectoru", protože samotné přiřazení hodnot vlastonostem není součástí p-kódu (příklad example9a.scx) a je nutné provést přiřazení v události Init() (příklad example9b.scx).

Problémy

Nemodální x modální formulář

Jedna z nečastějších chyb je volání nemodálního formuláře z modálního. Samotné zavolání však není nebezpečné. Nebezpečí je pečlivě skryto a ukáže se až bude nemodální formulář na pozadí:

Modální formulář a nabídka

Při aktivaci modálního formuláře dojde k tomu, že všechny volby nabídky, vyjma systémových, jsou zakázané. To se dá jednoduše obejít - stačí příslušné volby vygenerovat v události GotFocus() formuláře jak ukazuje příklad example10c.scx.
Aby mohla událost GotFocus() formuláře nastat musí formulář obsahovat nejméně jeden ovladač který focus může dostat.

Neuvolněný formulář

Aby se formulář kompletně uvolnil z paměti je nutné zajistit aby neexistovaly žádné další reference částí jeho objektového stromu. Příklad example10d.prg tento problém lehce demonstruje.

Zacyklování

Zacyklovaný formulář byl běžným jevem pro verze VFP 3.0-6.0 a podrobně to popisuje článek Zavěšený (hung) formulář a co s tím. Celé se to projevovalo tak, že při pokusu o ukončení formuláře proběhla událost QueryUnload() nebo Release(), dle způsobu ukončení, a formulář byl dále zobrazen. Nejčastější chybou bylo prolinkování v objektovém stromu, kdy se na vlastnost nějakého objektu nalinkoval jiný objekt z jiné části stromu či použitím funkce PEMSTATUS(). O verze VFP 7.0 jsou tyto chyby opraveny, nicméně nelze vyloučit, že při složitějším objektovém stromu dojde k při uvolňování objektu z paměti k zacyklování.

Formulář se spustil a zase zmizel

Jedna z nejčastějších otázek začátečníků je, proč program (spuštění formuláře) v IDE funguje dobře v pod Runtimem ne. Vcelku jsou možné dvě varianty:
Nemodální formulář a chybějící READ EVENTS (ukončení aplikace)
Je spuštěn nemodální formulář a chybí příkaz READ EVENTS. VFP tedy pokračuje dál, ukončí program a tím pádem i sebe (příklad example10f.exe).
Rozsah platnosti proměnné na kterou je formulář nalinkován a svázán
Je spuštěn nemodální formulář nalinkovaný na proměnnou deklarovou jako lokální, či která vůbec nebyla deklarovaná. VFP po suštění formuláře pokračuje ve vykonání procedury, při jejím opuštění uvolní všechny lokální a privátní proměnné (příklad example10g.prg).

Při kliknutí na tlačítko v panelu nabídky je hodnota ve zdroji dat ovladače nezměněná

Toto chování,nikoliv chyba, také vyvolává údiv na tvářích programátorů. Proč k tomu dochází? Protože při kliknutí na tlačítko v panelu nabídky nebo aktivaci baru v nabídce nedochází ke ztrátě fokusu aktivního objektu. A právě změna fokusu, vyvolá interně přepis hodnoty z vlastnosti Value do zdroje dat určeného vlastností ControlSource (příklad example10h.scx). Toto chování se dá vynutit zavoláním metodu SetFocus() daného objektu (příklad example10i.scx nebo example10i1.scx).
Bohužel za podmínky kdy je panel nabídky součástí kolekce Forms[] formsetu toto nefunguje a dochází přesně k opačnému jevu, kdy se zavoláním metody SetFocus() obnoví hodnota dle zdroje dat uvedeného v ControlSource (příklad example10i2.scx a example10i3.scx).

Proč mi nefunguje tento tip

Vysvětlení je jednoduché - protože je panel nabídky součástí kolekce Forms[] formsetu (příklad example10i2.scx a example10i3.scx). Tím pádem probíhá jiný sled událostí než u příkladů example10i.scx a example10i1.scx
Panel nabídky NENÍ součástí kolekce Forms[]
=== PROCEDURE _TOOLBAR.CMDSAVE.CLICK E:\NEW\CLANKY\FORMS\EXAMPLES\EXAMPLE.VCT
Value: 20060804         
Value of CS: 20060803         
38494,647, form1.text1.When()
38494,647, form1.text1.RangeLow()
38494,647, form1.text1.RangeHigh()
38494,647, form1.text1.Valid()
38494,647, form1.text1.GotFocus()
Value: 20060804         
Value of CS: 20060804
Panel nabídky JE součástí kolekce Forms[]
=== PROCEDURE FORMSET._TOOLBAR1._CMDSAVE1.CLICK E:\NEW\CLANKY\FORMS\EXAMPLES\EXAMPLE.VCT
Value: 20060803         
Value of CS: 20060802
37529,866, formset.form1.text1.When()
37529,866, formset._toolbar1._cmdsave1.LostFocus()
37529,866, formset.form1.text1.LostFocus()
37529,866, formset.form1.GotFocus()
37529,866, formset.form1.text1.GotFocus()
Value: 20060802
Value of CS: 20060802
37529,866, formset._toolbar1._cmdsave1.Valid()
37529,882, formset._toolbar1._cmdsave1.When()
Jak je vidět, v prvním případě proběhne sled událostí na textboxu který má fokus a tlačítko v panelu nabídky fokus nemůže získat. Kdežto v druhém případě tlačítko v panelu nabídky fokus může získat stejně jako když dochází ke změne fokusu mezi dvěma formuláři. Tím pádem nedojde k vyvolání interního zápisu z Value do zdroje dat uvedeného v ControlSource ale k opětovnému načtení hodnoty.

"Error with Edit1 - Value: Data type mismatch. Unbinding object edit1."

Projevuje se to pouze u objektů, které mají vyplněnou vlastnost ControlSource.
Je to chyba č. 2005 a její vzor je: Error with "name" - "property": "error". Tedy, nic moc...
Problém spočívá v tom, že datový typ zdroje dat neodpovídá datovému typu hodnoty vlastnosti Value daného objektu. Například: Objekt očekává ve Value hodnotu datového typu "number" ale zdroj dat je typu "boolean" nebo "string".

Optimalizace

Dynamické vytváření GUI

Výhoda RAD nástrojů spočívá v jednoduchém návrhu uživatelského rozhraní (GUI a TUI) což velmi lehce svádí nejen naházet objekty na formuláře ale i využívat kontainerové třídy jako Container, Controls a PageFrame a navíc vnořeně (příklad example11a.scx). Takový objekt se potom zavádí do paměti velmi pomalu ať už se jedná o spuštění nebo modifikaci v návrháři.

Proto se doporučuje mít jednotlivé části rozdělné a uložené jako třídy zabalené v objektu dle základní třídy container. A ve vhodný čas jej vytvořit. Tento způsob je sice náročný na programování, ale takto lze dosáhnout rychlejšího startu formuláře neboť uživateli se zobrazí jen to, co v danou chvíli může vidět. Nejčastěji se toho využívá při aktivaci stránky listovacího rámečku, kdy se vytvoří například mřížky, OLE objekty (stromy, seznamy, atd.), nebo celé oblasti s ovladači (příklad example11b.prg).

Dynamické napojování ovladačů

Od VFP 8.0 lze pomocí vlastnosti BindControls řídit, zda se mají být ovladače napojeny na zdroje uvedených ve vlastnostech ControlSource a RecordSource. Výchozí hodnota je .T. aby byla zachována zpětná kompatibilita s předchozími verzemi VFP. Při nastavení hodnoty .F. lze zdroje uzavřít a otevřít nové aniž by VFP hlásila chybu. Po nastavení hodnoty .T. dojde k opětovnému napojení ovladačů na zdroje (příklad example11c.scx).
Ovlivňuje obsah objektů tříd combobox a listbox - seznamy nezobrazují hodnoty pokud je vlastnost RowsourceType<>1.

Oddělení GUI od programového kódu

Opět jsme další pseudovýhody RAD nástrojů... plácání výkoného kódu přímo do GUI (příklady example11d.scx,example11e.scx ). Jistě je to jednoduché, pro začátečníky přívětivé, ale co se týče pozdější správy (tj. oprav a údržby) je i peklo vůči tomu vzorné pracoviště mající nejnižší normu ISO. Ve formuláři by měl být jen takový kód, který přímo manipuluje s GUI. Vše ostatní by mělo bý mimo objekty GUI (příklad example11f.prg).

Daší tipy a triky

Spuštění formuláře z dalšího EXE/APP

Jak vidno z helpu, příkaz DO FORM nemá klíčové slovo pro identifikaci EXE/APP modulu ve kterém se nachází formulář. Spuštění formuláře se tedy zajišťuje pomocí proxy programu.
Proxy program je buď program nebo procedura zahrnutá do výsledného EXE/APP zajištující potřebnou činnost: spuštění formuláře, vykopírování reportu, otevření zahrnutých tabulek atd. Čím více je proxy program obecnější, tím větší je to bezpečnostní riziko.
Proxy program může být označen jako hlavní ale taky nemusí.
Proxy program je označen jako hlavní
DO any.app WITH "myForm"

* proxy.prg
LPARAM lcName
DO FORM (lcName)
 
Proxy program není označen jako hlavní
DO proxy IN any.app WITH "myForm"

* proxy.prg
LPARAM lcName
DO FORM (lcName)
 

Mřížka, neboli grid

Většina podstatných věcí je řečena v tomto seriálu.

Změna hodnoty vlastnosti RecordSource mřížky

Jedna z dalších "nepříjemných" vlastností VFP je, že při změně hodnoty vlastnosti RecordSource mřížky dojde k automatickému vymazání hodnoty ve vlastnosti ControlSource ve všech sloupcích. Řešení je několik:

Sdílené datové oblasti více formuláři

Datová oblast se určuje pomocí vlastnosti DataSessionID. Čili jednoduchou změnou hodnoty vnutíme formuláři i jinou datovou oblast.

Použité zdroje