Kdyby nebylo textu v Tipech, tricích a hotline III nebyl by ani tento článek a dalo by se říci, že onen text navazuje.

Paralelní Tipy a triky

Spoustu informací o systému dokáže API funkce SystemParametersInfo() nejen vrátit, ale i nastavit…
Parametry se dají předávat různými způsoby, ale když je potřeba vrátit více hodnot, je každá rada drahá.


Funkce SystemParametersInfo()

Tato funkce slouží nejen pro zjišťování některých systémových hodnot, ale i jejich nastavení. Kompletní popis funkce se nachází v MSDN. Nás však bude zajímat zjištění viditelné plochy Windows bez „otravného“ TaskBaru či zda jsou okna animována.

Nejdříve hlavička programu:

LOCAL liErr,lcRect,liWidth,liHeight,liOut
#DEFINE SPI_GETWORKAREA            48
#DEFINE SPI_GETANIMATION           72
#DEFINE SPI_GETDRAGFULLWINDOWS     38
#DEFINE SPI_SETDRAGFULLWINDOWS     37

Pro zjištění velikosti plochy bez Taskbaru se použije konstanta SPI_GETWORKAREA. Druhý parametr je vždy 0. Třetí parametr je odkaz na strukturu RECT do které se naplní informace o počáteční pozici a velikosti. Čtvrtý parametr je vždy 0. Jelikož struktura RECT obsahuje pozice horního levého a pravého rohu a dolního levého a pravého rohu, musejí se hodnoty příslušné hodnoty odečíst.

DECLARE INTEGER SystemParametersInfo IN User32.dll INTEGER,INTEGER,STRING @,INTEGER

lcRect=SPACE(16) && Nadimenzování řetězce pro naplnění struktury
liErr=SystemParametersInfo(SPI_GETWORKAREA,0,@lcRect,0) && Vrať velikost plochy bez TaskBaru
IF liErr>0 && Neselhalo volání funkce
   liWidth=C4ToL(SUBS(lcRect,9,4))-C4ToL(LEFT(lcRect,4))
   liHeight=C4ToL(SUBS(lcRect,13,4))-C4ToL(SUBS(lcRect,5,4))
   ?'Velikost plochy: Šířka=',liWidth,'Výška:',liHeight
ENDIF

Pro zjištění zda jsou okna animována (při maximalizaci a minimalizaci) se použije konstanta SPI_GETANIMATION. Druhý parametr je vždy velikost struktury ANIMATIONINFO. Třetí parametr je odkaz na strukturu ANIMATIONINFO do které se naplní příznak animace. Čtvrtý parametr je vždy 0.

lcRect=IToC4(8)+'    ' && Nadimenzování řetězce pro naplnění struktury
liErr=SystemParametersInfo(SPI_GETANIMATION,8,@lcRect,0) && Zjisti příznak animace oken
IF liErr>0 && Neselhalo volání funkce
   ?'Animace oken:',IIF(C4ToI(SUBS(lcRect,5,4))>=1,'Ano','Ne')
ENDIF

Příznak posunu celých oken je funkční pouze pro Win95 Plus Pack! nebo vyšší a NT 3.51 nebo vyšší. Jelikož jako třetí parametr se musí poslat odkazem proměnná na číslo, musí se API funkce předeklarovat. Druhý a čtvrtý parametr je 0. Pro zjištění příznaku se použije konstanta SPI_GETDRAGFULLWINDOWS a pro nastavení SPI_SETDRAGFULLWINDOWS.

DECLARE INTEGER SystemParametersInfo IN User32.dll INTEGER,INTEGER,INTEGER @,INTEGER
liOut=0
liErr=SystemParametersInfo(SPI_GETDRAGFULLWINDOWS,0,@liOut,0) && Zjisti příznak posouvání oken
IF liErr>0 && Neselhalo volání funkce
   ?'Posouvání celých oken:',IIF(liOut>=1,'Ano','Ne')
   IF liOut=0 && Bylo-li to zakázáno
      liOut=1 && Pak to povol
      liErr=SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,0,@liOut,0)
      IF liErr>0 && Neselhalo volání funkce
         ?'Nastaveno posouvání celých oken...'
      ENDIF
   ELSE && Bylo-li to povoleno
      liOut=0 && Pak to zakaž
      liErr=SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,0,@liOut,0)
      IF liErr>0 && Neselhalo volání funkce
         ?'Zrušeno posouvání celých oken...'
      ENDIF
   ENDIF
ENDIF
A zbylé procedury:
***********************************************************************************************
*
PROCEDURE C4ToI(lcInt) && Převede číslo v řetězcovém tvaru WINAPI 32 bitů - signed na číslo
RETURN ASC(LEFT(lcInt,1))+;
       ASC(SUBS(lcInt,2,1))*256+;
       ASC(SUBS(lcInt,3,1))*65536+;
       ASC(RIGHT(lcInt,1))*256*65536  && vrať číslo v decimálním tvaru

***********************************************************************************************
*
PROCEDURE C4ToL(lcLong) && Převede číslo v řetězcovém tvaru WINAPI 32 bitů na číslo
LOCAL liVal
liVal=ASC(LEFT(lcLong,1))+;
                  ASC(SUBS(lcLong,2,1))*256+;
                  ASC(SUBS(lcLong,3,1))*65536+;
                  ASC(RIGHT(lcLong,1))*256*65536

RETURN IIF(liVal<=0x7FFFFFFF,liVal,liVal-(0xFFFFFFFF+1))

***********************************************************************************************
*
PROCEDURE IToC4(liVal) && Převede číslo na 4 bajtový řetězec
LOCAL lcVal,lii

lii=INT(liVal/256) && Vyděl 256
lcVal=CHR(liVal-lii*256) && Převeď rozdíl na znak a přičti ho
liVal=lii && Zapiš novou hodnotu pro převod

lii=INT(liVal/256) && Vyděl 256
lcVal=lcVal+CHR(liVal-lii*256)
liVal=lii && Zapiš novou hodnotu pro převod

lii=INT(liVal/256) && Vyděl 256
lcVal=lcVal+CHR(liVal-lii*256)
liVal=lii && Zapiš novou hodnotu pro převod

lii=INT(liVal/256) && Vyděl 256
RETURN lcVal+CHR(liVal-lii*256) && Převeď rozdíl na znak a přičti ho

Předání parametrů a návrat hodnot nejen z formuláře

Nejčastější je předávání více hodnot do formuláře a jejich navrácení zpět. Někdy je nutné znát parametry již v události Load() nebo ještě dříve (události DataEnvironmentu). Formulář sice podporuje převzetí parametrů, ale až v události Init() v době, kdy jsou inicializovány všechny objekty. Optimální by bylo, pokud by se do formuláře (nejen formuláře, ale i programu nebo procedury) předával jen jeden parametr typu Objekt.

Proč má být jako parametr objekt? Protože se přes něj dá poslat více hodnot, včetně odkazů na další objekty, a také navrátit další hodnoty získané v závislosti na zadaných parametrech.

Aby jsme se vyhli vytváření PUBLIC či PRIVATE proměnné, tak si vytvoříme třídu _StackParameters, která bude fungovat jako zásobník parametrů. Instanci třídy ( s názvem "SP") přidáme do objektu _Screen pomocí metody AddObject(). Třída má metodu Put() pro uložení parametru do zásobníku a metodu Get() pro výběr parametru ze zásobníku. Je to naprogramované ve stylu LIFO - Last In, First Out (Poslední dovnitř, První ven).

Jako parametr nám poslouží instance třídy _Param, která má vlastnosti First a Second pro předání hodnot do formuláře a vlastnost OK pro navrácení příznaku, zda byl formulář ukončen tlačítkem OK.

Před zavoláním formuláře (nebo procedury) se pomocí metody Put() uloží parametr do zásobníku. Pak se zavolá formulář, který v události Load() vybere pomocí metody Get() parametr ze zásobníku a uloží si ho do vlastnosti Param. V události Init() pak přebere hodnoty z vlastností parameru do vlastností Value textboxů.

Navrácení hodnot zajišťuje tlačítko OK, které uloží hodnoty z vlastností Value textboxů do příslušných vlastností parametru. Dále pak nastaví vlastnost OK na .T. - příznak ukončení dialogu (potvrzení uživatelem) - a ukončí formulář.

Poznámka: Formulář můžete zavolat kdykoliv, ale mějte na paměti, že pokud je z události When() nebo Valid() volán další formulář, pak při zavolání metody SetFocus() - třeba v události Init() formuláře - Visual FoxPro vyhlásí chybu.

Následující zdrojový kód předvádí výše uvedenou teorii od vytvoření zásobníku parametrů až po zpracování hodnot zadaných uživatelem.

************************************************************************
* Hlavní program

LOCAL loPar,loForm
* Přidej objekt do objektu _Screen, vyhneme se tím deklaraci PUBLIC proměnné
_Screen.AddObject("SP","_StackParameters")

loPar=CREATEOBJECT("_Param") && Vytvoř parametr pro předání hodnot
loPar.Second=loPar.Second+" A"
_Screen.SP.Put(@loPar) && Ulož parametr do zásobníku

loForm=CREATEOBJECT("_dlgForm") && Vytvoř instanci formuláře pro zadání hodnot
* Zde však může být
* DO FORM dglForm LINKED NAME loForm
*
* Takže je úplně jedno, zda je formulář definován jako třída v PRG souboru
* nebo VCX knihovně, či jako čistý formulář.

* V této chvíli jsou již hodnoty ve formuláři načteny do objektů
=MESSAGEBOX(loForm.txtSecond.Value)

loForm.Show() && Zobraz formulář
* Jelikož je formulář modální, zpracování událostí se zastaví,
* dokud formulář není uvolněn z paměti, nebo není skryt.

IF loPar.OK && Dialog byl potvrzen
   =MESSAGEBOX("První hodnota:"+CHR(9)+loPar.First+;
               CHR(13)+CHR(10)+;
               "Druhá hodnota:"+CHR(9)+loPar.Second)
ELSE
   * Dialog nebyl potvrzen
ENDIF

_Screen.RemoveObject("SP") && Odstraň Zásobník parametrů
RELE loForm,loPar

************************************************************************
*
#DEFINE _MaxCount 65000 && Maximální počet buňek v interní matici

DEFINE CLASS _StackParameters AS custom && Definice třídy zásobníku parametrů
   PROTECTED ARRAY aPar(_MaxCount)
   PROTECTED iCount

   iCount=0 && Počet řádků matice aPar

   PROCEDURE Put(luPar) && Uloží parametr do zásobníku
      This.iCount=This.iCount+1 && Inkrementuj čítač
      DIME This.aPar(This.iCount) && Předimenzuj matici
      This.aPar(This.iCount)=luPar && Ulož parametr do zásobníku
   ENDPROC

   PROCEDURE Get() && Vybere poslední parametr ze zásobníku
      LOCAl luPar
      IF This.iCount>0 && Pokud v zásobníku něco je
         luPar=This.aPar(This.iCount) && Vyber parametr ze zásobníku
         This.iCount=This.iCount-1 && Dekrementuj čítač
         IF This.iCount>0
            DIME This.aPar(This.iCount) && Předimenzuj matici
         ENDIF
      ELSE
         luPar=.NULL.
      ENDIF
      RETURN luPar && Vrať hodnotu
   ENDPROC

ENDDEFINE



************************************************************************
*
DEFINE CLASS _Param AS custom && Definice třídy pro předání parametrů
   OK=.F.            && Příznak o potvrzení dialogu

   First = "First"   && První hodnota
   Second = "Second" && Druhá hodnota
ENDDEFINE


**************************************************
*-- Form:         form1 (x:\ptriky\quas40_2.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Time Stamp:   04/18/02 03:11:01 PM
*
DEFINE CLASS _dlgForm AS form


	Top = 0
	Left = 0
	Height = 145
	Width = 272
	DoCreate = .T.
	BorderStyle = 2
	Caption = "Form1"
	WindowType = 1
	*-- Na tuto vlastnost se nalinkuje vybraný parametr ze zásobníku
	param = .NULL.
	Name = "dlgForm"


	ADD OBJECT txtfirst AS textbox WITH ;
		Height = 23, ;
		Left = 105, ;
		Top = 24, ;
		Width = 100, ;
		Name = "txtFirst"


	ADD OBJECT txtsecond AS textbox WITH ;
		Height = 23, ;
		Left = 105, ;
		Top = 51, ;
		Width = 100, ;
		Name = "txtSecond"


	ADD OBJECT cmdok AS commandbutton WITH ;
		Top = 96, ;
		Left = 21, ;
		Height = 27, ;
		Width = 84, ;
		Caption = "OK", ;
		Default = .T., ;
		Name = "cmdOK"


	ADD OBJECT cmdcancel AS commandbutton WITH ;
		Top = 96, ;
		Left = 146, ;
		Height = 27, ;
		Width = 84, ;
		Cancel = .T., ;
		Caption = "Zrušit", ;
		Name = "cmdCancel"


	ADD OBJECT lblfirst AS label WITH ;
		Caption = "První hodnota:", ;
		Height = 17, ;
		Left = 15, ;
		Top = 29, ;
		Width = 80, ;
		Name = "lblFirst"


	ADD OBJECT lblsecond AS label WITH ;
		Caption = "Druhá hodnota:", ;
		Height = 17, ;
		Left = 14, ;
		Top = 55, ;
		Width = 87, ;
		Name = "lblSecond"


	PROCEDURE Load

		This.Param=_Screen.SP.Get() && Vyber parametr ze zásobníku
		* V zde se můžou již nastavit některé věci pro formulář,
		* ale hodnoty do objektů se musí nastavit až v události Init()
		* (protože objekty txtFirst a txtSecond neexistují).
		* Výjimku tvoří případy, kdy je formulář vytvořen na základě tídy s vnořenýma objektama
	ENDPROC


	PROCEDURE Init
		* Nyní přetáhni hodnoty z parametru do objektů na formuláři
		IF !ISNULL(This.Param) && Pokud byl parametr předán
		   Thisform.txtFirst.Value=Thisform.Param.First
		   Thisform.txtSecond.Value=Thisform.Param.Second
		ENDIF
	ENDPROC


	PROCEDURE cmdok.Click
		IF !ISNULL(Thisform.Param) && Pokud byl parametr předán
		   Thisform.Param.OK=.T. && Nastav potvrzení dialogu
		   Thisform.Param.First=Thisform.txtFirst.Value && A ulož hodnoty do vlastností
		   Thisform.Param.Second=Thisform.txtSecond.Value
		ENDIF
		Thisform.Release() && Ukonči formulář
	ENDPROC

	PROCEDURE cmdcancel.Click
		Thisform.Release() && Ukonči formulář
	ENDPROC


ENDDEFINE
*
*-- EndDefine: form1
**************************************************