Soluţii - PC Magazine Romania, Aprilie 2004
ActiveX (2)
Konstantin Klyagin
Proprietăţi read sau write only
E posibil că ai mai văzut la controalele ActiveX mai avansate faţă de amărâtul
nostru Vasile (aşa ne-am botezat controlul luna trecută), proprietăţi care se
pot numai citi sau numai scrie. Această capacitate este de folos atunci când
e vorba de parametri cu ajutorul cărora utilizatorul poate afla starea curentă
a controlului. Evident, că astfel de informaţii pot fi setate numai de către
control şi n-ar avea sens să îl lasăm pe utilizator să le modifice. De exemplu,
succesul ultimei operaţiuni, cantitatea de date transferate sau numărul de înregistrări
găsite în baza de date.
Proprietăţile de genul "write-only" pot fi utile când vrei să le
foloseşti pentru a trimite informaţii către control într-o singură direcţie.
De exemplu, se poate face un fel de stream, la care tot ceea ce se transmite
unei proprietăţi să fie considerat ca un flux neîntrerupt de date. Exact cum
este controlul MsComm, el având proprietatea Output folosită pentru a trimite
caractere către portul serial.
Ca să faci o proprietate "read-only" sau "write-only" trebuie
doar să nu defineşti metoda Set sau Get respectivă. Următoarea proprietate va
fi read-only.
De reţinut: Dacă laşi un şir gol în câmpul "Set method" sau "Get
method", proprietatea devine automat write-only sau respectiv read-only.
De asemenea, proprietăţile write-only nu vor apărea pe lista de "Properties
browser" dintr-un motiv evident.
De fapt, orice proprietate poate fi făcută vizibilă numai în timpul executării
încât să nu aglomereze lista în regimul de design. Într-adevăr, ce sens are
să vezi şi să schimbi valoarea unor parametri care sunt de actualitate numai
când programul e pornit? Ca un exemplu, gândeşte-te la dimensiunea şi poziţia
curentă într-un fişier transferat prin FTP.
Pentru asta, trebuie să mai "îmbunătăţeşti" codul metodei Get în
modul următor.
long CFTPCtrl:: GetCurrentFilePosition() { if(!AmbientUserMode()) ThrowError(CTL_E_GETNOTSUPPORTED); return m_CurrentFilePosition; }
Secretul constă în folosirea metodei AmbientUserMode() care returnează TRUE
când componenta se află în mod design. Altfel primim FALSE. Aşadar, după o asemenea
modificare proprietatea va fi disponibilă numai în timpul executării, fără să
se vadă în "Properties browser". Exact ce am vrut să facem.
Liste de valori
Unele controale pentru anumite proprietăţi oferă alegere din listă în loc de
a introduce manual o valoare. Pentru a crea o asemenea listă definim un enum.
Acesta trebuie adăugat la fişierul .odl al proiectului, sintaxa fiind asemănătoare
celei din C.
typedef enum { clrAlbastru = 0, clrGalben = 1, clrRosu = 2 } vasile_Culoare;
După care, tot manual schimbăm tipul proprietăţii în modul următor:
[id(1)] vasile_Culoare Culoare;
Fii atent că metodele Get şi Set corespunzătoare nu trebuie schimbate. Având
ca tip int, short sau long, ele vor lucra perfect cu enum-ul definit, fiindcă
acestea sunt interconvertibile. De asemenea, în cod poţi folosi constante din
enum în loc de numere asociate (de exemplu, clrAlbastru în loc de 0), ceea ce
uşurează mult citirea codului sursă. Asta însă necesită un pas adiţional: la
începutul fişierului .cpp după include-uri adaugă următoarea directivă de import:
#import "Debug\vasile.tlb"
În acest caz va trebui să adaugi prefixul VASILElib:: la toate denumirile din
ODL, ca de exemplu VASILElib::clrAlbastru. Iar ca să eviţi această necesitate,
faci import aşa:
#import "Debug\vasile.tlb" no_namespace
Iar acum, încercând să schimbe valoarea proprietăţii din "Properties browser"
utilizatorul va vedea lista cu denumiri de valori din enum.
Denumiri textuale pentru valori
De asemenea, mai este posibil ca după ce utilizatorul introduce o valoare în
"Properties browser", în loc de aceasta să apară un şir de caractere
descriind valoarea aleasă. Rareori poate fi de folos, dar e bine să ştii şi
această metodă.
Deci, apelăm la bunul cunoscut al nostru ClassWizard, şi alegem din listă metoda
OnGetDisplayString() care trebuie redefinită.
Să spunem, că dorim în loc de "0 - clrAlbastru" la valoarea lui Culoare
să ne apară textul "ALBASTRU!", fără să dezvăluie numărul intern al
opţiunii la duşmani.
BOOL CVasileCtrl:: OnGetDisplayString(DISPID dispid, CString& strValue) { if(dispid == dispidCuloare) { if(m_Culoare == clrAlbastru) { strValue = "ALBASTRU!"; return TRUE; } } }
Fii atent că trebuie returnat TRUE în caz că s-a găsit un text potrivit. O
altă aplicare bună a acestei metode ar fi menţinerea unor denumiri generale
pentru intervale de valori.
Presupunând că avem o proprietate AgeRange şi variabila m_AgeRange asociată
cu ea, putem face o asemenea şmecherie:
BOOL CVasileCtrl:: OnGetDisplayString(DISPID dispid, CString& strValue) { if(dispid == dispidAgeRange) { if(m_AgeRange < 18) strValue = "copil"; else if(m_AgeRange < 25) strValue = "tânar"; else if(m_AgeRange < 60) strValue = "matur"; else strValue = "boşorog"; } return !strValue.IsEmpty(); }
Liste de echivalente textuale
Unii mai mofturoşi pot dori să vadă o listă de opţiuni complet textuale (sau
chiar localizate) în loc de meniuri de genul "0 - text", "1 -
text". Tehnologia ActiveX are ce le oferi şi lor. Ca să facem înlocuirea,
redefinim metodele OnGetPredefinedStrings() şi OnGetPredefinedValue():
BOOL CVasileCtrl::OnGetPrede- finedStrings(DISPID dispid, CStringArray* pStringArray, CDWordArray* pCookieArray) { if(dispid == dispidCuloare) { pStringArray->Add("ALBASTRU!"); pCookieArray->Add(clrAlbastru); pStringArray->Add("ROSU!"); pCookieArray->Add(clrRosu); pStringArray->Add("GALBEN!"); pCookieArray->Add(clrGalben); return TRUE; } return COleControl::OnGetPrede- finedStrings(dispid, pStringArray, pCookieArray); } BOOL CVasileCtrl::OnGetPredefined- Value(DISPID dispid, DWORD dwCookie, VARIANT* lpvarOut) { if(dispid == dispidCuloare) { CComVariant(dwCookie, VT_I4). Detach(lpvarOut); return TRUE; } return COleControl::OnGetPrede- finedValue(dispid, dwCookie, lpvarOut); }
Legatura între elemente de meniu şi valori actuale se face aici pe baza array-ului pCookieArray. Simultan cu pStringArray care conţine opţiunile disponibile, pCookieArray se umple cu numerele reprezintând variantele respective. După ce o nouă valoare este selectată, OnGetPredefinedValue() setează valoarea actuală în funcţie de cookie. VARIANT este o structură de date care de fapt poate conţine valori de orice tip. Deoarece în cazul nostru cookie-uri şi valorile au acelaşi conţinut, pur şi simplu creăm un variant cu aceeaşi valoare DWORD. Rezultatul se pune în variabila lpvarOut. Să nu uităm de OnGetDisplayString(), care trebuie modificat astfel încât valoarea aleasă să fie afişată în "Properties browser" după ce lista dispare.
Urmare în numărul viitor.
|