Dynamická změna pozice a velikostí objektů v návrhu formuláře či třídy

VFP umožňuje při návrhu formuláře nebo třídy nastavit vlastnosti hodnotou, ale i výrazem. Toto lze využít pro dynamické určení pozice a velikosti objektů vůči objektu ve kterém se nacházejí (Parent object - dále jen PO).

1) Nastavení příslušných vlastností v návrhu

a) Obecný úvod
Nastavením vlastností Left,Top,Width,Height pomocí výrazu lze zajistit dynamické chování. Bohužel toto se aplikuje pouze v okamžiku potvrzením hodnoty v okně "Properties". Proto pokud dojde ke změně velikosti formuláře, musí programátor projít všechny objekty a hodnoty patřičných vlastností potvrdit klávesou Enter. Jak to zařídit automaticky se zabývá bod 2) Programové zajištění změn.

Další nevýhoda je, pokud je dotyčný objekt přesunut či je interaktivně změněna velikost (oboje bylo vykonáno pomocí myši), pak se místo výrazu dosadí nové hodnoty coby čísla. Přesunutí ovlivňuje vlastnosti Left a Top; změna velikosti ovliňuje vlastnosti Width a Height.

b) Nastavení vlastností
Vlastnosti Left,Width objektů jsou zavislé na vlastnosti Width objektu PO.
Vlastnosti Top,Height objektů jsou zavislé na vlastnosti Height objektu PO.

Výjimku tvoří PO jehož základní třída je Page. V tom případě se musí jako PO použít celý PageFrame (třída Page nemá vlastnosti Height a Width).

Objekty lze:

c) Příklady použití:
(V závorkách se nachází výraz)

Souměrná změna šířky objektu v závislosti na šířce PO:
 Width (=This.Parent.Width-This.Left*2)
 Width (=This.Parent.Parent.PageWidth-This.Left*2) je-li PO třídy Page

Nesouměrná změna šířky objektu v závislosti na šířce PO:
 Width (=This.Parent.Width-10)
 Width (=This.Parent.Parent.PageWidth-10) je-li PO třídy Page

Souměrná změna výšky objektu v závislosti na výšce PO:
 Height (=This.Parent.Height-This.Top*2)
 Height (=This.Parent.Parent.PageHeight-This.Top*2) je-li PO třídy Page

Nesouměrná změna výšky objektu v závislosti na výšce PO:
 Height (=This.Parent.Height-10)
 Height (=This.Parent.Parent.PageHeight-10) je-li PO třídy Page

Změna pozice Left objektu v závislosti na šířce a pevné vzdálenosti od pravého okraje PO:
 Left (=This.Parent.Width-This.Width-20)
 Left (=This.Parent.Parent.PageWidth-This.Width-20) je-li PO třídy Page

Změna pozice Top objektu v závislosti na výšce a pevné vzdálenosti od dolního okraje PO:
 Top (=This.Parent.Height-This.Height-20)
 Top (=This.Parent.Parent.PageHeight-This.Height-20) je-li PO třídy Page

2) Programové zajištění změn

VFP podporuje v během návrhu formulářů a tříd přímý přístup k objektů, fungují i některé metody, ale není jich mnoho.
První je ASELOBJ(laObj,[1,2]):
Tato funkce slouží pro získání odkazů na vybrané objekty, formulář/třídu či objekt DataEnvironment ve formuláři.
ASELOBJ(laObj)   - Do matice laObj vrátí odkaz(y) na vybraný nebo vybrané objekty aktuálně editovaného formuláře/třídy
ASELOBJ(laObj,1) - Do matice laObj vrátí odkaz na Parent object vybraného objektu
ASELOBJ(laObj,2) - Do matice laObj vrátí odkaz na DataEnvironment aktuálně editovaného formuláře

Pro přístup na vlastnosti objektů se sice dá použít přímý přístup na vlastnosti, ale přímo ve vlastnostech se nacházejí již vypočtené hodnoty ( byl-li použit výraz). Proto se musí použít metody ReadExpression() pro načtení hodnoty vlastnosti a WriteExpression() pro uložení hodnoty do vlastnosti.

Načtení vlastnosti: ReadExpression(PEM_Name) kde PEM_Name je název vlastnosti.
Uložení vlastnosti: WriteExpression(PEM_Name,PEM_Value) kde PEM_Name je název vlastnosti a PEM_Value je hodnota vlastnosti převedaná na řetězec.

Pokud metoda ReadExpression() vrátí prázdný řetězec, pak hodnota vlastnosti je vyjádřena hodnotou, nikoliv funkcí. Pak se hodnota vlastnosti získá přímým přístupem na vlastnost.

Programový kód:
* Zjištění PO vybraného objektu
LOCAL loParent
LOCAL ARRAY laObj(1) && dimenzování pomocné matice
IF ASELOBJ(laObj,1)=0
   RETURN
ENDIF

* Nyní musím zjistit nejvýše postavený objekt
* (není to tak docela pravda, neboť v režimu návrhu je vždy na vrcholu objektové hierarchie objekt třídy FormSet)

* Pokud je předaný objekt formulář
IF UPPER(laObj(1).BaseClass)=="FORM"
ELSE
   loParent=laObj(1)
   * Nyní pojedu po objektové hierarchi tak dlouho, až narazím na formulář
   DO WHILE ! UPPER(loParent.BaseClass)=="FORM"
      loParent=loParent.Parent
   ENDDO
   * Pokud má formulář jeden objekt a popis formuláře souhlasí s názvem třídy jediného objektu
   * pak vrať jediný objekt (jde o třídu) jinak vrať přímo formulář
   laObj(1)=IIF(loParent.ControlCount=1 AND loParent.Caption==loParent.Controls(1).Class,;
            loParent.Controls(1),loParent)
ENDIF

* Nyní když znám "nejvýše postavený" objekt, otestuju zda patří mezi konteinerové objekty
IF !INLIST(UPPER(laObj(1).BaseClass),"FORM","CONTAINER","PAGE","PAGEFRAME","OPTIONGROUP","COMMANDGROUP")
   RETURN
ENDIF

* Nyní můžu projet celou objektovou hierarchií a provést změny
=ResizeContainerObject(laObj(1))
RETURN


PROCEDURE ResizeContainerObject(loCont)
* @loCont - Odkaz na konteiner

* Nyní budu probíhat vnořené objekty a měnit jejich polohu a velikost v závislosti na vlastnostech Left,Top,Width,Height
LOCAL luLeft,luTop,luWidth,luHeight,lii,liCount,loObj,;
      llLeft,llTop,llWidth,llHeight,lcBaseClass

* Velikost konteineru
lcBaseClass=UPPER(loCont.BaseClass)

* form-controls-controlcount: container-controls-controlcount:  page-controls-controlcount: 
* pageframe-pages-pagecount: optiongoup-buttons-buttoncount: commandgoup-buttons-buttoncount:
liCount=IIF(lcBaseClass=="FORM" OR lcBaseClass=="CONTAINER" OR lcBaseClass=="PAGE",loCont.ControlCount,;
        IIF(lcBaseClass=="OPTIONGROUP" OR lcBaseClass=="COMMANDGROUP",loCont.ButtonCount,loCont.PageCount))


FOR lii=1 TO liCount
    * Zkratka na objekt
    loObj=IIF(lcBaseClass=="FORM" OR lcBaseClass=="CONTAINER" OR lcBaseClass=="PAGE",loCont.Controls(lii),;
          IIF(lcBaseClass=="OPTIONGROUP" OR lcBaseClass=="COMMANDGROUP",loCont.Buttons(lii),loCont.Pages(lii)))

    IF !UPPER(loObj.BaseClass)=="PAGE"
       * Získej textové prezentace pozic a velikosti objektů
       luLeft=loObj.ReadExpression("Left")
       luTop=loObj.ReadExpression("Top")
       luWidth=loObj.ReadExpression("Width")
       luHeight=loObj.ReadExpression("Height")

       * Nyní zjisti, které vlastnosti jsou dynamické
       llLeft=!EMPTY(luLeft)
       llTop=!EMPTY(luTop)
       llWidth=!EMPTY(luWidth)
       llHeight=!EMPTY(luHeight)

       * Proveď zpět zápis hodnot
       loObj.WriteExpression("Left",IIF(llLeft,luLeft,LTRIM(STR(loObj.Left))))
       loObj.WriteExpression("Top",IIF(llTop,luTop,LTRIM(STR(loObj.Top))))
       loObj.WriteExpression("Width",IIF(llWidth,luWidth,LTRIM(STR(loObj.Width))))
       loObj.WriteExpression("Height",IIF(llHeight,luHeight,LTRIM(STR(loObj.Height))))
    ENDIF

    * Pokud je objekt konteinerová třída
    IF INLIST(UPPER(loObj.BaseClass),"FORM","CONTAINER","PAGE","PAGEFRAME","OPTIONGROUP","COMMANDGROUP")
       =ResizeContainerObject(@loObj) && Pak to zavolej
    ENDIF
NEXT

RETURN

3) Použití komponenty IRes

Komponenta IRes slouží pro dynamickou změnu pozic a velikostí objektů během návrhu (jak formulářů, tak i tříd) a je připravena pro okamžité použití.
Zajišťuje vyvolání dynamických změn buď v určeném intervalu nebo na vlastní žádost pomocí horkých kláves.

Počáteční inicializace
SET PROCEDURE TO ires.prg && Připojení PRG souboru
iRres=CREATEOBJECT("_tmrIRes","iRres") && Vytvoření objektu, třída sama zajistí vytvoření PUBLIC proměnné
* A to je vše.
* Defaultně je to nastaveno automatické vyvolání za 1 vteřinu.
* Pro manuální vyvolání se nejdříve musí nastavit rychlé klávesy.

Nastavení automatického/manuálního módu
IRes.SetMode(0) && Manuální mód
IRes.SetMode(1) && Automatický mód
IRes.Interval=5000 && Nastavení intervalu pro automatický mód

Nastavení/zrušení rychlých kláves pro manuální vyvolání
IRes.SetKey_Global("ALT+G") && Nastavení kombinace kláves pro celý formulář/třídu
IRes.SetKey_Current("ALT+C") && Nastavení kombinace kláves pro vybraný konteinerový objekt

IRes.ClearKey_Global() && Zrušení kombinace kláves pro celý formulář/třídu
IRes.ClearKey_Current() && Zrušení kombinace kláves pro vybraný konteinerový objekt

Programové vyvolání dynamických změn
IRes.GlobalResize() && Vyvolání změn pro celý formulář/třídu
IRes.CurrentResize() && Vyvolání změn pro vybraný konteinerový objekt