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.
|