20
In Kapitel 17, »Ein ActiveX-Steuerelement erstellen«, haben Sie gelernt, wie Sie eigene Steuerelemente erstellen und diese in formulargestützte Anwendungen einbinden, die in Visual Basic, Visual C++ oder der Makrosprache VBA geschrieben wurden. Doch nicht nur in Anwendungen lassen sich solche Steuerelemente einbinden, sondern auch in eine Web-Seite. Doch die in älteren Visual-C++-Versionen erstellten ActiveX-Steuerelemente waren zu groß und zu langsam, um sie auf Web-Seiten zu verwenden. Dieses Kapitel zeigt, wie Sie diese Steuerelemente auf Ihre Web-Seiten bringen und schnellere, schlankere Steuerelemente programmieren, die die Anwendung Ihrer Web-Seiten zum reinsten Vergnügen machen.
Es ist erstaunlich einfach, ein ActiveX-Steuerelement in eine Web-Seite einzubetten, die vom Microsoft Explorer 3.0 geladen wird. Sie verwenden das Tag <OBJECT>, eine relativ neue HTML-Erweiterung, die eine Vielzahl von Objekten beschreibt, die Sie in eine Web-Seite einfügen können: ein beweglicher Video-Clip, ein Sound, ein Java-Applet, ein ActiveX-Steuerelement sowie viele andere Informationstypen und Methoden der Benutzerinteraktion. In Listing 20.1 sehen Sie den HTML-Code für eine Seite, welche das Steuerelement Dieroll aus Kapitel 17, »Ein ActiveX-Steuerelement erstellen«, anzeigt.
Listing 20.1: Einsatz des <OBJECT>-Tags |
<HEAD>
<TITLE>A Web-Seite with a rolling die</TITLE>
</HEAD>
<BODY>
<OBJECT ID="Dieroll1"
CLASSID="CLSID:46646B43-EA16-11CF-870C-00201801DDD6"
CODEBASE="dieroll.cab#Version=1,0,0,1"
WIDTH="200"
HEIGHT="200">
<PARAM NAME="ForeColor" VALUE="0">
<PARAM NAME="BackColor" VALUE="16777215">
If you see this text, your browser does not support the OBJECT tag.
<BR>
</OBJECT>
<BR>
Here is some text after the die
</BODY>
</HTML>
Das Unschöne hier ist die CLSID. Die einfachste Möglichkeit, diese Klassen-ID einzufügen, besteht darin, sie aus der Objektbeschreibungsbibliothek dieroll.odl (engl. Object Descrip-tion Library) zu kopieren. Die Datei dieroll.odl läßt sich am schnellsten über das Register FileView des Developer Studio öffnen. Hier ist der Abschnitt in der dieroll.odl, in dem die CLSID angegeben ist:
// Klassen-Informationen für CDierollCtrl
[ uuid(46646B43-EA16-11CF-870C-00201801DDD6),
helpstring("Dieroll Control"), control ]
Dieser Abschnitt am Ende von dieroll.odl û die CLSIDs weiter oben in der Datei û beziehen sich nicht auf das gesamte Steuerelement, sondern nur auf Teile davon. Kopieren Sie die uuid innerhalb der Klammern in Ihren HTML-Code.
Das Attribut CODEBASE des <OBJECT>-Tags gibt an, wo die OCX-Datei gespeichert ist, so daß das ActiveX-Steuerelement automatisch heruntergeladen wird, wenn der Anwender noch keine Kopie besitzt. Die Verwendung der CLSID hat den folgenden Vorteil: Wenn der Anwender das betreffende ActiveX-Steuerelement bereits installiert hat, spart man sich die für einen Download erforderliche Zeit. Sie können auch einfach eine URL-Adresse zu der OCX-Datei angeben, doch um den DLL-Download zu automatisieren, zeigt das Attribut CODEBASE auf eine CAB-Datei. Indem Sie Ihr Steuerelement in einer CAB-Datei bereitstellen, wird die Download-Zeit reduziert. Mehr über die CAB-Technologie erfahren Sie unter http://www.microsoft.com/intdev/cab/. Diese Seite wurde für Java-Entwickler geschrieben, doch eignet sich diese Technik ebensogut, um die Download-Zeit von ActiveX-Steuerelementen herabzusetzen.
Der Rest der Attribute des <OBJECT>-Tag sollten recht intuitive sein, wenn Sie bereits Web-Seiten entwickelt haben: Die ID wird von anderen Tags auf der Seite verwendet, um auf das Steuerelement zu verweisen. WIDTH und HEIGHT bestimmen die Größe des Steuerelements in Pixeln. HSPACE und VSPACE sind die horizontalen und vertikalen Abstände in Pixeln zwischen dem Steuerelement und anderen Elementen der Web-Seite.
Text, den Sie zwischen dem Tag <OBJECT> und dem Tag </OBJECT> eingeben, wird von Browsern ignoriert, die das Tag <OBJECT> nicht kennen. Sie zeigen den Text zwischen den Tags an û in diesem Fall eine Textzeile, die darauf hinweist, daß der aktuelle Browser den Tag nicht unterstützt. Diese Verfahrensweise ist ein Teil der Spezifikation für einen Web-Browser: Unbekannte Tags sollen ignoriert werden.
In Abbildung 20.1 sehen Sie, wie diese Seite im Microsoft Explorer 3.0 angezeigt wird. Wenn Sie auf das Steuerelement klicken, wird gewürfelt, und alles funktioniert wunderbar. Scheint ja alles erstaunlich einfach zu sein; nur zwei Schönheitsfehler werden gleich offensichtlich:
In Abbildung 20.2 sehen Sie dieselbe Seite im Netscape Navigator 2.0, der das <OBJECT>-Tag nicht unterstützt und somit auch den Würfel nicht anzeigt. Und der Netscape Navigator wird von über der Hälfte aller Web-Surfer verwendet! Bedeutet dies das Aus für die Entwicklung von ActiveX-Steuerelementen für Web-Seiten? Ganz und gar nicht. Wie Sie im nächsten Abschnitt sehen werden, gibt es auch für Navigator-Benutzer eine Möglichkeit, dieselben Steuerelemente zu verwenden.
Die Frage des Umfangs eines Steuerelements ist das größere Problem. Die Auslieferungsversion des in Kapitel 17, »Ein ActiveX-Steuerelement erstellen«, entwickelten Steuerelements Dieroll beträgt 26 Kbyte. Viele Entwickler setzen ein Limit von 50 Kbyte pro Web-Seite für Grafiken und anderes Material, das heruntergeladen werden muß. Und dieses einfache Steuerelement nimmt bereits die Hälfte dieser Größenbeschränkung in Anspruch. Ein etwas leistungsfähigeres Steuerelement würde somit die gesetzte Grenze leicht überschreiten. Der überwiegende Teil dieses Kapitels beschäftigt sich damit, wie der Umfang solcher Steuerelemente reduziert bzw. die Download-Zeit für ActiveX-Steuerelemente sonst herabgesetzt werden kann. Dies setzt Web-Seiten-Designer in die Lage, die volle Leistungsfähigkeit eines Steuerelements zu nutzen, ohne sich um eines der härtesten Kritierien bei der Beurteilung von Web-Sites kümmern zu müssen, nämlich daß die Anwender ihre Seiten zu langsam finden werden.
Abbildung 20.1: Im Microsoft Internet Explorer können ActiveX-Steuerelemente angezeigt werden.
Abbildung 20.2: Der Netscape Navigator unterstützt die Anzeige von ActiveX-Steuerelementen nicht.
Es gibt noch einen dritten Schönheitsfehler, den Sie gar nicht bemerken werden, da Sie auf Ihrem System Visual C++ installiert haben. Das Steuerelement benötigt die MFC-DLL. Der Anwender muß sie herunterladen und installieren, damit er die Steuerelemente ausführen kann. Der Mechanismus, der Steuerelemente automatisch herunterlädt und installiert, lädt und installiert nicht auch automatisch diese DLL. (Obwohl Sie dies durch den Einsatz einer, bereits oben erwähnten, CAB-Datei realisieren könnten.)
Listing 20.2: Verwendung der Tags <OBJECT> und <EMBED> |
<HTML>
<HEAD>
<TITLE>A Web-Seite with a rolling die</TITLE>
</HEAD>
<BODY>
<OBJECT ID="Dieroll1"
CLASSID="CLSID:46646B43-EA16-11CF-870C-00201801DDD6"
CODEBASE="dieroll.cab#Version=1,0,0,1"
WIDTH="200"
HEIGHT="200">
<PARAM NAME="ForeColor" VALUE="0">
<PARAM NAME="BackColor" VALUE="16777215">
<PARAM NAME="Image" VALUE="beans.bmp">
<EMBED LIVECONNECT NAME="Dieroll1"
WIDTH="200"
HEIGHT="200"
CLASSID="CLSID:46646B43-EA16-11CF-870C-00201801DDD6"
TYPE="application/oleobject"
CODEBASE="dieroll.cab#Version=1,0,0,1"
PARAM_ForeColor="0"
PARAM_BackColor="16777215">
</OBJECT>
<BR>
Here is some text after the die
</BODY>
</HTML>
Das Tag <EMBED> sorgt dafür, daß das Plug-in geöffnet wird. Da es sich innerhalb des Tag-Paars <OBJECT>...</OBJECT> befindet, wird es vom Microsoft Internet Explorer und anderen Browsern, die das <OBJECT>-Tag unterstützen, ignoriert werden. Das bedeutet, daß dieser HTML-Code das Steuerelement im Netscape Navigator ebensogut anzeigt wie im Explorer. Es empfiehlt sich, in eine solche Web-Seite einen Link zur NCompass-Seite einzubinden, damit die Leser sich das Plug-in herunterladen können.
Microsoft hat sich das Ziel gesetzt, ActiveX-Steuerelemente als plattformübergreifende Multi-Browser-Lösung zu etablieren, gemäß seinem Slogan »Activate the Internet«. Die ActiveX-Steuerelement-Spezifikation ist kein firmeneigenes Dokument mehr, sondern wurde einem Normierungskomitee übergeben, das künftig für die Wartung dieses Standards zuständig ist. Lassen Sie sich also nicht davon beeinflussen, wenn manche Leute meinen, Sie sollten diese Steuerelemente nur dann erstellen, wenn Ihre Leser den Internet Explorer verwenden!
Abbildung 20.3: Mit dem Plug-in von NCompassLabs lassen sich ActiveX-Steuerelemente im Netscape Navigator anzeigen.
Leser, die mit einem mittleren Sicherheitsgrad arbeiten, sollten das Steuerelement Steuerelemente (als sicher fuer die Skript-Ausfuehrung)als sicher für die Sicherheitsgrad und ActiveX-SteuerelementeInitialisierung und Skript-Ausführung registrieren. Dadurch wird jedem, der eine Seite mit dem Steuerelement anzeigen will, versichert, daß keine Sicherheitslücken entstehen, egal, welche Funktionen von einem Skript aufgerufen werden oder welche Parameter durch das Attribut PARAM initialisiert werden. Ein Beispiel für ein Steuerelement, das die Sicherheit Ihres Systems gefährden könnte, wäre ein Steuerelement, das beim Ausführen eine Datei auf Ihrer Festplatte löscht. Die Standarddatei ist eine Datei, deren Fehlen Sie nicht bemerken oder die sehr wahrscheinlich nicht existiert. Eine Seite, die dieses Steuerelement in ein Skript eingebunden hat oder mit dem Attribut PARAM den Dateinamen initialisiert, könnte das Steuerelement anweisen, eine oder mehrere sehr wichtige Dateien zu löschen, da bestimmte Dateien von den meisten Anwendern im selben Verzeichnis gespeichert werden. So würde man etwa eine hohe Trefferquote erzielen, wenn man ein Steuerelement anweisen würde, die Datei C:\MSOFFICE\WINWORD\WINWORD.EXE, zu löschen. In Abbildung 20.4 sehen Sie die Fehlermeldung, die im Sicherheitsoptionen fuer ActiveX-SteuereleExplorer anzeigt wird, wenn Sie den mittleren Sicherheitsgrad aktiviert haben und eine Seite mit einem Steuerelement laden, das nicht als sicher für die Skript-Ausführung und Initialisierung registriert ist. Das NCompassLabs Plug-in Sicherheitsoptionen fuerControlActive verweigert ebenfalls das Laden von Steuerelementen, die nicht als sicher für die Skript-Ausführung und Initialisierung registriert sind.
Abbildung 20.4: Vom Explorer wird eine Meldung eingeblendet, die Sie vor sicherheitsbedenklichen Steuerelementen warnt.
Zunächst müssen Sie drei Funktionen in die Datei DierollCtl.cpp einfügen. (Diese stammen unverändert aus dem ActiveX SDK.) Diese Funktionen werden von einer Code-Sequenz aufgerufen, die weiter hinten in diesem Abschnitt präsentiert wird. Vergessen Sie nicht, Deklarationen dieser Funktionen auch in die Header-Datei aufzunehmen. Der Code wird in Listing 20.3 angeführt.
Listing 20.3: Neue Funktionen, mit denen ein Steuerelement als sicherheitstechnisch unbedenklich gekennzeichnet werden kann |
////////////////////////////////////////////////////////////////
// Aus der ActiveX SDK kopiert
// Der folgende Code dient zur Registrierung eines Steuerelements,
// als sicher hinsichtlich Initialisierung und Skript-Ausführung
// bzw. zum Aufheben einer solchen Registrierung.
HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
{
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (FAILED(hr))
return hr;
// Sicher stellen, daß der Schlüssel
// HKCR\Component Categories\{..catid...} registriert ist.
CATEGORYINFO catinfo;
catinfo.catid = catid;
catinfo.lcid = 0x0409 ; // english
// Sicher stellen, daß die bereitgestellte Beschreibung nicht
// zu lang ist. Ist dies der Fall, werden nur die ersten
Listing 20.3: Neue Funktionen, mit denen ein Steuerelement als sicherheitstechnisch unbedenklich gekennzeichnet werden kann (Forts.) |
// 127 Zeichen kopiert.
int len = wcslen(catDescription);
if (len>127)
len = 127;
wcsncpy(catinfo.szDescription, catDescription, len);
// Sicher stellen, daß die Beschreibung auf Null endet
catinfo.szDescription[len] = '\0';
hr = pcr->RegisterCategories(1, &catinfo);
pcr->Release();
return hr;
}
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Registrierung der Komponentkategorieninformationen.
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (SUCCEEDED(hr))
{
// Registerierung dieser Kategorie als durch die Klasse
// "implementiert".
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
}
if (pcr != NULL)
pcr->Release();
return hr;
}
HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
ICatRegister* pcr = NULL ;
HRESULT hr = S_OK ;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
if (SUCCEEDED(hr))
{
// Aufheben der Registerierung dieser Kategorie als durch die
// Klasse "implementiert".
CATID rgcatid[1] ;
rgcatid[0] = catid;
hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
}
if (pcr != NULL)
pcr->Release();
return hr;
}
Der zweite Schritt besteht darin, am Anfang der Datei DierollCtl.cpp zwei #include-Anweisungen einzufügen:
#include "comcat.h"
#include "objsafe.h"
Schließlich ändern Sie UpdateRegistry in der Datei DierollCtl.cpp, so daß die neuen Funktionen aufgerufen werden. Der neue Code ruft CreateComponentCategory auf, um die Kategorie "CATID_SafeForScripting" anzulegen, und fügt dann dieses Steuerelement in diese Kategorie ein. Anschließend wird die Kategorie "CATID_SafeForInitializing" angelegt und das Steuerelement auch in diese Kategorie eingefügt. In Listing 20.4 sehen Sie die neue Version von UpdateRegistry.
Listing 20.4: CDierollCtrl::CDierollCtrlFactory::UpdateRegistry() |
BOOL CDierollCtrl::CDierollCtrlFactory::UpdateRegistry(BOOL bRegister)
{
// ZU ERLEDIGEN: Prüfen Sie, ob Ihr Steuerelement den Thread-Regeln
// nach dem "Apartment"-Modell entspricht. Weitere Informationen
// finden Sie unter MFC TechNote 64.
// Falls Ihr Steuerelement nicht den Regeln nach dem Apartment-
// Modell entspricht, so müssen Sie den nachfolgenden Code ändern,
// indem Sie den 6. Parameter von
// afxRegInsertable | afxRegApartmentThreading auf
// afxRegInsertable ändern.
if (bRegister)
{
HRESULT hr = S_OK ;
// Sicherheitsregistrierung hinsichtlich der Skript-Ausführung
hr = CreateComponentCategory(CATID_SafeForScripting,
L"Controls that are safely scriptable");
if (FAILED(hr))
return FALSE;
hr = RegisterCLSIDInCategory(m_clsid, CATID_SafeForScripting);
if (FAILED(hr))
return FALSE;
// Sicherheitsregistrierung hinsichtlich der Initialisierung
hr = CreateComponentCategory(CATID_SafeForInitializing,
L"Controls safely initializable from persistent data");
if (FAILED(hr))
return FALSE;
hr = RegisterCLSIDInCategory(m_clsid, CATID_SafeForInitializing);
if (FAILED(hr))
return FALSE;
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_DIEROLL,
IDB_DIEROLL,
afxRegInsertable | afxRegApartmentThreading,
_dwDierollOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
{
HRESULT hr = S_OK ;
hr = UnRegisterCLSIDInCategory(m_clsid, CATID_SafeForScripting);
if (FAILED(hr))
return FALSE;
hr = UnRegisterCLSIDInCategory(m_clsid, CATID_SafeForInitializing);
if (FAILED(hr))
return FALSE;
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}
}
Wenn Sie überprüfen möchten, ob dies funktioniert, öffnen Sie den Explorer und stellen den Sicherheitsgrad Mittel ein. Laden Sie die HTML-Seite, die das Steuerelement verwendet; Sie sollten gewarnt werden, daß das Steuerelement nicht sicher ist. Nehmen Sie dann die obigen Änderungen vor, erstellen Sie das Steuerelement neu, und laden Sie die Seite wieder. Die Meldung wird nicht mehr erscheinen.
Die Java-Implementierung von Microsoft macht aus jeder Java-Klasse ein COM-Objekt. Das bedeutet, daß Sie mit Microsoft Visual J++ in Java auch ActiveX-Steuerelemente programmieren können. Doch wer das Thema »ActiveX versus Java« erörtert, meint meist »ActiveX versus Java-Applets«. Java-Applets sind kleine fest eingebettete Anwendungen, die auf einer Web-Seite ausgeführt werden, nicht aber als eigenständige Anwendungen ausgeführt werden können.
Viele Leute machen sich Gedanken wegen der Sicherheit beim Ausführen einer Anwendung, die sie nicht selbst geschrieben haben und die Person oder die Firma, die diese Anwendung bereitstellt, nicht kennen. Der Java-Ansatz beschränkt die für Applets erlaubten Aktionen, so daß selbst böswillige Applets keinen wirklichen Schaden anrichten können. Doch immer wiederkehrende Ankündigungen von Mängeln in diesem Ansatz erschüttern die Glaubwürdigkeit von Java. Selbst wenn ein Java-Applet als sicher eingestuft wird, sind es gerade diese Einschränkungen, die die Ausführung nützlicher Aufgaben verhindern.
Der von Microsoft mit ActiveX eingeschlagene Weg ist der Ansatz vertrauenswürdiger Lieferanten, der auf Java und jeden sonstigen ausführbaren Code erweiterbar ist. Der Code wird digital signiert, so daß Sie sichergehen können, von wem er bereitgestellt und daß er seit der Signierung nicht verändert wurde. Dadurch läßt sich nicht verhindern, daß auf Ihrem System unerwünschte Aktionen ausgeführt werden können û doch wenn dies geschieht, wissen Sie, wer dafür verantwortlich ist. Das ist auch nicht anders, als wenn Sie Software aus den Regalen des Computer-Handels kaufen. Genauere Informationen hierzu erhalten Sie, wenn Sie die Seite http://www.microsoft.com/ie/most/howto/trusted.htm aufschlagen und einigen Links auf dieser Seite folgen.
Der wohl größte Unterschied zwischen dem ActiveX- und dem Java-Applet-Ansatz ist der Download. Java-Code wird jedesmal heruntergeladen, wenn Sie die Web-Seite laden. ActiveX-Code hingegen wird nur einmal heruntergeladen oder anderweitig installiert (etwa von einer CD, die Sie aus einem Magazin bezogen haben). Auf der Festplatte des Benutzers wird eine Kopie gespeichert, und das Steuerelement wird in die Registrierung eingetragen. Der zu ladende Java-Code ist klein, da der größte Teil des beteiligten Codes bereits in der auf Ihrem Computer installierten Java Virtual Machine enthalten ist, die z.B. als Teil des Web-Browsers installiert wurde.
Der zu ladende ActiveX-Code kann umfangreicher sein, wenn auch die im nächsten Abschnitt erläuterten Optimierungsmaßnahmen den Umgang beträchtlich reduzieren können. Diese Optmierungsmaßnahmen stützen sich auf DLLs und anderen Code, die sich bereits auf dem Computer des Benutzers befinden. Wenn ein Anwender eine Seite mit einem ActiveX-Steuerelement nur einmal besucht, wird er wohl verärgert darüber sein, daß dadurch die Festplatte und die Registrierung »vollgemüllt« werden. Wenn eine Seite hingegen häufiger besucht wird, ist man froh, daß man sich die Download-Zeit spart: Das Steuerelement wird einfach aktiviert und ausgeführt.
Es gibt noch weitere Unterschiede: Java-Applets könnten keine Ereignisse auslösen, die den Container von einer bestimmten Aktion unterrichten. Java-Applets lassen sich nicht lizenzieren und unterscheiden oft nicht zwischen einer Entwurfs- und Laufzeitanwendung. Java-Applets können in Visual-Basic-Formularen, VC++-Programmen oder Word-Dokumenten nicht in derselben Weise eingesetzt werden wie ActiveX-Steuerelemente. ActiveX-Steuerelemente laufen etwa 10mal schneller als Java-Applets. Für Java-Applets spricht, daß sie definitiv platformunabhängig und typischerweise kleiner als das entsprechende ActiveX-Steuerelement sind.
Microsoft hat OCX-Steuerelemente ursprünglich nicht für den Einsatz auf Web-Seiten entwickelt, und die Namensänderung auf ActiveX-Steuerelemente hat sie nicht kleiner gemacht und auch nicht die Zeit reduziert, die zum Laden erforderlich ist. Aber es wurden auch einige Änderungen in der Spezifikation vorgenommen, die sich in den Optionen des Steuerelement-Assistent widerspiegeln. Groesse mit Hilfe des Assistenten reduzierenSteuerelementeGroesse mit Hilfe des Assistenten reduzierenDownload-Zeit fuer Steuerelemente reduzieIn diesem Kapitel werden diese Optionen für das bereits einmal erstellte Steuerelement Dieroll aktiviert, um die Auswirkung zu demonstrieren. Da Dieroll bereits ein recht schlankes und schnell ladbares Steuerelement ist, werden diese einfachen Änderungen keinen großen Unterschied ausmachen. Dennoch lohnt es sich, diese Techniken zu erlernen, denn Ihre eigenen Steuerelemente werden sicher umfangreicher sein als Dieroll.
Die ersten zwei Optionen zur Reduzierung der Steuerelementgröße waren in Schritt 2 des Steuerelement-Assistenten schon immer verfügbar: ActiveX-Steuerelementgroesse reduzierenElementgroesse reduzieren
Wenn Sie Ihr Steuerelement ganz für das Web entwickeln, sind viele dieser Einstellungen unwichtig. So spielt es keine Rolle, ob das Steuerelement über ein Info-Dialogfeld verfügt, denn von der Web-Seite aus läßt sich dieses Dialogfeld ohnehin nicht öffnen.
Ganz im Gegensatz dazu ist die Option Aktivieren, wenn sichtbar sehr wichtig. Die Aktivierung eines Steuerelements erfordert einiges an zusätzlicher Rechenleistung und sollte möglichst lange hinausgezögert werden, so daß Ihr Steuerelement scheinbar sehr schnell geladen wird. Wird ein Steuerelement aktiviert, sobald es sichtbar ist, so wird die dafür erforderliche Zeit im Prinzip zur Download-Zeit addiert. Um diese Option im vorhandenen Dieroll-Code zu deaktivieren, öffnen Sie das Projekt im Developer Studio und öffnen dann im Register FileView die Datei DierollCtl.cpp. Suchen Sie nach einem Code-Abschnitt wie den in Listing 20.5 abgebildeten.
Listing 20.5: Aktivieren, wenn sichtbar |
/////////////////////////////////////////////////////////////////////////////
// Steuerelement-Typinformation
static const DWORD BASED_CODE _dwDierollOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CDierollCtrl, IDS_DIEROLL, _dwDierollOleMisc)
Löschen Sie die Zeile OLEMISC_ACTIVATEWHENVISIBLE. Erstellen Sie eine Auslieferungsversion der Anwendung. Obwohl die Größe der OCX-Datei unverändert ist, sollten Web-Seiten mit diesem Steuerelement schneller geladen werden, da das Fenster erst erstellt wird, wenn der Anwender auf den Würfel klickt. Aktualisieren Sie die Web-Seite mit dem Würfel darin, werden Sie den ersten Wert sofort sehen, obwohl das Steuerelement inaktiv ist. Das Fenster wird erstellt, um Mausklicks abzufangen, nicht um den Würfel anzuzeigen.
Doch es sind noch weitere Optimierungen verfügbar. In der Abbildung 20.5 wird die Liste der erweiterten Optionen des ActiveX-Steuerelement-Assistenten angezeigt. Dieses Dialogfeld wird eingeblendet, wenn Sie in Schritt 2 auf die Schaltfläche Erweitert klicken. Sie können jede dieser Optionen auswählen, wenn Sie die Anwendung mit Hilfe des Steuerelement-Assistenten erstellen. Es ist auch möglich, diese Einstellungen in einer bereits erstellten Anwendung nachträglich zu ändern. Diese Optionen sind die folgenden:
Die fensterlose Aktivierung wird wegen ihrer Vorteile wohl sehr beliebt werden. Für ein durchsichtiges Steuerelement oder ein Steuerelement, das nicht die Form eines Rechtecks haben soll, sollten Sie diese Option aktivieren. Da sie jedoch auch den Codeumfang reduziert und die Ausführung beschleunigt, sollte man bei jedem Steuerelement überlegen, ob man es mit dieser Option erstellt. Moderne Container werden die Funktionalität für das Steuerelement bereitstellen. In älteren Containern wird das Fenster des Steuerelements ohnehin erstellt, zwar unter Verlust der genannten Vorzüge, doch ist dadurch gewährleistet, daß das Steuerelement funktioniert.
Abbildung 20.5: Erweiterte ActiveX-Optionen.
Um die Option Fensterlose Aktivierung in Dieroll zu implementieren, überschreiben Sie CDierollCtrl::GetControlFlags wie folgt:
DWORD CDierollCtrl::GetControlFlags()
{
return COleControl::GetControlFlags()
| windowlessActivate;
}
Sie fügen diese Funktion schnell ein, wenn Sie im Register ClassView mit der rechten Maustaste auf CDierollCtrl klicken und den Befehl Member-Funktion hinzufügen wählen. Definieren Sie die Funktion für Dieroll, erstellen Sie das Steuerelement neu, und laden Sie erneut die Web-Seite, in der es vorkommt. Sie werden keinen offensichtlichen Effekt bemerken, da Dieroll ein so schlankes Steuerelement ist. Was Sie bemerken werden, ist folgendes: Es funktioniert immer noch perfekt, obwohl es kein Fenster besitzt.
Die nächsten zwei Optionen, ActiveX-Steuerelement- (Option Unbegrenzter GeraetekontextOption Unbegrenzter GeraetekontextActiveX-Steuerelement-Assistendie Optionen Unbegrenzter Gerätekontext und Nicht flimmernde Aktivierung, sind für fensterlose Steuerelemente nicht verfügbar. In einem Steuerelement mit einem Fenster bedeutet die Auswahl der Option Unbegrenzter Gerätekontext, daß Sie mit absoluter Sicherheit nie außerhalb des Client-Rechtecks des Steuerelements zeichnen. Diese Option sollte nur mit Bedacht eingesetzt werden, da ein Fehler im Zeichencode zu Problemen führen kann. In Dieroll würde die Überladung von GetControlFlags folgenden Inhalt haben:
DWORD CDierollCtrl::GetControlFlags()
{
return COleControl::GetControlFlags()
& ~clipPaintDC;
}
Kombinieren Sie diese Option nie mit der fensterlosen Aktivierung; dann passiert gar nichts.
Die Option Nicht flimmernde Aktivierung eignet sich für Steuerelemente, bei denen aktive und inaktive Darstellung gleich ist. (Denken Sie an Kapitel 15, »Eine ActiveX-Server-Anwendung«, zurück, in dem das Server-Objekt mit abgeblendeten Farben gezeichnet wurde, wenn die Objekte inaktiv waren.) Wenn kein Bedarf für einen Neuaufbau der Darstellung besteht, sollten Sie diese Option aktivieren und so den zweiten Aufbau verhindern. Dies hat den Vorteil, daß der Anwender beim Aktivieren und Deaktivieren des Steuerelements kein störendes Flimmern wahrnimmt. Und die Aktivierung wird geringfügig schneller vollzogen. Um diese Option in Dieroll zu integrieren, müßten Sie GetControlFlags wie folgt überschreiben:
DWORD CDierollCtrl::GetControlFlags()
{
return COleControl::GetControlFlags()| noFlickerActivate;
}
Wie auch die Option Unbegrenzter Gerätekontext sollten Sie die Option Nicht flimmernde Aktivierung nicht mit der Option Fensterlose Aktivierung kombinieren, sonst geschieht nichts.
Durch die Option Mauszeiger-Benachrichtigung, falls inaktiv kann so manches Steuerelement auf die Option Aktivieren, wenn sichtbar verzichten, da sie ein eigenes Fenster nur für die Reaktion auf Mausnachrichten benötigt hätten. Wenn der einzige Grund für die Aktivierung ein zur Verarbeitung von Mausaktionen benötigtes Fenster ist, so sollten Sie diese Option aktivieren. Denn dadurch werden diese Interaktionen durch eine IPointerInactive-Schnittstelle an den Container übergeben. Um diese Option für eine bereits erstellte Anwender zu aktivieren, überschreiben Sie wieder GetControlFlags:
DWORD CDierollCtrl::GetControlFlags()
{
return COleControl::GetControlFlags()| pointerInactive;
}
Jetzt erhält Ihr Code die Nachrichten WM_SETCURSOR und WM_MOUSEMOVE über Nachrichtentabelleneinträge, obwohl Sie gar kein Fenster haben. Der Container, dessen Fenster Ihr Steuerelement verwendet, wird diese Nachrichten über die IPointerInactive-Schnittstelle senden.
Es gibt vielleicht noch einen Fall, in dem Sie Fensternachrichten in inaktivem Zustand ohne Fenster verarbeiten möchten: wenn ein Objekt über dem Steuerelement abgelegt wird. Das Steuerelement muß genau in diesem Moment aktiviert werden, damit ein Fenster als Ablageziel existiert. Sie erreichen dies durch Überladen von GetActivationPolicy:
DWORD CDierollCtrl::GetActivationPolicy()
{
return POINTERINACTIVE_ACTIVATEONDRAG;
}
Diese Mühe müssen Sie sich selbstverständlich nicht machen, wenn Ihr Steuerelement gar nicht als Ablageziel dienen kann.
Wenn der Container Ihre Nachricht über die IPointerInactive-Schnittstelle weiterleiten soll, gibt es ein Problem: Vielleicht weiß der Container gar nicht, daß eine solche Schnittstelle exisitert. Wenn Sie damit rechnen, daß Ihr Steuerelement in einen solchen Container eingebunden wird, sollten Sie das Flag OLEMISC_ACTIVATEWHENVISIBLE nicht aus dem Code-Segment entfernen (vgl. Listing 20.6).
Listing 20.6: Flag setzen für die Option »Aktivieren, wenn sichtbar« |
/////////////////////////////////////////////////////////////////////////////
// Steuerelement-Typinformation
static const DWORD BASED_CODE _dwDierollOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CDierollCtrl, IDS_DIEROLL, _dwDierollOleMisc)
Statt dessen kombinieren Sie das Flag OLEMISC_IGNOREACTIVATEWHENVISIBLE mit Hilfe eines bitweisen ODER-Operators mit diesen Flags. Dieses Flag ist von Containern interpretierbar, die IPointerInactive kennen, und zwar folgendermaßen: »Ich nehme es zurück, also trotz allem nicht aktivieren, wenn sichtbar«. Container, die IPointerInactive nicht verstehen, können auch dieses Flag nicht interpretieren, und Ihr Steuerelement wird aktiviert, wenn sichtbar, und kann somit Mausnachrichten entgegennehmen.
Die Option Optimierte Zeichenfunktionen ist nur für Steuerelemente sinnvoll, die den Container mit einer Reihe anderer Zeichensteuerelemente teilen. Wie Sie aus Kapitel 6, »Zeichnen auf dem Bildschirm«, wissen, wird zum Zeichnen typischerweise die Farbrolle, der Stift oder ein anderes GDI-Objekt auf einen neuen Wert gesetzt, der alte Wert gespeichert, dann das GDI-Objekt verwendet und anschließend der gespeicherte Wert wieder hergestellt. Wenn dies für mehrere Steuerelemente abwechselnd geschieht, könnten alle diese Schritte zugunsten einer einzigen Wiederherstellung am Ende des Zeichenvorgangs entfallen. Der Container speichert sämtliche GDI-Objektwerte, bevor er die Steuerelemente zu einem Neuaufbau anweist, und stellt sie erst anschließend wieder her.
Soll Ihr Steuerelement diese Möglichkeit nutzen, so müssen zwei Änderungen vorgenommen werden. Wenn ein Stift oder sonstiges GDI-Objekt zwischen den einzelnen Zeichenaufrufen erhalten bleiben soll, darf er einen bestimmte Gültigkeitsbereich nicht verlassen. Dies bedeutet, daß lokale Stifte, Farbrollen oder Schriften in Mitgliedsvariablen abgelegt werden sollten, damit sie zwischen den Funktionsaufrufen erhalten bleiben. Als zweite Änderung sollte der Code zum Wiederherstellen des alten Objekts von einer if-Anweisung eingerahmt werden. Diese ruft COleControl::IsOptimizedDraw auf, um festzustellen, ob der Container die optimierte Darstellung überhaupt unterstützt. Eine typische Zeichenroutine würde die Farben einstellen und dann wie folgt weiter verfahren:
...
if(!m_pen.m_hObject)
{
m_pen.CreatePen(PS_SOLID, 0, forecolor);
}
if(!m_brush.m_hObject)
{
m_brush.CreateSolidBrush(backcolor);
}
CPen* savepen = pdc->SelectObject(&m_pen);
CBrush* savebrush = pdc->SelectObject(&m_brush);
...
// Gerätekontext verwenden
...
if(!IsOptimizedDraw())
{
pdc->SelectObject(savepen);
pdc->SelectObject(savebrush);
}
...
Zuerst wird überprüft, ob die Zeichenwerkzeuge nicht schon existieren. Solange m_hObject nicht NULL ist, müssen sie nicht erstellt werden. Und wenn dieser Container zudem die optimierten Zeichenfunktionen unterstützt, brauch er auch nicht wiederhergestellt werden.
Wenn Sie die Option Optimierte Zeichenfunktionen im Steuerelement-Assistenten einstellen, wird die if-Anweisung mit dem Aufruf von IsOptimizedDraw in den Zeichencode eingefügt, und zwar mit einigen »Zu erledigen«-Kommentaren.
Die letzte der Optimierungsoptionen lädt Eigenschaftswerte asynchron und wird im nächsten Abschnitt behandelt.
Schnellere Internet-Verbindungen und kompaktere Bildformate haben die Dringlichkeit hierfür etwas herabgesetzt. Dennoch ist die asynchrone Verarbeitung eine gute Sache. So haben viele Web-Anwender beim Warten auf Video-Clips, Sound-Clips und ausführbaren Code schon die guten, alten Zeiten heraufbeschworen, in denen man nur 30 Sekunden auf eine Seite warten mußte, bis alle ihre Bilder angezeigt waren.
Der Würfel, der in Ihrer Web-Seite angezeigt wird, ist die Standarddarstellung des Würfels. Es gibt keine Möglichkeit für den Anwender, auf die Eigenschaften des Steuerelements zuzugreifen. Der Web-Seiten-Entwickler kann das Attribut <PARAM> innerhalb des Tags <OBJECT> verwenden. (Browser, die den Tag <OBJECT> ignorieren, ignorieren auch den Tag <PARAM>.) Wenn Sie die folgenden Zeile in Ihren HTML-Code einsetzen, und zwar zwischen <OBJECT> und </OBJECT>, wird anstelle der Punkte eine Zahl angezeigt.
<PARAM NAME="Dots" value="0">
Der Tag <PARAM> besitzt zwei Attribute: NAME bezieht sich auf einen Namen, der dem externen ActiveX-Namen entspricht (in diesem Fall Dots), und value auf den Wert (in diesem Fall 0 bzw. FALSE). Auf dem Würfel werden nun Ziffern angezeigt.
Erhält Dieroll nun eine weitere Eigenschaft, etwa eine umfangreiche Bilddatei, so ergibt sich eine weitere Verzögerung durch das Laden dieser Bilddatei (wo immer diese gespeichert sein mag). Wenn inzwischen nichts passiert, verliert der Anwender irgendwann die Lust, auf ein leeres Quadrat zu stieren, und öffnet eine andere Seite. Der Einsatz asynchroner Eigenschaften bedeutet, daß das Steuerelement zunächst grob gezeichnet und seine Funktionalität bereits genutzt werden kann, während die große Bilddatei noch heruntergeladen wird. Für Dieroll reicht es aus, mit Hilfe von GetBackColor einen einfachen Hintergrund zu zeichnen, bis die Bilddatei bereit ist. Webseitenhintergrund-hintergrund (als Ersatz fuer Bild-DownloadsWebseitenersatz fuer Bild-Downloads
Der Ausdruck BLOB steht für engl. Binary Large OBject. Es ist der Überbegriff für Dinge wie die Bilddatei, die in das Steuerelement Dieroll aufgenommen werden soll. Die Kommunikation zwischen Steuerelement und BLOB geschieht durch ein Verweisobjekt (Moniker). Das ist keine Neuheit, nur wurden Moniker bisher in OLE verborgen. Wenn Sie die Funktionsweise von solchen Verweisobjekten bereits kennen, gibt es dennoch viel Neues zu lernen, denn durch die Einführung asynchroner Moniker ändert sich einiges. Wenn Sie nie zuvor davon gehört haben, auch kein Problem. Mit der Zeit wird es ganz verschiedene asynchrone Moniker geben, doch bisher wurden nur URL-Moniker implementiert. Dabei handelt es sich um Methoden für ActiveX, BLOB-Eigenschaften mit URLs zu verbinden. Wenn Sie darauf vertrauen, daß ActiveX dies für Sie erledigt, können Sie erstaunliche Dinge realisieren. Der Rest dieses Teilabschnitts erläutert, wie Sie mit Hilfe von URL-Monikern BLOB-Eigenschaften asynchron laden.
Wenn Sie sich erinnern, ist der Zweck dieser Aktion, daß das Steuerelement sich bereits zu zeichnen beginnt, wenn es auch noch nicht alle seine Eigenschaften annehmen kann. Der OnDraw-Code wird dazu folgendermaßen strukturiert:
// Vorbereitung für das Zeichnen
if(AllPropertiesAreLoaded)
{
// mit BLOB zeichnen
}
else
{
// ohne BLOB zeichnen
}
// Bereinigung nach dem Zeichnen
Hierbei sind zwei Probleme zu lösen. Erstens wird dies der Test sein, um festzustellen, ob alle Eigenschaften geladen sind. Zweitens muß ein Weg gefunden werden, wie OnDraw wieder aufgerufen wird, wenn alle Eigenschaften bereit sind, wenn diese Funktion bereits zum Zeichnen des Steuerelements ohne BLOBs aufgerufen wurde.
Das erste Problem wurde bereits gelöst, indem zwei neue Funktionen in COleControl aufgenommen wurden. GetReadyState liefert einen der folgenden Werte:
Die Funktion InternalSetReadyState setzt den Ready-Status auf einen dieser Werte.
Das zweite Problem, nämlich ein zweiter Funktionsaufruf von OnDraw, nachdem das Steuerelement bereits ohne BLOB gezeichnet wurde, wurde durch die neue Klasse CDataPathProperty und der daraus abgeleiteten Klasse CCachedDataPathProperty gelöst. Diese Klassen besitzen die Mitgliedsfunktion OnDataAvailable, welche die Windows-Nachricht abfängt, die beim Abrufen der Eigenschaft von dem entfernten Site erzeugt wird. Die Funktion OnDataAvailable macht das Steuerelement ungültig und erzwingt einen Neuaufbau.
Erstellen Sie eine Kopie des Inhalts des Ordners Dieroll, den Sie in Kapitel 17, »Ein ActiveX-Steuerelement erstellen«, erstellt haben (oder des Kapitel-17-Codes auf der CD), und ändern Sie ihn auf eine fensterlose Aktivierung, wie es am Anfang dieses Kapitels beschrieben wurde. Jetzt können Sie beginnen. Zur Implementierung von asynchronen Eigenschaften sind einige Aufgaben zu erledigen, doch ist jeder Arbeitsgang sehr direkt.
Die Klasse CDierollDataPathProperty hinzufügen: Öffnen Sie den Klassen-Assistenten und darin das Register Automatisierung. Klicken Sie auf die Schaltfläche Klasse hinzufügen, und wählen Sie aus dem Drop-down-Menü den Befehl Neu. Das Dialogfeld Neue Klasse wird eingeblendet. Weisen Sie der Klasse den Namen CDierollDataPathProperty zu. Wählen Sie aus der Drop-down-Liste Basisklasse den Eintrag CCachedDataPathProperty aus. Das ausgefüllte Dialogfeld sollte jetzt der Abbildung 20.6 entsprechen. Klicken Sie auf OK, um die Klasse zu erstellen und zum Projekt hinzuzufügen.
Abbildung 20.6: Erstellen Sie eine neue Klasse für die Verarbeitung asynchroner Eigenschaften.
Die neue Klasse soll von CCachedDataProperty abgeleitet sein, da es die Eigenschaftsinformationen in eine Datei laden soll, und dies ist eine einfachere Methode, die Bitmap-Grafik zu behandeln. Besitzt das Steuerelement eine Eigenschaft, die heruntergeladen wurde, weil sie sich oft ändert (z.B. das aktuelle Wetter), wäre CDataPathProperty die geeignetere Basisklasse.
Die Bildeigenschaft in Property zu CDierollCtrl hinzufügen: Nachdem Sie für das Steuerelement Dieroll die neue Klasse CDierollDataPathProperty definiert haben, fügen Sie die Eigenschaft wie folgt in die Klasse CDierollCtrl ein: Öffnen Sie im Klassen-Assistenten das Register Automatisierung, stellen Sie sicher, daß die Klasse CDierollCtrl ausgewählt ist, und klicken Sie auf die Schaltfläche Eigenschaft hinzufügen. Füllen Sie das Dialogfeld entsprechend der Abbildung 20.7 aus. Der hier festgelegte externe Name wird im HTM-Code erscheinen: Image ist einfach und erfordert nicht viel Tipparbeit. Der Typ sollte OLE_BSTR (Option BSTR) sein û diese Option werden Sie nur in der Drop-down-Liste finden, wenn Sie die Implementierung auf Get/Set-Methode ändern.
Abbildung 20.7: Die Bilddatei wird als BSTR-Eigenschaft hinzugefügt.
Die Get- und Set-Funktionen werden in die Steuerelementklasse aufgenommen, doch die »Zu erledigen«-Kommentare (vg. Listing 20.7) sind etwas kryptisch.
Listing 20.7: Get- und Set-Funktionen für die Image-Eigenschaft |
BSTR CDierollCtrl::GetImage()
{
CString strResult;
// ZU ERLEDIGEN: Fügen Sie hier Ihre Behandlungsroutine für
// Eigenschaften hinzu
return strResult.AllocSysString();
}
void CDierollCtrl::SetImage(LPCTSTR lpszNewValue)
{
// ZU ERLEDIGEN: Fügen Sie hier Ihre Behandlungsroutine für
// Eigenschaften hinzu
SetModifiedFlag();
}
Wie bei anderen Get- und Set-Eigenschaften werden Sie eine Mitgliedsvariable in die Steuerelementklasse einfügen müssen und in diese Funktionen den Code zum Abrufen bzw. Setzen ihrer Werte einfügen. Sie ist eine Instanz der neuen Klasse CDierollDataPathProperty. Klicken Sie im Register ClassView mit der rechten Maustaste auf die Klasse CDierollCtrl, und wählen Sie Member-Variable hinzufügen. In Abbildung 20.8 sehen Sie, wie das Dialogfeld auszufüllen ist, um die Mitgliedsvariable mdpp_image zu deklarieren. (Das dpp im Namen soll daran erinnern, daß es sich um eine Datenpfadeigenschaft (engl. data path property) handelt.
Abbildung 20.8: Die Bilddatei-Mitgliedsvariable ist eine Instanz der neuen Klasse.
Jetzt können Sie die Get- und Set-Funktionen fertigstellen (siehe Listing 20.8).
Listing 20.8: Fertiggestellte Get- und Set-Funktionen |
BSTR CDierollCtrl::GetImage()
{
CString strResult;
strResult = mdpp_image.GetPath();
return strResult.AllocSysString();
}
void CDierollCtrl::SetImage(LPCTSTR lpszNewValue)
{
Load(lpszNewValue, mdpp_image);
SetModifiedFlag();
}
Fügen Sie am Anfang der Header-Datei für CDierollCtrl die folgende include-Anweisung ein:
#include "DierollDataPathProperty.h"
Nun gilt es noch einige Anpassungen vorzunehmen, da Sie ja ein vorhandenes Steuerelement ändern und nicht beim Erstellen des Steuerelements einfach die Option Asynchrones Laden der Eigenschaften aktiviert hatten. Zunächst sorgen Sie in CDierollCtrl::DoPropExchange für Persistenz und die Initialisierung von mdpp_image, indem Sie die folgende Zeile einfügen:
PX_DataPath( pPX, _T("Image"), mdpp_image);
Danach fügen Sie in das vom Klassen-Assistenten für CDierollCtrl::OnResetState angelegte Code-Gerüst eine Zeile ein, damit beim Zurücksetzen des Steuerelements auch die Datenpfadeigenschaft zurückgesetzt wird. Der Inhalt der Funktion ist in Listing 20.9 abgebildet.
Listing 20.9: CDierollCtrl::OnResetState() |
////////////////////////////////////////////////////////////////////////
// CDierollCtrl::OnResetState û Setzt das Steuerelement in den
// Standardzustand zurück
void CDierollCtrl::OnResetState()
{
COleControl::OnResetState(); // Setzt die Standards zurück, die in
// DoPropExchange gefunden wurden
mdpp_image.ResetData();
}
Das Ereignis ReadyStateChange und die Eigenschaft ReadyState hinzufügen: Verwenden Sie den Klassen-Assistenten, um das Standardereignis ReadyStateChange hinzuzufügen. Dazu öffnen Sie das Register ActiveX-Ereignisse und klicken auf die Schaltfläche Ereignis hinzufügen. Wählen Sie aus der Drop-down-Liste den externen Namen ReadyStateChange aus, und klicken Sie auf OK. In Abbildung 20.9 sehen Sie das Dialogfeld Ereignis hinzufügen. Steuerelement-Ereignisse, die in Kapitel 17 erörtert wurden, benachrichtigen den Container des Steuerelements, daß mit dem Steuerelement eine Aktion durchgeführt wurde. In diesem Fall sind die restlichen Daten des Steuerelements angekommen, und der Status des Steuerelements wurde geändert.
Abbildung 20.9: Fügen Sie ein Standardereignis hinzu, das den Container über den Bereitschaftsstatus des Steuerelements benachrichtigt.
Fügen Sie mit Hilfe des Klassen-Assistenten in CDierollCtrl eine Eigenschaft für den Bereitschaftsstatus ein. Dazu öffnen Sie das Register Automatisierung und klicken auf die Schaltfläche Eigenschaft hinzufügen. Wählen Sie aus der Drop-down-Liste den Eintrag ReadyState û und da es sich hier um eine vordefinierte Standardeigenschaft handelt, wird der Rest des Dialogfelds automatisch ausgefüllt (vgl. Abbildung 20.10). Der Klassen-Assistent fügt kein Funktionsgerüst für GetReadyState in den Code ein, da CDierollCtrl dieses von COleControl erben wird.
Abbildung 20.10: Mit Hilfe einer Standardeigenschaft läßt sich der Bereitschaftsgrad des Steuerelements nachverfolgen.
Fügen Sie Code in den Konstruktor ein, um die zwischengespeicherte Eigenschaft mit diesem Steuerelement zu verbinden und die Mitgliedsvariable in COleControl zu initialisieren, die, in COleControl::GetReadyState verwendet, durch COleControl::InternalSetReadyState gesetzt wird. Da das Steuerelement sofort verwendet werden kann, sollte der Bereitschaftsstatus mit READYSTATE_INTERACTIVE beginnen. In Listing 20.10 wird der neue Konstruktor aufgezeigt:
Listing 20.10: CDierollCtrl::GetImage() |
CDierollCtrl::CDierollCtrl()
{
InitializeIIDs(&IID_DDieroll, &IID_DDierollEvents);
mdpp_image.SetControl(this);
m_lReadyState = READYSTATE_INTERACTIVE;
}
CDierollDataPathProperty implementieren: In CDierollDataPathProperty müssen noch einige Arbeiten durchgeführt werden, bevor CDierollCtrl::OnDraw geändert werden kann. Diese Klasse lädt eine Bitmap-Grafik, doch kann in diesem Kapitel nicht alles erklärt werden, was beim Lesen einer BMP-Datei in einem CBitmap-Objekt geschieht. Die wichtigste Funktion ist OnDataAvailable, die Sie in Listing 20.11 finden. Fügen Sie diese Funktion in die Klasse ein, indem Sie im Register ClassView mit der rechten Maustaste auf CDierollCtrl klicken und Virtuelle Funktion hinzufügen wählen. Markieren Sie in der Liste links die Funktion OnDataAvailable, klicken Sie auf Hinzufügen und Bearbeiten, und geben Sie den Code aus Listing 20.11 ein.
Listing 20.11: OnDataAvailable() |
void CDierollDataPathProperty::OnDataAvailable(DWORD dwSize, DWORD grfBSCF)
{
CCachedDataPathProperty::OnDataAvailable(dwSize, grfBSCF);
if(grfBSCF & BSCF_LASTDATANOTIFICATION)
{
m_Cache.SeekToBegin();
if (ReadBitmap(m_Cache))
{
BitmapDataLoaded = TRUE;
// Ist sicher, da dieses Steuerelement nur eine Eigenschaft
// besitzt:
GetControl()->InternalSetReadyState(READYSTATE_COMPLETE);
GetControl()->InvalidateControl();
}
}
}
Jedesmal, wenn ein Datenblock empfangen wird, wird diese Funktion aufgerufen. Die erste Codezeile verwendet die Basisklassenversion der Funktion, um den Block zu verarbeiten und das Flag grfBSCF zu setzen. Wenn nach der Verarbeitung des letzten Blocks der Download beendet ist, wird die Funktion ReadBitmap aufgerufen, um die zwischengespeicherten Daten in ein Bitmap-Objekt einzulesen, das als Hintergrund des Steuerelements angezeigt werden kann. Der Code für ReadBitmap wird an dieser Stelle nicht präsentiert oder erörtert, jedoch auf der CD bereitgestellt, falls Sie sich dafür interessieren. Sobald die Bitmap-Grafik eingelesen ist, ist der Bereitschaftsstatus des Steuerelements vollständig, und der Aufruf von InvalidateControl sorgt für einen Neuaufbau.
CDierollCtrl::OnDraw überprüfen: Die Struktur von CDierollCtrl::OnDraw wurde Ihnen bereits präsentiert. Der Hintergrund wird vor dem Code ausgefüllt, der überprüft, ob Punkte oder Ziffern anzuzeigen sind, und zwar in dem folgenden Codeblock:
COLORREF back = TranslateColor(GetBackColor());
CBrush backbrush;
backbrush.CreateSolidBrush(back);
pdc->FillRect(rcBounds, &backbrush);
Ersetzen Sie diesen Block durch den in Listing 20.12 angeführten.
Listing 20.12: Neuer Code für OnDraw() |
CBrush backbrush;
BOOL drawn = FALSE;
if (GetReadyState() == READYSTATE_COMPLETE)
{
CBitmap* image = mdpp_image.GetBitmap(*pdc);
if (image)
{
CDC memdc;
memdc.CreateCompatibleDC(pdc);
memdc.SelectObject(image);
BITMAP bmp; // nur für die Höhe und Breite
image->GetBitmap(&bmp);
pdc->StretchBlt(0, // links oben
0, // rechts oben
rcBounds.Width(), // Zielbreite
rcBounds.Height(), // Zielhöhe
&memdc, // das Bild
0, // Abstand zum Bild -x
0, // Abstand zum Bild -y
bmp.bmWidth, // Breite
bmp.bmHeight, // Höhe
SRCCOPY); // kopieren
drawn = TRUE;
}
}
if (!drawn)
{
COLORREF back = TranslateColor(GetBackColor());
backbrush.CreateSolidBrush(back);
pdc->FillRect(rcBounds, &backbrush);
}
Die gezeichnete BOOL-Variable stellt sicher, daß das Steuerelement auf die alte Weise gezeichnet wird, falls das Steuerelement zwar vollständig ist, beim Verwenden der Bitmap jedoch ein Fehler entsteht. Ist das Steuerelement vollständig, so wird das Bild in ein CBitmap-Objekt geladen und anschließend in den Gerätekontext gezeichnet. Bitmaps können nur in einem Speicher-Gerätekontext ausgewählt und dann in einen gewöhnlichen Gerätekontext kopiert werden. Durch den Einsatz von StretchBlt wird die Bitmap-Grafik beim Kopieren gedehnt, so daß sie den ganzen Darstellungsbereich des Steuerelements ausfüllt. Der alte Zeichencode ist noch vorhanden und wird verwendet, falls beim Zeichnen ein Fehler auftritt.
Wählen Sie im Developer Studio den Befehl Projekt/Einstellungen. Öffnen Sie das Register Debug, und stellen Sie sicher, daß im Listenfeld links alle Zeilen ausgewählt sind. Wählen Sie aus der oberen Drop-down-Liste die Option Allgemein, tragen Sie in das Eingabefeld Ausführbares Programm für Debug-Sitzung den vollständigen Pfad zum Microsoft Internet Explorer ein (in Abbildung 20.11 sehen Sie ein Beispiel), und wählen Sie OK. Wenn Sie jetzt Erstellen/Ausführen wählen oder in der Symbolleiste auf die Schaltfläche Programm ausführen klicken, wird der Explorer geladen. Öffnen Sie eine Seite mit HTML-Code, der das Steuerelement lädt, und das Steuerelement wird im Debugger ausgeführt. Wie bei jeder anderen Anwendung können Sie Haltepunkte setzen, den Code schrittweise durchlaufen und Variablen untersuchen.
Abbildung 20.11: Legen Sie fest, daß bei der Fehlersuche für das Steuerelement der Explorer ausgeführt wird.
Hier ist die Syntax für ein <OBJECT>-Tag, in dem die Eigenschaft Image gesetzt wurde.
<OBJECT
CLASSID="clsid:46646B43-EA16-11CF-870C-00201801DDD6"
CODEBASE="http://www.gregcons.com/test/dieroll.ocx"
ID=die1
WIDTH=200
HEIGHT=200
ALIGN=center
HSPACE=0
VSPACE=0
>
<PARAM NAME="Dots" VALUE="1">
<PARAM NAME="Image" VALUE="http://www.gregcons.com/test/beans.bmp">
If you see this text, your browser does not support the OBJECT tag. </BR>
</OBJECT>
In Abbildung 20.12 sehen Sie das Steuerelement mit einem Hintergrundbild. Zum Laden dieses 40-Kbyte-Bilds über das Web sind 30 bis 60 Sekunden erforderlich; dennoch ist das Steuerelement bereits während des Ladevorgangs einsatzbereit, nämlich als schlichter Würfel ohne Hintergrundbild. Das ist schon die ganze Zauberei beim Einsatz asynchroner Eigenschaften û wie man sieht, hat sich die Mühe des vorangegangenen Abschnitts gelohnt.
Abbildung 20.12: Im Würfelhintergrund läßt sich jetzt jedes beliebige Bild anzeigen.
ActiveX-Steuerelemente ändern sich schnell. Halten Sie Ausschau nach neuen Ankündigen von Microsoft von Entwicklungs-Kits und anderen Add-ons für eine schnellere und einfachere Erstellung Internet-fähiger Steuerelemente.
Weitere Informationen über die Programmierung für das Internet erhalten Sie in den folgenden Kapiteln: