17
ActiveX-Steuerelemente ersetzen die OLE-Steuerelemente, wenn die Änderung auch in erster Linie den Namen betrifft. (Ein großer Teil der Microsoft-Dokumentation verweist immer noch auf OLE-Steuerelemente.) Das aufregende Verhalten dieser Steuerelemente ist ActiveX zuzuschreiben. Ein ActiveX-Steuerelement ist etwas Ähnliches wie ein ActiveX-Automations-Server, doch stellt ein ActiveX-Steuerelement auch Ereignisse bereit, über die es seinen Containers zu Aktivitäten anregen kann. Dieses Kapitel greift teilweise auf die ActiveX-Arbeit früherer Kapitel zurück.
Die Beispielanwendung Wuerfel-Steuerelemente (Wuerfel)für dieses Kapitel ist ein Würfel. Stellen Sie sich das Bild eines Würfels mit dem gewohnten Punktemuster vor, der den aktuellen Wert (von 1 bis 6) anzeigt. Klickt nun der Anwender auf dieses Bild, wird eine neue, zufällig erzeugte Zahl angezeigt. Sie könnten einen oder mehrere solcher Würfel in ein beliebiges Spieleprogramm implementieren.
Der Prozeß für das Erstellen eines solchen Würfel-Steuerelements beginnt, wie immer, mit einem Assistenten, diesmal dem Steuerelement-Assistenten. Wuerfel-SteuerelementStarten Sie das Developer Studio, und wählen Sie Datei/Neu. Öffnen Sie das Register Projekt, und markieren Sie in der Liste links den Eintrag MFC ActiveX-Steuerelement-Assistent. Tragen Sie rechts oben den Projektnamen ein, wählen Sie darunter den gewünschten Ordner für dieses Projekt aus, und klicken Sie auf OK. In Abbildung 17.1 sehen Sie das ausgefüllte Dialogfeld mit dem Projektnamen Dieroll.
Abbildung 17.1: Der Steuerelement-Assistent macht das Erstellen eines ActiveX-Steuerelements einfach.
Der ActiveX-Steuerelement-Assistent besteht aus zwei Schritten. Füllen Sie den Dialog von Schritt 1 wie in Abbildung 17.2 gezeigt aus: Sie benötigen ein Steuerelement, keine Laufzeitlizenz, Kommentare in Quellcodedateien und keine Hilfedateien. Wenn das Dialogfeld ausgefüllt ist, klicken Sie auf Weiter.
Abbildung 17.2: Der erste Schritt des Steuerelement-Assistenten dient der Einstellung der Grundparameter für Ihr Steuerelement.
Laufzeitlizenz
Im zweiten und letzten Schritt des Steuerelement-Assistenten können Sie die Merkmale des neuen Steuerelements festlegen. Aktivieren Sie auf jeden Fall die Optionen Aktivieren, wenn sichtbar, Im Dialogfeld Objekt einfügen verfügbar und Verfügt über ein Dialogfeld Info (vgl. Abbildung 17.3), und klicken Sie dann auf Fertigstellen. Vom Steuerelement-Assistenten werden Ihre Einstellungen in einem letzten Dialogfeld noch einmal zusammengefaßt. Klikken Sie auf OK, und es werden 19 Dateien für Sie angelegt und zu einem Projekt zusammengebunden. Diese Dateien sind bereit für eine Kompilierung, würden jedoch momentan noch keine Aktion hervorbringen. Sie haben eine leere Rahmenanwendung, und es ist Ihre Aufgabe, sie zu füllen.
Abbildung 17.3: Der zweite Schritt des Steuerelement-Assistenten betrifft die Darstellung und das Verhalten Ihres Steuerelements.
19 Dateien klingt ja nach einer ganzen Menge û ist es aber nicht. So Steuerelemente (Quellcodeuebersicht)ActiveX (Quellcodeuebersicht)ActiveX-Steuerelemente (Uebersicht)ActiveX-Steuerelemente (Uebersicht)gibt es nur drei Klassen: CDierollApp, CDierollCtrl und CDierollPropPage. Diese machen bereits sechs Dateien aus, die restlichen 13 sind die Projektdatei, die Make-Datei, die Ressourcendatei, die Klassen-Assistent-Datenbank, die ODL-Datei usw.
CDierollApp: Diese Klasse ist von COleControlModule abgeleitet und stellt Überschreibungen von InitInstance und ExitInstance bereit, die einfach nur die Basisklassenversionen dieser Funktionen aufrufen. Hier finden Sie auch _tlid, die externe, global eindeutige ID für Ihr Steuerelement sowie einige Versionsnummern, die das Ausliefern von aktualisierten Fassungen Ihres Steuerelements einfacher machen. Die folgenden Zeilen in Dieroll.cpp richten diese Bezeichner ein:
const GUID CDECL BASED_CODE _tlid =
{ 0x914b21a5, 0x7946, 0x11d0, { 0x9b, 0x1, 0, 0x80, 0xc8, 0x1a, 0x39, 0x7c } };const WORD _wVerMajor = 1;
const WORD _wVerMinor = 0;
CDierollCtrl: Die Klasse CDierollCtrl ist von COleControl abgeleitet und überschreibt den Konstruktor und den Destruktor sowie die folgenden vier Funktionen:
Die Implementierung dieser Funktionen ist nicht besonders interessant. Doch einige der Tabellen, die zu dieser Klasse hinzugefügt wurden, verdienen Beachtung. So gibt es eine leere Nachrichtentabelle, die für die Aufnahme neuer Einträge bereit ist, sowie eine leere Verteilertabelle, die für die Aufnahme der bereitzustellenden Eigenschaften und Methoden vorgesehen ist.
Unter den leeren Nachrichten- und Verteilertabellen kommt eine neue Tabelle: die Ereignistabelle. Die Ereignistabelle der Header-Datei sehen Sie in Listing 17.1 und die Ereignistabelle der CPP-Datei in Listing 17.2.
Listing 17.1: Auszug aus DierollCtl.h û Ereignistabelle |
// Ereignistabellen
//{{AFX_EVENT(CDierollCtrl)
// HINWEIS û Der Klassen-Assistent fügt Member-Funktionen hier
// ein und entfernt diese.
// Innerhalb dieser generierten Quelltextabschnitte
// NICHTS VERÄNDERN!
//}}AFX_EVENT
DECLARE_EVENT_MAP()
BEGIN_EVENT_MAP(CDierollCtrl, COleControl)
//{{AFX_EVENT_MAP(CDierollCtrl)
// HINWEIS û Der Klassen-Assistent fügt Einträge in die
// Ereignistabelle ein und entfernt diese
// Innerhalb dieser generierten Quelltextabschnitte
// NICHTS VERÄNDERN!
//}}AFX_EVENT_MAP
END_EVENT_MAP()
Ereignistabellen verbinden, wie auch Nachrichten- und Verteilertabellen, externe Aktionen mit Ihrem Code. Nachrichtentabellen fangen Benutzeraktionen ab, etwa die Auswahl eines Menübefehls oder das Klicken auf eine Schaltfläche. Sie können auch Nachrichten abfangen, die von einem Teil der Anwendung an einen anderen gesendet werden. Verteilertabellen leiten Anfragen für den Zugang zu Eigenschaften weiter oder rufen Methoden eines Automations-Servers oder ActiveX-Steuerelements auf. Ereignistabellen leiten Benachrichtigungen von einem ActiveX-Steuerelement an die Anwendung mit dem Steuerelement weiter (weitere Erläuterungen zu Ereignistabellen erhalten Sie weiter unten in diesem Kapitel).
Noch ein Code-Segment in DierollCtl.cpp verdient Ihre Beachtung. Es wird in Listing 17.3 gezeigt.
Listing 17.3: Auszug aus DierollCtl.cpp û Eigenschaftsseiten |
/////////////////////////////////////////////////////////////////////////////
// Eigenschaftsseiten
// ZU ERLEDIGEN: Fügen Sie mehr Eigenschaftenseiten ein, als
// erforderlich sind. Denken Sie daran, den Zähler zu erhöhen!
BEGIN_PROPPAGEIDS(CDierollCtrl, 1)
PROPPAGEID(CDierollPropPage::guid)
END_PROPPAGEIDS(CDierollCtrl)
Der Code in Listing 17.3 ist Teil des Mechanismus, der in Ihren Steuerelementen leistungsstarke und intuitive Eigenschaftsseiten implementiert. Dieser Mechanismus wird weiter hinten in diesem Kapitel erörtert.
CDierollPropPage::CDierollPropPage() :
COlePropertyPage(IDD, IDS_DIEROLL_PPG_CAPTION)
{
//{{AFX_DATA_INIT(CDierollPropPage)
// HINWEIS: Der Klassen-Assistent fügt die Elementinitialisierung
// hier ein
// Innerhalb dieser generierten Quelltextabschnitte
// NICHTS VERÄNDERN!
//}}AFX_DATA_INIT
}
Die Funktion DoDataExchange regelt den Austausch von Daten zwischen einer Instanz der Klasse CDierollPropPage und dem tatsächlichen Dialogfeld auf dem Bildschirm. Auch diese Funktion wird vom Klassen-Assistenten erweitert (siehe Listing 17.5).
Listing 17.5: CDierollPropPage::DoDataExchange() |
void CDierollPropPage::DoDataExchange(CDataExchange* pDX)
{
//{{AFX_DATA_MAP(CDierollPropPage)
// HINWEIS: Der Klassen-Assistent fügt DDP-, DDX- und DDV-Aufrufe
// hier ein
// Innerhalb dieser generierten Quelltextabschnitte
// NICHTS VERÄNDERN!
//}}AFX_DATA_MAP
DDP_PostProcessing(pDX);
}
Keineswegs überraschend, gibt es auch eine Nachrichtentabelle für CDierollPropPage und etwas Registrierungscode (siehe Listing 17.6).
Listing 17.6: CDierollPropPage::UpdateRegistry() |
/////////////////////////////////////////////////////////////////////////////
// Klassenerzeugung und GUID initialisieren
IMPLEMENT_OLECREATE_EX(CDierollPropPage, »DIEROLL.DierollPropPage.1",
0x914b21a8, 0x7946, 0x11d0, 0x9b, 0x1, 0, 0x80, 0xc8, 0x1a, 0x39, 0x7c)
/////////////////////////////////////////////////////////////////////////////
// CDierollPropPage::CDierollPropPageFactory::UpdateRegistry -
// Fügt Einträge der Systemregistrierung für CDierollPropPage hinzu oder
// entfernt diese
BOOL CDierollPropPage::CDierollPropPageFactory::UpdateRegistry(BOOL bRegister)
{
if (bRegister)
return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),
m_clsid, IDS_DIEROLL_PPG);
else
return AfxOleUnregisterClass(m_clsid, NULL);
}
Typischerweise besitzt ein Steuerelement Wuerfel-SteuerelementWuerfel-Steuerelemententwerfeninterne Daten (Eigenschaften) und zeigt sie auf die eine oder andere Weise dem Anwender. Dieser sorgt für Eingaben, wodurch interne Datenwerte und manchmal auch die Darstellung des Steuerelements geändert werden. Manche Steuerelemente präsentieren dem Anwender Daten aus anderen Quellen wie Datenbanken. Für das Würfel-Steuerelement hat nur ein interner Datenwert Sinn: Ein ganze Zahl zwischen eins und sechs, eben das Würfelergebnis. Einige Darstellungseigenschaften werden weiter hinten erläutert. Irgendwann wird das Steuerelement ein Punktemuster wie das eines echten Würfels anzeigen, doch die erste Implementierung von OnDraw wird einfach die Ziffer anzeigen. Eine weitere Vereinfachung besteht darin, für die Dauer der Entwicklung der Grundstruktur des Programms die Ziffer im Code fest zu programmieren, also einem bestimmten Wert zuzuordnen. Der Code für das eigentliche Würfeln wird erst später, zusammen mit den Reaktionen auf die Benutzereingaben, ergänzt.
Bevor der Wert angezeigt werden kann, muß das Steuerelement wissen, welchen Wert es anzeigen soll. Werte anzeigenWerte des Wuerfel-Steuerelementsdes Wuerfel-Steuerelements anzeigenDazu muß eine Eigenschaft hinzugefügt und der Zeichencode für die Anzeige geschrieben werden.
ActiveX-Steuerelemente besitzen vier Arten von Eigenschaften: EigenschaftenWuerfel-Steuerelement
Um den Wert des Würfel-Steuerelements zu implementieren, verwenden Sie den Klassen-Assistenten Eigenschaften hinzufuegenund fügen eine benutzerdefinierte Eigenschaft mit dem Namen Number hinzu. Führen Sie dazu folgende Arbeitsschritte aus:
Abbildung 17.4: Der Klassen-Assistent unterstützt Sie beim Definieren neuer Eigenschaften für das Würfel-Steuerelement.
Sie können den Code für die Anzeige der Eigenschaft Number Eigenschaften (Number)Wuerfel-Steuerelement (Number)Werte in das Wuerfel-Steuerelement implementnur schreiben, wenn die Eigenschaft auch einen Wert hat, der angezeigt werden kann. Steuerelementeigenschaften werden in DoPropExchange initialisiert. Diese Methode implementiert Persistenz, d.h. das Steuerelement kann als Teil des Dokuments gespeichert und beim Öffnen des Dokuments wieder eingelesen werden. Wird nun ein neues Steuerelement angelegt, so können die Eigenschaften nicht aus einer Datei gelesen werden, daher werden sie auf die in dieser Methode bereitgestellten Standardwerte gesetzt. Steuerelemente besitzen keine Serialize-Methode.
Vom Steuerelement-Assistenten wurde ein Gerüst für die Methode DoPropExchange generiert, dessen Code Sie in Listing 17.7 finden.
Listing 17.7: CDierollCtrl::DoPropExchange() |
void CDierollCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// ZU ERLEDIGEN: PX_ Funktionen für jede dauerhafte
// benutzerdefinierte Eigenschaft aufrufen.
}
Beachten Sie die Verwendung der Versionsnummern. Denn nur so kann sichergestellt werden, daß eine Wertedatei von derselben Steuerelement-Version gespeichert wurde. Entfernen Sie den »Zu erledigen«-Kommentar, und fügen Sie an seiner Stelle die folgende Zeile ein:
PX_Short( pPX, "Number", m_number, (short)3 );
PX_Short (Wuerfel-Steuerelement)PX_Short (Funktion)Wuerfel-SteuerelementPX_Short ist eine von vielen Funktionen für den Eigenschaftenaustausch û es gibt eine solche Funktion für jeden der unterstützten Eigenschaftstypen. Die anzugebenden Parameter sind wie folgt:
Es folgt eine Auflistung der PX-Funktionen:
PX_Blob (für den Datentyp BLOB [binary large object])
PX_Bool
PX_Color (OLE_COLOR)
PX_Currency
PX_DATAPATH (CDataPathProperty)
PX_Double
PX_Float
PX_Font
PX_IUnknown (für LPUNKNOWN-Typen, COM-Schnittstellenzeiger)
PX_Long
PX_Picture
PX_Short
PX_String
PX_ULong
PX_UShort
Das Ausfüllen des Standardwerts von Eigenschaften (Wuerfel-Steuerelement)Wuerfel-Steuerelement (Standardwerte)Standard- (Wuerfel-Steuerelement)ist bei manchen Eigenschaften ganz einfach, bei anderen wiederum etwas schwieriger. Farben setzen Sie beispielsweise mit Hilfe des Makros RGB, das einen Rot-, einen Grün- und einen Blauwert zwischen 0 und 255 akzeptiert und ein COLORREF liefert. Angenommen, Sie haben eine Eigenschaft mit dem externen Namen EdgeColor und dem internen Namen m_edgecolor und die Eigenschaft soll den Standardwert Grau erhalten. Dies würden Sie folgendermaßen programmieren:
PX_Short( pPX, "EdgeColor", m_edgecolor, RGB(128,128,128) );
Steuerelemente mit Schrifteigenschaften sollten standardmäßig auf die Schrift des Containers gesetzt werden. Um diese zu ermitteln, rufen Sie die COleControl-Methode AmbientFont auf.
Der Code für die Anzeige der Zahl ZahlenanzeigeoptionenWuerfel-Steuerelementgehört in die OnDraw-Methode der Steuerelementklasse CDierollCtrl. (Steuerelemente haben keine Dokumente oder Ansichten.) Diese Funktion wird immer dann automatisch aufgerufen, wenn Windows den Bereich des Bildschirms neu aufbauen muß, auf dem sich das Steuerelement befindet. Vom Steuerelement-Assistenten wurde auch für diese Methode ein Gerüst erstellt (siehe Listing 17.8).
void CDierollCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,
const CRect& rcInvalid)
{
// ZU ERLEDIGEN: Folgenden Code durch eigene Zeichenfunktion
// ersetzen.
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}
Wie bereits in Kapitel 6, »Zeichnen auf dem Bildschirm«, erörtert, übergibt das Anwendungsgerüst der Funktion einen Gerätekontext, um darin zu zeichnen, ein CRect beschreibt den vom Steuerelement einzunehmenden Platz sowie ein weiteres CRect, das den Platz beschreibt, der ungültig ist. Der Code in Listing 17.8 zeichnet ein weißes Rechteck durch rcBounds und dann eine Ellipse innerhalb dieses Rechtecks, und zwar mit den Standardvordergrundfarben. Obwohl Sie das weiße Rechteck vorerst beibehalten können, sollten Sie darin keine Ellipse anzeigen, sondern vielmehr ein Zeichen, das dem Wert in Number entspricht. Dazu ersetzen Sie die letzte Zeile im OnDraw-Gerüst durch die folgenden Zeilen:
CString val; //Zeichenentsprechung des short-Werts
val.Format("%i",m_number);
pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );
Diese Zeilen konvertieren den short-Wert in der Variablen m_number (die Sie im Dialogfeld Eigenschaft hinzufügen der Eigenschaft Number zugeordnet haben) in eine CString-Variable mit der Bezeichnung val, und zwar mit Hilfe der neuen Funktion CString:: Format (die eine der letzten Einsatzmöglichkeiten von sprintf in der C++-Programmierung eliminiert). Die Funktion ExtTextOut (Wuerfel-Steuerelement)Wuerfel-SteuerelementExtTextOut (Funktion)ExtTextOut zeichnet Text û nämlich das Zeichen in val û innerhalb des rcBounds-Rechtecks. Beim momentanen Stand der Implementierung ist diese Zahl immer 3.
Wenn Sie wollen, können Sie das Steuerelement jetzt erstellen und testen, damit Sie sehen, mit wie wenig Aufwand man ein Steuerelement schaffen kann, das eine Aktion ausführt. Im Gegensatz zu den anderen ActiveX-Anwendungen muß ein Steuerelement nicht als eigenständige Anwendung ausgeführt werden, damit es registriert wird. Erstellen Sie das Projekt, korrigieren Sie etwaige Tippfehler, und wählen Sie Extras/Test-Container für ActiveX-Steuerelement, um den in Abbildung 17.5 gezeigten Text-Container zu öffnen.
Abbildung 17.5: Der ActiveX-Steuerelement-Test-Container eignet sich ideal für das Testen Ihres Steuerelements.
Wählen Sie nun im Test-Container den Menübefehl Bearbeiten/ActiveX Steuerelement einfügen und dann Dieroll Control aus der angezeigten Liste. Wie Sie in Abbildung 17.6 sehen, erscheint das Steuerelement als weißes Rechteck, in dem ganz klein die Zahl 3 angezeigt wird. Sie können das Steuerelement innerhalb des Containers verschieben oder seine Größe ändern, doch die Zahl drei bleibt immer treulich links oben in der Ecke. Die nächste Aufgabe besteht darin, dafür zu sorgen, daß sich die Zahl ändert, wenn ein Anwender auf den Würfel klickt.
Abbildung 17.6: Nur durch das Hinzufügen einer Eigenschaft und das Ändern von zwei Funktionen haben Sie die leere Shell in ein Steuerelement verwandelt, das eine 3 anzeigt.
Das Steuerelement soll im Prinzip zwei Aktionen ausführen, wenn der Anwender mit der Maus auf das Steuerelement klickt: MausklickreaktionenwuerfelnActiveX (wuerfeln)Steuerelemente (wuerfeln)Wuerfel-SteuerelementEs soll den Container darüber informieren, daß auf das Steuerelement geklickt wurde, und es soll den neuen internen Wert anzeigen.
Lassen Sie uns zunächst darüber reden, wie ein Ereignis für die Benachrichtigung des Containers eingesetzt wird. Als Parallele zu den Standardeigenschaften gibt es auch Standardereignisse. Diese sind bereits für Sie programmiert worden: vom Mausklick unterrichtenvom Mausklick unterrichten (Wuerfel-Steuerelement) vom Mausklick unterrichten (Wuerfel-Steuerelement)
Die beste Art, dem Container mitzuteilen, daß der Anwender auf ein Steuerelement geklickt hat, Ereignisse (Click)Click (Wuerfel-Steuerelement)Wuerfel-Steuerelementbesteht darin, das Standardereignis Click auszulösen. Dazu muß es in einem ersten Arbeitsgang mit Hilfe des Klassen-Assistenten in das Steuerelement eingefügt werden.
Abbildung 17.7: Der Klassen-Assistent unterstützt Sie beim Hinzufügen von Ereignissen für das Steuerelement.
Vielleicht haben Sie bereits bemerkt, daß das Register ClassView um zwei neue Symbole ergänzt wurde, die Griffen gleichen. Klicken Sie auf das + vor DDierollEvents, und Sie sehen, daß Click jetzt als Ereignis für diese Anwendung aufgelistet ist (vgl. Abbildung 17.8).
Abbildung 17.8: Im Register ClassView werden auch Ereignisse aufgelistet.
Wenn der Anwender jetzt auf das Steuerelement klickt, wird die Container-Klasse davon in Kenntnis gesetzt. Wenn Sie nun beispielsweise ein Backgammon-Spiel schreiben, kann der Container auf den Klick reagieren und aufgrund des neuen Werts auf dem Würfel die möglichen Züge berechnen oder eine sonstige, backgammon-spezifische Aufgabe ausführen.
Der zweite Arbeitsgang hinsichtlich der Reaktion auf Klicks betrifft das eigentliche Würfeln und das erneute Anzeigen des Würfels. Wieder hilft der Klassen-Assistent bei der Implementierung dieser Aufgabe. Wenn der Anwender auf das Steuerelement klickt, fangen Sie dieses Ereignis mit einem Nachrichtentabelleneintrag ab, gerade so wie bei jeder sonstigen Anwendung. Wenn der Klassen-Assistent nicht mehr angezeigt wird, öffnen Sie ihn erneut und führen Sie dann die folgenden Arbeitsschritte aus:
void CDierollCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Code für die Behandlungsroutine für Nachrichten hier
// einfügen und/oder Standard aufrufen
COleControl::OnLButtonDown(nFlags, point);}
m_number = Roll();
InvalidateControl();
wuerfelnWuerfel-SteuerelementUm Roll in CDierollCtrl einzufügen, klicken Sie im Register ClassView mit der rechten Maustaste auf CDierollCtrl und wählen Member-Funktion hinzufügen aus dem Kontextmenü. Wie Sie in Abbildung 17.9 sehen, Roll()Wuerfel-SteuerelementWuerfel-Steuerelementsollte Roll als public-Funktion definiert werden, die keine Parameter akzeptiert und einen Wert vom Datentyp short liefert.
Abbildung 17.9: Mit Member-Funktion hinzufügen können Sie Routine-Aufgaben schneller erledigen.
Welche Aktion sollte Roll ausführen? Sie soll einen Zufallswert zwischen 1 und 6 berechnen. Die C-Bibliotheksfunktion, die einen Zufallswert liefert ist die Funktion rand, die eine ganze Zahl zwischen 0 und RAND_MAX liefert. Durch Division durch RAND_MAX + 1 entsteht eine positive Zahl, die immer kleiner als 1 ist, und durch Multiplikation mit 6 ein Wert, der immer kleiner als 6 ist. Der ganzzahlige Teil der Zahl wird also zwischen 0 und 5 liegen. Durch Addition von 1 erhalten Sie das gewünschte Ergebnis, nämlich eine Zahl zwischen 1 und 6. Den Code finden Sie in Listing 17.9.
short CDierollCtrl::Roll(void)
{
double number = rand();
number /= RAND_MAX + 1;
number *= 6;
return (short)number + 1;
}
Der ZufallszahlengeneratorWuerfel-SteuerelementZufallszahlengenerator muß auf einen »Quasi-zufälligen« Startwert gesetzt werden, bevor er eingesetzt wird. Traditioneller-(und auch praktischer-)weise wird die aktuelle Uhrzeit als Start-Wert verwendet. Fügen Sie in DoPropExchange vor PX_Short die folgende Zeile ein:
srand( (unsigned)time( NULL ) );
Der Ausgangswert wird nun nicht mehr fest auf den Wert 3 programmiert, sondern Roll wird aufgerufen, um einen Zufallswert zu bestimmen. Ändern Sie den Aufruf von PX_Short wie folgt um:
PX_Short( pPX, "Number", m_number, Roll());
Erstellen und testen Sie das Steuerelement wieder. Wenn Sie auf das Steuerelement klicken, sollte sich die angezeigte Zahl nun bei jedem Klick ändern. Machen Sie mehrere Versuche: Sehen Sie je eine Zahl, die kleiner als 1 oder größer als 6 ist? Sonst irgendwelche Überraschungen?
Nachdem Sie nun die grundlegende Funktionalität Punktemuster des Wuerfels erstellenfuer das Wuerfel-Steuerelementfuer das Wuerfel-Steuerelement erstellendes Würfel-Steuerelements programmiert haben, sollte das Element optisch etwas aufgewertet werden. Es braucht ein Symbol, und anstelle einer Ziffer sollten Punkte angezeigt werden.
Vielleicht möchten einige Anwender des Würfel-Steuerelements Wuerfel-Steuerelementdieses in die Steuerelementpalette in Visual Basic oder Visual C++ aufnehmen. Daher sollte es durch ein Symbol repräsentiert werden. Vom Steuerelement-Assistenten wurde zwar schon ein Symbol erstellt, doch handelt es sich hier einfach um ein MFC-Logo, das nicht speziell dieses Steuerelement repräsentiert. Im Developer Studio haben Sie jedoch die Möglichkeit, ein spezifisches Symbol für Ihr Steuerelement zu erstellen. Öffnen Sie im Arbeitsbereich das Register ResourceView, klicken Sie auf das + vor Bitmap und dann zweimal auf IDB_DIEROLL. Sie können die Bitmap-Grafik jetzt Pixel für Pixel ändern. In Abbildung 17.10 sehen Sie ein gutes Beispiel für ein Würfel-Symbol. Von jetzt an wird beim Laden des Würfel-Steuerelements in den Text-Container anstelle des OCX-Symbols dieses Symbol in der Symbolleiste angezeigt.
Abbildung 17.10: Im Register ResourceView des Developer Studio können Sie ein eigenes Symbol für ihr Steuerelement entwerfen, das in die Steuerelementpalette von Visual Basic aufgenommen wird.
Der nächste Arbeitsgang Punktemuster des Wuerfels erstellenPunktemuster fuer das Wuerfel-Steuerelement erstellenbesteht darin, dem Würfel-Steuerelement auch das Aussehen eines Würfels zu geben. Ein schöner dreidimensionaler Effekt, bei dem auch einige der anderen Seitenflächen zu sehen sind, würde den illustrativen Charakter dieses Kapitels sprengen, doch zumindest das Punktemuster soll angezeigt werden.
Zunächst muß in OnDraw eine switch-Anweisung eingerichtet werden. Kommentieren Sie die drei Zeichenzeilen aus, und fügen Sie die switch-Anweisungen fuer die FunktionOnDrawFunktion OnDraw (Wuerfel-Steuerelement)switch-Anweisung so ein, daß OnDraw den in Listing 17.10 gezeigten Inhalt hat.
void CDierollCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
// CString val; //Zeichenentsprechung des short-Werts
// val.Format("%i",m_number);
// pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );
switch(m_number)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
}
}
Bleibt nur noch, in den case-Block für 1 Code einzufügen, der einen Punkt zeichnet, in den für 2 Code, der zwei Punkte zeichnet, usw. Wenn Sie gerade einen richtigen Würfel zur Hand haben, sollten Sie diesen einmal genau betrachten. Punktemuster fuer das Wuerfel-Steuerelement erstellenDer Abstand zwischen jedem Punkt entspricht in etwa einem Viertel der Würfelseite. Punkte in der Nähe der Kante sind in etwa ein Sechzehntel einer Würfelseite, vom Rand abgesetzt. Von der Sechs einmal abgesehen, sind alle weiteren Muster im Layout der 5 enthalten. Der Einser-Punkt befindet sich beispielsweise auf derselben Stelle wie der mittlere Punkt der Fünf.
Der zweite Parameter von OnDraw, rcBounds, ist eine CRect-Instanz, die das Rechteck beschreibt, das von dem Steuerelement eingenommen wird. Sie besitzt Mitgliedsvariablen, und -funktionen die die Koordinaten der Ecke links oben sowie die Breite und Höhe des Steuerelements wiedergeben. Im vom Anwendungs-Assistenten generierten Standardcode wurde CDC::Ellipse aufgerufen, damit in dem Rechteck eine Ellipse gezeichnet wird. Ihr Code wird ebenfalls Ellipse aufrufen, wobei ein kleineres Recheck innerhalb des größeren Rechtecks des Steuerelements als Abmessung übergeben wird. Ihr Code wird leichter verständlich sein û und etwas schneller ausgeführt werden û, wenn Sie mit Einheiten arbeiten, die ein Sechzehntel der Gesamtbreite bzw. -höhe ausmachen. Jeder Punkt wird vier Einheiten breit und hoch sein. Fügen Sie vor der switch-Anweisung den folgenden Code ein:
int Xunit = rcBounds.Width()/16;
int Yunit = rcBounds.Height()/16;
int Top = rcBounds.top;
int Left = rcBounds.left;
Bevor Sie durch Aufruf von Ellipse eine Form zeichnen, müssen Sie ein Tool für das Zeichnen auswählen. Punktemuster des Wuerfel-Steuerelements (Tool)Da die Kreise ausgefüllt werden sollten, sollten sie mit einer Farbrolle (Brush) gezeichnet werden. Der folgende Code definiert eine Farbrolle und weist den Gerätekontext pdc an, ihn zu verwenden. Gleichzeitig wird ein Zeiger auf die vorige Farbrolle gespeichert, so daß dieser später wieder hergestellt werden kann. zeichnen (Wuerfel-Steuerelement)
CBrush Black;
Black.CreateSolidBrush(RGB(0x00,0x00,0x00)); //ausgefüllte, schwarze Farbrolle
CBrush* savebrush = pdc->SelectObject(&Black);
Fügen Sie nach der switch-Anweisung die folgende Zeile ein, um die vorige Farbrolle wieder zu aktivieren:
pdc->SelectObject(savebrush);
Jetzt können Sie den Code für das Zeichnen einiger Punkte in die case-Blocks einfügen. Beim Wurf einer 2, 3, 4, 5 oder 6 wird beispielsweise in der Ecke links oben jeweils ein Punkt benötigt. Dieser Punkt wird in einem rechteckigen Feld erscheinen, das, von der linken oberen Ecke des Rechteck aus gemessen, eine Einheit nach rechts und eine Einheit nach unten beginnt. Der Aufruf von Ellipse hat den folgenden Inhalt:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit);
Die Koordinaten für die anderen Punkte werden in ähnlicher Weise bestimmt. Die switch-Anweisung hat schließlich den Inhalt von Listing 17.11.
switch(m_number)
{
case 1:
pdc->Ellipse(Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //Mitte
break;
case 2:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //Links oben
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //Rechts unten
break;
case 3:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //Links oben
pdc->Ellipse(Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //Mitte
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //Rechts unten
break;
case 4:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //Links oben
pdc->Ellipse(Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //Rechts oben
pdc->Ellipse(Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //Links unten
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //Rechts unten
break;
case 5:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //Links oben
pdc->Ellipse(Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //Rechts oben
pdc->Ellipse(Left+6*Xunit, Top+6*Yunit,
Left+10*Xunit, Top + 10*Yunit); //Mitte
pdc->Ellipse(Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //Links unten
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //Rechts unten
break;
case 6:
pdc->Ellipse(Left+Xunit, Top+Yunit,
Left+5*Xunit, Top + 5*Yunit); //Links oben
pdc->Ellipse(Left+11*Xunit, Top+Yunit,
Left+15*Xunit, Top + 5*Yunit); //Rechts oben
pdc->Ellipse(Left+Xunit, Top+6*Yunit,
Left+5*Xunit, Top + 10*Yunit); //Mitte links
pdc->Ellipse(Left+11*Xunit, Top+6*Yunit,
Left+15*Xunit, Top + 10*Yunit); //Mitte rechts
pdc->Ellipse(Left+Xunit, Top+11*Yunit,
Left+5*Xunit, Top + 15*Yunit); //Links unten
pdc->Ellipse(Left+11*Xunit, Top+11*Yunit,
Left+15*Xunit, Top + 15*Yunit); //Rechts unten
break;
}
Erstellen Sie das Steuerelement (OCX-Datei) noch einmal, und testen Sie es im Test-Container. Das Resultat sollte in etwa der Abbildung 17.11 entsprechen, in der ja tatsächlich ein Würfel zu sehen ist!
Abbildung 17.11: Das Würfel-Steuerelement hat jetzt das Aussehen eines Würfels.
Wenn Sie scharfe Augen haben oder den Würfel stark verkleinern, werden Sie wohl erkennen, daß sich das Punktemuster nicht exakt in der Mitte befindet. Das liegt daran, daß die Höhe und Breite des Steuerelements nicht immer ein ganzes Vielfaches von 16 beträgt. Angenommen, Width würde den Wert 31 liefern, so würde Xunit 1 betragen, und alle Punkte würden innerhalb der Positionen 0 und 16 angeordnet werden, wodurch rechts vom Steuerelement ein leerer Streifen entsteht. Glücklicherweise ist die Breite größer als 31 Pixel, was typisch ist, so daß die unsymmetrische Anordnung kaum bemerkbar ist.
Um dies zu beheben, zentrieren Sie die Punkte innerhalb des Steuerelements. Suchen Sie die Zeilen, in denen Xunit und Yunit berechnet werden, und fügen Sie dann die neuen Zeilen aus dem Code-Fragment in Listing 17.12 ein.
//Punkte sind 4 Einheiten breit und hoch und um eine Einheit vom Rand abgesetzt
int Xunit = rcBounds.Width()/16;
int Yunit = rcBounds.Height()/16;
int Xleft = rcBounds.Width()%16;
int Yleft = rcBounds.Height()%16;
// Die Hälfte des verbleibenden Abstands oben und links als Abstand einfügen
int Top = rcBounds.top + Yleft/2;
int Left = rcBounds.left + Xleft/2;
Xleft und Yleft sind die Restabstände in x- und y-Richtung. Indem Sie Top und Left um die Hälfte des Rests verschieben, wird das Punktemuster innerhalb des Steuerelements zentriert, ohne weiteren Code ändern zu müssen.
ActiveX-Steuerelemente besitzen EigenschaftsdialogeWuerfel-SteuerelementEigenschaftsdialoge, die dem Anwender das Einstellen von Eigenschaften erlauben, ohne Änderungen in der Container-Anwendung vornehmen zu müssen. (Eigenschaftsdialoge und -seiten werden in Kapitel 12, »Assistenten, Eigenschaftsseiten und -dialoge », erläutert.) Diese werden als Dialogfelder erstellt, wobei bereits geschriebene Seiten für Schrift, Farbe und andere häufig verwendete Eigenschaften genutzt werden. Für dieses Steuerelement sind die offensichtlich benötigten Eigenschaften die folgenden:
Es ist ganz einfach, dem Anwender die Auswahl zwischen einer Punkte- und Ziffernanzeige zu geben. Punke- oder ZiffernanzeigeWuerfel-SteuerelementZiffern- oder PunkteanzeigeAnzeigeoptionen fuer das Wuerfel-SteuerelementAnzeigeoptionen fuer das Wuerfel-SteuerelementSie fügen einfach eine Eigenschaft hinzu, die diese Auswahl kennzeichnet, und verwenden die Eigenschaft dann in OnDraw. Der Anwender kann die Eigenschaft auf der Eigenschaftsseite einstellen.
Definieren Sie die Eigenschaft zunächst mit Hilfe des Klassen-Assistenten: Öffnen Sie den Klassen-Assistenten und dann das Register Automatisierung. Stellen Sie sicher, daß die Klasse CDierollCtrl ausgewählt ist, und klicken Sie dann auf Eigenschaft hinzufügen. Geben Sie den externen Namen Dots ein, und übernehmen Sie den internalen Namen m_dots. Wählen Sie den Typ BOOL aus, da Dots entweder den Wert TRUE oder FALSE annehmen soll. Implementieren Sie diese neue Eigenschaft als Member-Variable (Direktzugriff). Klicken Sie auf OK, um das Dialogfeld Eigenschaft hinzufügen zu schließen, und dann noch einmal auf OK, um den Klassen-Assistenten zu schließen. Die Mitgliedsvariable wird in die Klasse aufgenommen, die Verteilertabelle aktualisiert und für die Benachrichtigungsfunktion OnDotsChanged wird ein Codegerüst hinzugefügt. Funktion OnDotsChangedOnDotsChanged (Wuerfel-Steuerelement)Wuerfel-Steuerelement
Um Dots zu initialisieren und dafür zu sorgen, daß es im Dokument gespeichert wird, fügen Sie in DoPropExchange nach dem Aufruf von PX_Short die folgende Zeile ein.
PX_Bool( pPX, "Dots", m_dots, TRUE);
Durch die Initialisierung der Eigenschaft Eigenschaft DotsWuerfel-Steuerelement (Dots)Wuerfel-SteuerelementDots mit TRUE wird sichergestellt, daß als Standardverhalten des Steuerelements das Punktemuster angezeigt wird.
Entfernen Sie in OnDraw die Kommentarzeichen wieder in den Zeilen, die für die Anzeige der Ziffern verantwortlich waren. Umschließen Sie diese Zeilen mit einer if-Bedingung, so daß die Ziffer angezeigt wird, wenn m_dots den Wert FALSE annimmt und zugewiesen wird, und das Punktemuster, wenn es den Wert TRUE annimmt. Der Code entspricht dem Inhalt von Listing 17.13.
void CDierollCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
if (!m_dots)
{
CString val; //Zeichenentsprechung des short-Werts
val.Format("%i",m_number);
pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );
}
else
{
// Punkte sind 4 Einheiten breit und hoch und um eine Einheit vom Rand abgesetzt
int Xunit = rcBounds.Width()/16;
int Yunit = rcBounds.Height()/16;
int Xleft = rcBounds.Width()%16;
int Yleft = rcBounds.Height()%16;
// Die Hälfte des verbleibenden Abstands oben und links als Abstand einfügen
int Top = rcBounds.top + Yleft/2;
int Left = rcBounds.left + Xleft/2;
CBrush Black;
Black.CreateSolidBrush(RGB(0x00,0x00,0x00)); // ausgefüllte, schwarze Farbrolle
CBrush* savebrush = pdc->SelectObject(&Black);
switch(m_number)
{
case 1:
...
}
pdc->SelectObject(savebrush);
}
}
Damit der Punkt die Anzeige im Punktemuster aktivieren und deaktivieren kann, erstellen Sie mit Hilfe der folgenden Arbeitsschritte eine Eigenschaftsseite:
Abbildung 17.12: Vom Steuerelement-Assistenten wurde eine leere Eigenschaftsseite angelegt.
Abbildung 17.13: Sie erstellen die Eigenschaftsseite für das Würfel-Steuerelement wie jedes sonstige Dialogfeld.
Wenn der Anwender die Eigenschaftsseite öffnet und auf das Kontrollkästchen klickt, um die Option Display Dot Pattern zu aktivieren oder zu deaktivieren, wird davon der Wert von m_dots bzw. die Eigenschaft Eigenschaft Dots (Benutzeroption)Optionen des Wuerfel-SteuerelementsDots nicht direkt betroffen. Um das Dialogfeld mit Mitgliedsvariablen zu verbinden, führen Sie im Klassen-Assistenten die folgenden Arbeitsschritte aus:
Der Pfad, dem die Daten folgen, kann etwas verschlungen sein. Wenn der Anwender den Eigenschaftsdialog öffnet, befindet sich der Wert TRUE oder FALSE in einer temporären Variablen. Per Klick auf das Kontrollkästchen wird der Wert dieser temporären Variablen umgeschaltet. Wenn der Anwender auf OK klickt, wird dieser Wert in CDierollProp-Page::m_dots sowie in die Automatisierungseigenschaft Dots eingelesen. Diese Eigenschaft wurde bereits mit CDierollCtrl:: m_dots verbunden, so daß die Verteilertabelle in CDierollCtrl sicherstellt, daß die andere m_dots geändert wird. Die Funktion OnDraw verwendet CDierollCtrl:: m_dots, daher ändert sich die Darstellung des Steuerelements als Reaktion auf die Änderung, die vom Anwender in der Eigenschaftsseite vorgenommen wurde.
Abbildung 17.14: Im Klassen-Assistenten verbinden Sie die Eigenschaftsseite mit den Eigenschaften des Steuerelements.
Abbildung 17.15: Vom Steuerelement-Test-Container wird Ihr eigenes Steuerelement angzeigt.
Jetzt funktioniert's. Erstellen Sie das Steuerelement, und fügen Sie es in den Test-Container ein. Um die Eigenschaften zu ändern, wählen Sie Bearbeiten/Dieroll Control-Objekt/Eigenschaften. Die soeben von Ihnen erstellte Eigenschaftsseite (siehe Abbildung 17.15) sollte jetzt erscheinen. Beweisen Sie es sich selbst, daß das Steuerelement, abhängig von der Einstellung auf dieser Seite, Punkte oder eine Ziffer anzeigt. Ändern Sie die Einstellung, klikken Sie auf OK, und beobachten Sie, was passiert.
Wenn das Steuerelement den Wert als Zahl anzeigt, möchten Sie diese vielleicht in einer Schriftart darstellen, die etwas angemessener für die aktuelle Größe des Steuerelements ist, und die Zahl in die Mitte des Würfels versetzen. Dies läßt sich durch eine relativ simple Änderung in OnDraw erreichen, die Sie selbst ausprobieren können.
Der bis zu diesem Punkt erstellte Würfel hat immer schwarze Punkte auf weißem Hintergrund. Doch ist es erstaunlich einfach, dem Benutzer eine Möglichkeit einzuräumen, dies zu ändern. Sie werden eine Eigenschaft für die Vordergrundfarbe und eine weitere für die Hintergrundfarbe benötigen. Beide wurden bereits als Standardeigenschaften implementiert: BackColor und ForeColor.
Standardeigenschaften (Stock): Es folgt eine vollständige Liste der für ein Steuerelement verfügbaren Standardeigenschaften: ActiveX-Steuerelemente (Optionen fuer die Anzeige
Umgebungseigenschaften (Ambient): Steuerelemente haben auch Zugang zu Umgebungseigenschaften; das sind bestimmte Eigenschaften des Containers, in den das Steuerelement eingebunden wurde. Umgebungseigenschaften lassen sich nicht ändern, doch kann das Steuerelement sie dazu verwenden, seine eigenen Eigenschaften anzupassen. So kann es beispielsweise seine Hintergrundfarbe der des Containers anpassen.
Der Container bietet die gesamte Unterstützung für Umgebungseigenschaften. Wenn im Code eine Umgebungseigenschaft vorkommt, sollte immer auch ein Wert vorgegeben werden, um Fälle abzudecken, in denen der Container die Eigenschaft nicht unterstützt. Hier eine Umgebungseigenschaft mit dem Namen Eigenschaft UserModeWuerfel-Steuerelement (UserMode)Wuerfel-SteuerelementUserMode:
BOOL bUserMode;
if( !GetAmbientProperty( DISPID_AMBIENT_USERMODE,
VT_BOOL, &bUserMode ) )
{
bUserMode = TRUE;
}
Dieser Code ruft GetAmbientProperty (Funktion)GetAmbientProperty (Wuerfel-Steuerelement)Wuerfel-SteuerelementGetAmbientProperty mit der erforderlichen ID (dispid) und dem erforderlichen Variablentyp auf. Zudem wird ein Zeiger auf eine Variable bereitgestellt, in die der Wert eingefügt wird. Der Datentyp dieser Variable muß dem Parameter vartype entsprechen. Liefert GetAmbientProperty den Wert FALSE, so nimmt bUserMode den vorgegebenen Wert an.
Die folgenden dispids werden in olectl.h angezeigt:
DISPID_AMBIENT_BACKCOLOR
DISPID_AMBIENT_DISPLAYNAME
DISPID_AMBIENT_FONT
DISPID_AMBIENT_FORECOLOR
DISPID_AMBIENT_LOCALEID
DISPID_AMBIENT_MESSAGEREFLECT
DISPID_AMBIENT_SCALEUNITS
DISPID_AMBIENT_TEXTALIGN
DISPID_AMBIENT_USERMODE
DISPID_AMBIENT_UIDEAD
DISPID_AMBIENT_SHOWGRABHANDLES
DISPID_AMBIENT_SHOWHATCHING
DISPID_AMBIENT_DISPLAYASDEFAULT
DISPID_AMBIENT_SUPPORTSMNEMONICS
DISPID_AMBIENT_AUTOCLIP
DISPID_AMBIENT_APPEARANCE
Denken Sie daran, daß nicht alle Container auch alle diese Eigenschaften unterstützen. Einige unterstützen bestimmte Eigenschaften nicht, andere wiederum unterstützen Eigenschaften, die in dieser Liste gar nicht enthalten sind.
Die unterstützten Variablentypen sind in Tabelle Table 17.1 aufgeführt.
Tabelle 17.1: Variablentypen für Umgebungseigenschaften (Forts.)
Vartype |
Beschreibung |
VT_BOOL |
BOOL |
VT_BSTR |
CString |
VT_I2 |
short |
VT_I4 |
long |
VT_R4 |
float |
VT_R8 |
double |
VT_CY |
CY |
VT_COLOR |
OLE_COLOR |
VT_DISPATCH |
LPDISPATCH |
VT_FONT |
LPFONTDISP |
Es ist etwas lästig, wenn man sich immer merken muß, welcher Variablentyp zu welcher dispid gehört, und den Rückgabewert von GetAmbientProperty überprüfen muß. Daher stellen die MFC Mitgliedsfunktionen von COleControl bereit, mit denen Sie die gebräuchlichsten Umgebungseigenschaften abrufen können:
Alle diese Funktionen weisen vernünftige Vorgabewerte zu, wenn der Container die angeforderte Eigenschaft nicht unterstützt.
BackColor und ForeColor implementieren: Um die Eigenschaften BackColor und ForeColor für das Steuerelement zu implementieren, führen Sie folgende Arbeitsschritte aus:
Abbildung 17.16: Standardeigenschaften sind vom Klassen-Assistenten vordefiniert.
Abbildung 17.17: Vordefinierte Standardeigenschaften sind in der Automatisierungsliste der Methoden und Eigenschaften durch ein S vor dem Namen gekennzeichnet.
Das Einrichten der Eigenschaftsseiten für diese Steuerelemente ist fast genauso einfach, da es eine bereits geschriebene Seite gibt, die Sie verwenden können. Suchen Sie in der Datei DierollCtl.cpp nach einem Code-Block wie in Listing 17.14.
//////////////////////////////////////////////////////////////
// Eigenschaftenseiten
// ZU ERLEDIGEN: Fügen Sie mehr Eigenschaftenseiten ein, als
// erforderlich sind. Denken Sie daran, den Zähler zu erhöhen!
PROPPAGEID(CDierollPropPage::guid)
END_PROPPAGEIDS(CDierollCtrl)
Entfernen Sie den »Zu erledigen«-Kommentar, ändern Sie den Zähler auf 2, und fügen Sie eine andere PROPPAGEID ein, so daß der Code-Block dem Inhalt von Listing 17.15 entspricht.
/////////////////////////////////////////////////////////////////////////////
// Eigenschaftsseiten
BEGIN_PROPPAGEIDS(CDierollCtrl, 2)
PROPPAGEID(CDierollPropPage::guid)
PROPPAGEID(CLSID_CColorPropPage)
END_PROPPAGEIDS(CDierollCtrl)
CLSID_CColorPropPage ist eine Klassen-ID einer Eigenschaftsseite, die für die Einstellung von Farben verwendet wird. Wenn der Anwender nun den Eigenschaftsdialog öffnet, wird er zwei Eigenschaftsseiten, d.h. zwei Register, sehen: eines zur Einstellung von Farben sowie das Register Allgemein, das sie ja bereits erstellt haben. Sowohl die Eigenschaft ForeColor als auch die Eigenschaft BackColor sind auf dieser Seite bereits verfügbar û die einzige Aufgabe, die also noch zu erledigen ist, besteht darin, die vom Anwender eingegebenen Werte zu verwenden. Bald werden Sie die Chance dazu haben, doch zunächst muß Ihr Code diese Farben verwenden.
Änderungen in OnDraw: In OnDraw kann der Code auf die Hintergrundfarbe mit GetBackColor zugreifen. Obwohl Sie es nicht sehen können, wurde diese Funktion beim Hinzufügen der Standardeigenschaft ebenfalls hinzugefügt. Die Verteilertabelle für CDierollCtrl hat jetzt den in Listing 17.16 angeführten Inhalt.
BEGIN_DISPATCH_MAP(CDierollCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CDierollCtrl)
DISP_PROPERTY_NOTIFY(CDierollCtrl, "Number", m_number, OnNumberChanged, VT_I2)
DISP_PROPERTY_NOTIFY(CDierollCtrl, "Dots", m_dots, OnDotsChanged, VT_BOOL)
DISP_STOCKPROP_BACKCOLOR()
DISP_STOCKPROP_FORECOLOR()
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CDierollCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
Das Makro DISP_STOCKPROP_BACKCOLOR umfaßt in erweitertem Zustand die folgenden Zeilen:
#define DISP_STOCKPROP_BACKCOLOR() \
DISP_PROPERTY_STOCK(COleControl, "BackColor", \
DISPID_BACKCOLOR, COleControl::GetBackColor, \
COleControl::SetBackColor, VT_COLOR)
Dieser Code ruft ein weiteres Makro, nämlich DISP_PROPERTY_STOCK, auf, das schließlich die GetBackColor (Funktion)GetBackColor()Wuerfel-SteuerelementWuerfel-SteuerelementFunktion GetBackColor als Element der Klasse CDierollCtrl deklariert, die wiederum von COleControl abgeleitet ist. Obwohl Sie sie nicht sehen können, ist diese Funktion also für Sie verfügbar. Sie liefert ein OLE_COLOR, das Sie mit TranslateColor in ein COLORREF umwandeln. Dieses COLORREF können Sie an CreateSolidBrush übergeben, um dann mit dieser Farbrolle den Hintergrund einzufärben. Greifen Sie mit GetForeColor auf die Vordergrundfarbe zu, und behandeln Sie diese auf dieselbe Weise. (Verwenden Sie SetTextColor im Ziffernteil des Codes.) In Listing 17.17 sehen Sie die vollständige OnDraw-Funktion.
void CDierollCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,
const CRect& rcInvalid)
{
COLORREF back = TranslateColor(GetBackColor());
CBrush backbrush;
backbrush.CreateSolidBrush(back);
pdc->FillRect(rcBounds, &backbrush);
if (!m_dots)
{
CString val; //Zeichenentsprechung des short-Werts
val.Format("%i",m_number);
pdc->SetTextColor(TranslateColor(GetForeColor()));
pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );
}
else
{
// Punkte sind 4 Einheiten breit und hoch und um eine Einheit
// vom Rand abgesetzt
int Xunit = rcBounds.Width()/16;
int Yunit = rcBounds.Height()/16;
int Top = rcBounds.top;
int Left = rcBounds.left;
COLORREF fore = TranslateColor(GetForeColor());
CBrush forebrush;
forebrush.CreateSolidBrush(fore);
CBrush* savebrush = pdc->SelectObject(&forebrush);
switch(m_number)
...
}
}
Erstellen Sie das Steuerelement noch einmal, fügen Sie es in den Test-Container ein, und öffnen Sie den Eigenschaftsdialog, indem Sie Bearbeiten/Dieroll Control-Objekt/Eigenschaften wählen. Wie Sie in Abbildung 17.18 sehen, eignet sich die neue Eigenschaftsseite ganz gut zum Einstellen von Farben. Ändern Sie die Vorder- und Hintergrundfarbe ein paar Mal, und testen Sie die Farbauswahl sowohl in der Punkte- als auch in der Ziffernanzeige, damit der gesamte Code getestet wird.
Abbildung 17.18: Standardeigenschaften machen es einfach, eine Benkutzerschnittstelle zur Farbauswahl zu erstellen.
ActiveX-Steuerelemente stellen, ebenso wie Automations-Server, Methoden (Funktionen) bereit. Dieses Steuerelement würfelt, wenn der Anwender darauf klickt, doch vielleicht soll ein Wurf in bestimmten Fällen auch von der Container-Anwendung veranlaßt werden û ohne Intervention durch den Anwender. Dazu fügen Sie eine Funktion mit dem Namen DoRoll (Funktion)DoRoll()Wuerfel-SteuerelementWuerfel-SteuerelementDoRoll hinzu.
Öffnen Sie den Klassen-Assistent und das Register Automatisierung, und klicken Sie dann auf Methode hinzufügen. Tragen Sie für die neue Funktion den Namen DoRoll ein, wählen Sie als Datentyp für den Rückgabewert den Typ void aus, und klicken Sie auf OK. Klicken Sie dann auf Code bearbeiten, und fügen Sie die folgenden Zeilen ein:
void CDierollCtrl::DoRoll()
{
m_number = Roll();
InvalidateControl();
}
Dieser einfache Code würfelt einmal und fordert einen Neuaufbau des Steuerelements an.
Das Würfel-Steuerelement mag vollständig erscheinen, doch könnte es noch besser sein.
In vielen Würfelspielen Wuerfel-SteuerelementWuerfeln deaktivierenkönnen Sie nur dann würfeln, wenn Sie an der Reihe sind. Momentan würfelt das Steuerelement immer, wenn es angeklickt wird. Durch die Definition einer selbstdefinierten Eigenschaft mit dem Namen Eigenschaften (RollAllowed)Wuerfel-Steuerelement (RollAllowed)Wuerfel-SteuerelementRollAllowed können Sie dem Container eine Steuerungsmöglichkeit für das Würfeln geben. Hat RollAllowed den Wert FALSE, so sollte nur CDieCtrl::OnLButtonDown übergeben werden û ohne ein Würfeln oder einen Neuaufbau der Anzeige. Vielleicht sollte OnDraw einen etwas unterschiedlichen Würfel zeichnen (z.B. graue Punkte?), wenn RollAllowed den Wert FALSE besitzt. Sie entscheiden, denn es ist ja Ihr Steuerelement.
Warum sollten Sie sich auf einen Würfel mit sechs Flächen beschränken. Es gibt Würfel mit vier, acht, zwölf, 20, ja selbst 30 Flächen ungewoehnliche FlaechenanzahlWuerfel-Steuerelementû würden solche Würfel dem Spiel nicht eine interessante Zusatznote geben? Sie sollten sich ein Paar dieser ungewöhnlichen Würfel anschaffen, damit Sie das Layout im Steuerelement nachahmen können, und den Zeichencode in CDierollCtrl::OnDraw ändern. Anschließend müssen Sie die fest programmierte 6 in Roll in eine selbstdefinierte Eigenschaft ändern, nämlich in einen Integer-Wert mit dem externen Namen Sides und einer Mitgliedsvariablen mit dem Namen m_sides. Vergessen Sie nicht, die Eigenschaftsseite so abzuändern, daß der Anwender dort die Anzahl der Flächen einstellen (die Eigenschaft Sides ändern) kann, und fügen Sie eine Zeile in CDieCtrl::DoPropExchange ein, um die Flächen persistent zu machen, und initialisieren Sie es auf 6.
Wenn Sie einWuerfel-Steuerelementarrays of diceWuerfel-Steuerelement Backgammon-Spiel schreiben würden, würden Sie zwei Würfel benötigen. Eine Möglichkeit wäre, zwei Steuerelemente einzubetten. Doch wie stellen Sie es an, daß diese mit einem Klick gleichzeitig gewürfelt werden? Warum erweitern Sie das Steuerelement nicht einfach, so daß es ein Array an Würfeln darstellt? Die Anzahl der Würfel wäre eine weitere selbstdefinierte Eigenschaft, und das Steuerelement würde alle Würfel gleichzeitig werfen. Das Flag RollAllowed würde sich auf alle Würfel beziehen, ebenso die Eigenschaft Sides, so daß Sie zwei sechsflächige Würfel verwenden könnten oder drei zwölfseitige, aber nicht zwei vierflächige und einen Würfel mit 20 Flächen. Number würde ein Array werden.
Das in diesem Kapitel vorgestellte Würfel-Steuerelement unterscheidet sich nicht allzusehr von den OLE-Steuerelementen aus früheren Visual-C++-Versionen. Selbst in der Dokumentation wurde keine Entscheidung getroffen, ob man nun von OLE- oder ActiveX-Steuerelementen spricht. Doch unabhängig von der Namensgebung empfiehlt es sich, ActiveX-Steuerelemente in Ihr Programmierrepertoire aufzunehmen, so daß Sie eine Bedieneroberfläche realisieren können, die eben genau Ihren Vorstellungen entspricht. Um mehr über verwandte Themen zu erfahren, sollten Sie die folgenden Kapitel lesen: