In den meisten Windows-Anwendungen brauchen Sie sich nicht darum zu kümmern, welche Schriften, in welcher Stärke, Höhe usw. zu verwenden sind. Wenn Sie nicht die Schrift explizit festlegen, nimmt Windows eine Standardschrift für die Anwendung an. Wollen Sie eine bestimmte Schrift einsetzen, können Sie die Schrift für ein bestimmtes Dialogfeld über die Eigenschaften des Dialogfelds spezifizieren. Manchmal möchte man aber die in der Anwendung einzusetzende Schrift selbst steuern können oder dem Benutzer die Möglichkeit geben, eine Schrift für eine bestimmte Situation auszuwählen. Für diese Fälle lernen Sie heute, wie man Schriften ändert und auflistet. Insbesondere geht es heute darum, wie man ...
Bei der Arbeit mit Schriften muß man zunächst wissen, daß nicht jedes System, auf dem die Anwendung läuft, über die gleichen installierten Schriften verfügt. Schriften sind in Dateien festgelegt, die man in Windows-Systemen relativ leicht installieren und wieder entfernen kann. Jeder Computerbenutzer kann sein System mit einer beliebigen Auswahl von Schriften anpassen. Wenn Sie eine Schrift festlegen, die in einem System nicht existiert, wählt Windows entweder die Standardschrift des Systems oder eine möglichst ähnliche alternative Schrift.
Man kann aber die verfügbaren Schriften des Betriebssystems abfragen. Bei dieser Methode können Sie selbst entscheiden oder den Benutzer darüber entscheiden lassen, welche der verfügbaren Schriften zu verwenden sind. Wenn Sie die verfügbaren Schriften abfragen, können Sie auch die aufgelisteten Schriften einschränken oder alle auflisten und dann verschiedene Schriften auf der Basis verschiedener Attribute auswählen.
Um eine Liste der verfügbaren Schriften auf einem Computer zu erhalten, rufen Sie
die Windows-API-Funktion (API - Application Programming Interface, Anwendungsprogrammierschnittstelle)
EnumFontFamiliesEx
auf. Diese Funktion teilt Windows mit,
daß Sie eine Liste der Schriften im System wünschen. Bevor Sie diese Funktion verwenden
und erwarten, daß sie Ihnen eine große Liste der verfügbaren Schriften liefert,
müssen Sie verstehen, auf welche Weise die Funktion die Liste zurückgibt.
Eines der Schlüsselargumente der Funktion EnumFontFamiliesEx
ist die Adresse einer
anderen Funktion. Diese zweite Funktion ist eine sogenannte Callback-Funktion (oder
Anwendungsrückruf-Routine), die durch das Betriebssystem aufgerufen wird. Bei fast
jeder Aufzählungsfunktion im Betriebssystem Windows übergibt man die Adresse einer
Callback-Funktion als Argument, da die Callback-Funktion einmal für jedes Element
in der Aufzählungsliste aufgerufen wird. Mit anderen Worten muß man eine
Funktion in die Anwendung einbinden, um jede einzelne Schrift des Systems zu empfangen,
und dann selbst eine Liste aufbauen.
Wenn man diese Funktion erstellt, um jede Schrift zu empfangen und die Liste zu erstellen,
kann man die Callback-Funktion nicht nach Gutdünken selbst festlegen. Alle
Callback-Funktionen sind bereits im Windows-API vordefiniert. Man muß einen bestimmten
Typ von Callback-Funktion verwenden, um die Liste der Schriften zu empfangen.
Der betreffende Funktionstyp lautet EnumFontFamProc
. Dieser Funktionstyp
legt fest, wie Ihre Funktion definiert sein muß, welche Argumente vorhanden sein
müssen und welcher Typ des Rückgabewertes zurückzugeben ist. Der Funktionstyp
legt nicht fest, wie die Funktion zu benennen ist oder wie sie intern arbeitet. Diese
Aspekte liegen vollkommen in Ihrer Hand.
Die Funktion EnumFontFamiliesEx
, die Sie aufrufen, um die Liste der verfügbaren
Schriften abzufragen, übernimmt fünf Argumente. Ein typischer Einsatzfall dieser
Funktion sieht folgendermaßen aus:
// Eine Gerätekontextvariable erzeugen
CClientDC dc (this);
// Eine LOGFONT-Struktur deklarieren
LOGFONT lLogFont;
// Zeichensatz festlegen
lLogFont.lfCharSet = DEFAULT_CHARSET;
// Alle Schriften spezifizieren
lLogFont.lfFaceName[0] = NULL;
// Muß außer bei Hebräisch oder Arabisch null sein
lLogFont.lfPitchAndFamily = 0;
// Schriftfamilien auflisten
::EnumFontFamiliesEx((HDC) dc, &lLogFont,
(FONTENUMPROC) EnumFontFamProc, (LPARAM) this, 0);
Das erste Argument ist ein Gerätekontext, der eine Instanz der Klasse CClientDC
sein
kann. Jede Anwendung, die im Betriebssystem Windows läuft, verfügt über einen Gerätekontext.
Dieser stellt dem Betriebssystem eine Reihe notwendiger Informationen -
was für die Anwendung verfügbar ist und was nicht - bereit.
Im zweiten Argument übergibt man einen Zeiger auf eine LOGFONT
-Struktur. Diese enthält
die Informationen über die Schriftarten, die man auflisten möchte. In der Struktur
kann man angeben, welcher Zeichensatz aufzulisten ist oder ob man alle Schriften einer
bestimmten Schriftfamilie haben möchte. Wenn Sie an allen Schriften des Systems
interessiert sind, übergeben Sie für dieses Argument den Wert NULL
.
Das dritte Argument gibt die Adresse der Callback-Funktion an, über die die Liste der Schriften erstellt wird. Die Adresse einer Funktion übergibt man einfach, indem man den Funktionsnamen als Argument verwendet. Der Visual C++-Compiler ersetzt den Funktionsnamen durch die Adresse der Funktion. Allerdings müssen Sie den Typ der Funktion in den Typ derjenigen Callback-Funktion umwandeln, auf die die Funktion zurückgreift.
Das vierte Argument stellt einen LPARAM
-Wert dar, der an die Callback-Funktion weitergereicht
wird. Windows verwendet diesen Parameter nicht, er liefert aber der Callback-Funktion
einen Kontext, in dem die Schriftliste zu erstellen ist. Im Beispiel ist der
übergebene Wert ein Zeiger auf das Fenster, in dem der Code ausgeführt wird. Auf
diese Weise kann die Callback-Funktion diesen Zeiger verwenden, um auf eine beliebige
Struktur zuzugreifen, die sie für den Aufbau der Schriftliste benötigt. Der Zeiger
kann auch der erste Knoten in einer verketteten Liste von Schriften oder ähnlicher
Strukturen sein.
Das fünfte und letzte Argument ist für zukünftige Versionen von Windows reserviert und muß momentan auf 0 gesetzt werden, damit die Anwendung einen Wert übergibt, der kein Fehlverhalten der Funktion bewirkt.
Wenn Sie Ihre Callback-Funktion erstellen, muß sie als unabhängige Funktion definiert
sein und nicht als Element irgendeiner C++-Klasse. Eine typische Funktionsdeklaration
von EnumFontFamProc
sieht folgendermaßen aus:
int CALLBACK EnumFontFamProc(
LPENUMLOGFONT lpelf,
LPNEWTEXTMETRIC lpntm,
DWORD nFontType,
long lParam)
{
// Einen Zeiger auf das Dialogfenster erzeugen
CMyDlg* pWnd = (CMyDlg*) lParam;
// Den Schriftname in das Listenfeld hinzufügen
pWnd->m_ctlFontList.AddString(lpelf->elfLogFont.lfFaceName);
// 1 zurückgeben, um die Aufzählung fortzusetzen
return 1;
}
Das erste Argument an diese Funktion ist ein Zeiger auf eine ENUMLOGFONTEX
-Struktur.
Diese enthält Informationen über die logischen Attribute der Schrift, einschließlich
Schriftname, Stil und Skript. Unter ein und demselben Schriftnamen können mehrere
Schriften mit unterschiedlichen Stilen - Standard, Fett, Kursiv und Fett Kursiv - erscheinen.
Das zweite Argument ist ein Zeiger auf eine NEWTEXTMETRICEX
-Struktur, die Informationen
über die physikalischen Attribute der Schrift wie Höhe, Breite und Zeichenabstand
enthält. Es handelt sich dabei um relative Werte, da sie je nach Größe der Schrift
zu skalieren sind.
Das dritte Argument ist ein Flag, das den Typ der Schrift spezifiziert. Dieses Flag kann eine Kombination der folgenden Werte darstellen:
Schließlich gibt das vierte Argument den Wert an, der an die Funktion EnumFontFamiliesEx
übergeben wurde. Im Beispiel war das ein Zeiger auf das Dialogfeld, in dem
die Liste der Schriften zu erstellen ist. Wenn man diesen Wert als Zeiger auf das Dialogfeld
umwandelt, kann die Funktion auf ein Listenfeld zugreifen, um die Schriftnamen
hinzuzufügen.
Der Rückgabewert aus dieser Funktion bestimmt, ob sich die Liste der Schriften fortsetzt.
Liefert die Funktion 0
, beendet das Betriebssystem die Auflistung der verfügbaren
Schriften. Wenn die Funktion eine 1
liefert, setzt das Betriebssystem die Liste der
verfügbaren Schriften fort.
Um eine bestimmte Schrift in einer Anwendung einzusetzen, ruft man eine Instanz der
Klasse CFont
auf. Über die Methode CreateFont
läßt sich die zu verwendende Schrift
zusammen mit deren Größe, Stil und Ausrichtung festlegen. Nachdem Sie eine Schrift
erstellt haben, können Sie ein Steuerelement oder Fenster anweisen, diese Schrift zu
verwenden, indem Sie die Methode SetFont
des jeweiligen Objekts aufrufen. Dieser
Ablauf sieht beispielsweise wie folgt aus:
CFont m_fFont; // Die zu verwendende Schrift
// Die zu verwendende Schrift erzeugen
m_fFont.CreateFont(12, 0, 0, 0, FW_NORMAL,
0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
FF_DONTCARE, m_sFontName);
// Die Schrift für den Anzeigebereich festlegen
m_ctlDisplayText.SetFont(&m_fFont);
Es sieht so einfach aus - lediglich zwei Funktionen aufrufen. Aber an die Funktion
CreateFont
ist eine furchtbar große Zahl von Argumenten zu übergeben. Gerade diese
Argumente machen die Methode CreateFont
zu einer flexiblen Funktion mit umfangreicher
Funktionalität. Nachdem man die Schrift erstellt hat, kann man sie ganz
einfach einsetzen, indem man die Schrift an die Methode SetFont
übergibt. Diese Methode
ist eine Elementfunktion der Klasse CWnd
und demzufolge für alle Fenster- und
Steuerelementklassen in Visual C++ verfügbar. Das bedeutet, daß man dieses Verfahren
für alle visuellen Objekte in einer Visual C++-Anwendung einsetzen kann.
Um die Arbeitsweise der Funktion CreateFont
zu verstehen, sehen wir uns die einzelnen
Argumente an, die an die Funktion zu übergeben sind. Die Funktion ist folgendermaßen
definiert:
BOOL CreateFont(
int nHeight,
int nWidth,
int nEscapement,
int nOrientation,
int nWeight,
BYTE bItalic,
BYTE bUnderline,
BYTE cStrikeOut,
BYTE nCharSet,
BYTE nOutPrecision,
BYTE nClipPrecision,
BYTE nQuality,
BYTE nPitchAndFamily,
LPCTSTR lpszFaceName);
Das erste dieser Argumente, nHeight
, legt die Höhe der zu verwendenden Schrift fest.
Dieser logische Wert wird in einen physikalischen Wert übersetzt. Ist der Wert gleich
0
, kommt ein plausibler Standardwert zur Anwendung. Wenn der Wert größer oder
kleiner als 0
ist, wird die absolute Höhe in Geräteeinheiten konvertiert. Wichtig ist
hierbei vor allem, daß Höhenwerte von 10
und -10
grundsätzlich gleich sind.
Das zweite Argument, nWidth
, spezifiziert die durchschnittliche Breite der Zeichen in
der Schrift. Dieser logische Wert wird in einen physikalischen Wert konvertiert, genau
wie es bei der Höhe der Fall ist.
Das dritte Argument, nEscapement
, bestimmt den Winkel, in dem der Text ausgegeben
wird. Dieser Wert ist in Schritten von 0,1 Grad entgegen dem Uhrzeigersinn spezifiziert.
Möchte man senkrechten Text von unten nach oben ausgeben, gibt man 900
für dieses Argument an. Der Wert 0
steht für normalen waagerecht von links nach
rechts verlaufenden Text.
Das vierte Argument, nOrientation
, bestimmt den Winkel der einzelnen Zeichen in
der Schrift. Das funktioniert genau wie beim vorherigen Argument, steuert aber die
zeichenweise Ausgabe und nicht die zeilenweise Textausgabe. Um Zeichen von oben
nach unten auszugeben, setzt man diesen Wert auf 1800
. Mit dem Wert 900
werden
die Zeichen liegend ausgegeben.
Im fünften Argument, nWeight
, gibt man die Gewichtung oder die Strichstärke der
Schrift an. Dieser Wert umfaßt einen Bereich von 0
bis 1000
, wobei 1000
sehr fett bedeutet.
Für dieses Argument sind Konstanten definiert, damit man diesen Wert einfach
und einheitlich steuern kann. Tabelle 7.1 listet diese Konstanten auf.
Die tatsächliche Interpretation und Verfügbarkeit dieser Gewichte hängt von der
Schrift ab. Einige Schriften haben nur die Gewichte FW_NORMAL
, FW_REGULAR
und
FW_BOLD
. Wenn man FW_DONTCARE
angibt, wird ein Standardwert wie bei den meisten
der übrigen Argumente eingesetzt.
Das sechste Argument, bItalic
, legt fest, ob die Schrift kursiv auszugeben ist. Es handelt
sich um einen Booleschen Wert, wobei 0
für nicht kursive Schrift und jeder andere
Wert für eine kursive Schrift steht.
Das siebente Argument, bUnderline
, ist ebenfalls ein Boolescher Wert und kennzeichnet
mit einer 0
, daß die Schrift nicht unterstrichen ist, während jeder andere Wert für
eine unterstrichene Schrift steht.
Das achte Argument, cStrikeOut
, gibt an, ob die Zeichen in der Schrift mit einer Linie
durch das Zeichen - also durchgestrichen - darzustellen sind. Auch dieser Boolesche
Wert bedeutet TRUE
- durchgestrichen - bei allen von 0
verschiedenen Werten
und FALSE
- nicht durchgestrichen - bei 0
.
Das neunte Argument, nCharSet
, legt den Zeichensatz der Schrift fest. Die verfügbaren
Konstanten für diesen Wert sind in Tabelle 7.2 aufgeführt.
Das System, auf dem Ihre Anwendung läuft, kann andere Zeichensätze verwenden. Darüber hinaus ist der OEM-Zeichensatz systemabhängig. Das verkompliziert die ganze Sache für Systeme unterschiedlicher Hersteller. Wenn Sie einen dieser Zeichensätze verwenden, ist es riskant, die auszugebenden Strings zu manipulieren, so daß es sich empfiehlt, einfach den anzuzeigenden String zu übergeben.
Das zehnte Argument, nOutPrecision
, legt fest, wie nahe die Ausgabe den angeforderten
Werten für Höhe, Breite, Zeichenausrichtung, Textwinkel und Zeichenabstand
der Schrift entsprechen muß. Die verfügbaren Werte für dieses Argument lauten:
Die Werte OUT_DEVICE_PRECIS
, OUT_RASTER_PRECIS
und OUT_TT_PRECIS
steuern, welche
Schrift zu wählen ist, wenn mehrere Schriften mit dem gleichen Namen existieren.
Wenn Sie zum Beispiel den Wert OUT_TT_PRECIS
verwenden und eine Schrift sowohl
als TrueType- als auch als Rasterversion spezifizieren, kommt die TrueType-
Version zum Einsatz. Praktisch erzwingt OUT_TT_PRECIS
die Verwendung einer TrueType-Schrift,
selbst wenn die angegebene Schrift nicht in einer TrueType-Version vorliegt.
Das elfte Argument, nClipPrecision
, legt fest, wie die Zeichen, die teilweise außerhalb
des Anzeigebereichs liegen, abzuschneiden sind. Die Werte für dieses Argument
lauten:
Diese Werte lassen sich mit OR
verknüpfen, um eine Kombination der Clipping-Verfahren
zu spezifizieren.
Das zwölfte Argument, nQuality
, spezifiziert die Ausgabequalität und gibt an, wie genau
das GDI (Graphics Device Interface) versuchen muß, die logischen Attribute in die
physikalische Schriftausgabe umzusetzen. Die verfügbaren Werte für dieses Argument
lauten:
Im dreizehnten Argument, nPitchAndFamily
, legt man den Zeichenabstand und die
Schriftfamilie fest. Dieser Wert besteht eigentlich aus zwei Werten, die per OR
verknüpft
sind, um einen Kombinationswert zu bilden. Der erste Satz von verfügbaren
Werten lautet:
Diese Werte geben den Zeichenabstand der Schrift an. Die zweite Gruppe von verfügbaren Werten gibt die Schriftfamilie an. Die verfügbaren Werte für diesen Teil des Arguments heißen:
Die Schriftfamilie beschreibt auf allgemeine Weise die Erscheinung einer Schrift. Man kann mit dem Wert für die Schriftfamilie eine alternative Schrift wählen, wenn eine bestimmte Schrift nicht im System vorhanden ist.
Das letzte Argument, lpszFacename
, ist ein normaler C-String, der den Namen der zu
verwendenden Schrift angibt. Dieser Schriftname stammt aus der Schriftinformation,
die die Callback-Funktion EnumFontFamProc
zurückgibt.
Heute erstellen Sie eine Anwendung, die dem Benutzer die Möglichkeit gibt, die anzuzeigende Schrift aus einer Liste der verfügbaren Schriften auszuwählen. Der Benutzer gibt den anzuzeigenden Text in der ausgewählten Schrift ein, um sich vom Aussehen der Schrift ein Bild machen zu können.
Die heutige Anwendung erstellen Sie mit den folgenden Schritten:
1. Erzeugen Sie mit dem MFC-Anwendungs-Assistenten einen neuen Arbeitsbereich.
Nennen Sie das Projekt Tag7
.
2. Übernehmen Sie die Standardeinstellungen, die Sie bereits in den Projekten der
vergangenen Tage verwendet haben. Als Titel der Anwendung geben Sie Schriften
ein.
3. Entwerfen Sie das Hauptdialogfeld gemäß Abbildung 7.1 mit den Eigenschaften nach Tabelle 7.3.
Abbildung 7.1:
Der Entwurf des Hauptdialogfelds
4. Fügen Sie mit dem Klassen-Assistenten die Variablen entsprechend Tabelle 7.4 für die Steuerelemente des Dialogfelds hinzu.
5. Fügen Sie - wie in den vergangenen Lektionen - der Schaltfläche IDC_EXIT
eine
Funktion hinzu, um die Anwendung zu schließen.
Damit Sie die Liste der Schriften erstellen können, fügen Sie eine Callback-Funktion
hinzu. Damit lassen Sie alle Schriften auflisten und in das Listenfeld des Dialogfelds
schreiben. Fügen Sie zu diesem Zweck am Anfang der Header-Datei Tag7Dlg.h
die
Funktionsdeklaration aus Listing 7.1 ein. Diese Funktion läßt sich nicht mit den in Visual
C++ verfügbaren Werkzeugen hinzufügen. Sie müssen die Datei öffnen und die
Funktion in eigener Regie eintragen.
Listing 7.1: Die Deklaration der Callback-Funktion in der Header-Datei Tag7Dlg.h
1: #if _MSC_VER > 1000
2: #pragma once
3: #endif // _MSC_VER > 1000
4:
5: int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,
6: LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam);
7:
8: ///////////////////////////////////////////////////////////////////////////
9: // CTag7Dlg Dialogfeld
10:
11: class CTag7Dlg : public CDialog
12: .
13: .
14: .
Nachdem Sie die Funktionsdeklaration in die Header-Datei aufgenommen haben, öffnen
Sie die Quellcodedatei Tag7Dlg.cpp
, gehen an das Ende der Datei und fügen die
Funktionsdefinition aus Listing 7.2 ein.
Listing 7.2: Die Definition der Callback-Funktion in der Quelldatei Tag7Dlg.cpp
1: int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,
2: LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam)
3: {
4: // Zeiger auf Dialogfenster erzeugen
5: CTag7Dlg* pWnd = (CTag7Dlg*) lParam;
6:
7: // Schriftname in Listenfeld einfügen
8: pWnd->m_ctlFontList.AddString(lpelf->elfLogFont.lfFaceName);
9: // 1 zurückgeben, um Schriftauflistung fortzusetzen
10: return 1;
11: }
Damit haben Sie die Callback-Funktion definiert und müssen nun noch eine Funktion hinzufügen, um die Liste der Schriften vom Betriebssystem abzurufen. Führen Sie dazu die folgenden Schritte aus:
1. Gehen Sie im Arbeitsbereich auf die Registerkarte Klassen.
2. Markieren Sie die Klasse CTag7Dlg
, klicken Sie mit der rechten Maustaste, und
wählen Sie Member-Funktion hinzufügen aus dem Kontextmenü.
3. Legen Sie den Funktionstyp mit void
, die Funktionsdeklaration als FillFontList
und den Zugriff als Privat fest. Klicken Sie auf OK, um das Dialogfeld zu schließen
und die Funktion hinzuzufügen.
4. Bearbeiten Sie die Funktionsdefinition gemäß Listing 7.3.
Listing 7.3: Die Funktion FillFontList
1: void CTag7Dlg::FillFontList()
2: {
3: LOGFONT lf;
4:
5: // LOGFONT-Struktur initialisieren
6: lf.lfCharSet = DEFAULT_CHARSET;
7: strcpy(lf.lfFaceName, "");
8: // Listenfeld leeren
9: m_ctlFontList.ResetContent();
10: // Gerätekontextvariable erzeugen
11: CClientDC dc (this);
12: // Schriftfamilien aufzählen
13: ::EnumFontFamiliesEx((HDC) dc, &lf,
14: (FONTENUMPROC) EnumFontFamProc, (LPARAM) this, 0);
15: }
5. Bearbeiten Sie die Funktion OnInitDialog
gemäß Listing 7.4, um die Funktion
FillFontList
aufzurufen.
Listing 7.4: Die bearbeitete Funktion OnInitDialog
1: BOOL CTag7Dlg::OnInitDialog()
2: {
3: CDialog::OnInitDialog();
4: .
5: .
6: .
7: // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
8:
9: ///////////////////////
10: // EIGENER CODE, ANFANG
11: ///////////////////////
12:
13: // Listenfeld für Schriften füllen
14: FillFontList();
15:
16: ///////////////////////
17: // EIGENER CODE, ENDE
18: ///////////////////////
19:
20: return TRUE; // Geben Sie TRUE zurück, außer ein Steuerelement soll den
ÂFokus erhalten
21: }
Wenn Sie die Anwendung jetzt kompilieren und ausführen, sollte das Listenfeld mit den Namen aller im System verfügbaren Schriften gefüllt sein. Allerdings gibt es einen Aspekt dieser Liste, den Sie in Ihrer Anwendung wahrscheinlich nicht haben möchten. Abbildung 7.2 zeigt viele doppelte Einträge in der Liste der Schriften im Listenfeld. Es wäre schön, wenn man diese Duplikate ausblenden könnte, um nur noch eine Zeile pro Schriftart anzuzeigen.
Abbildung 7.2:
Alle Schriften im System auflisten
Es zeigt sich, daß der Funktionsaufruf von EnumFontFamiliesEx
von Haus aus synchron
ist. Das bedeutet, daß die Funktion erst dann zurückkehrt, wenn alle Schriften
im System in Aufrufen Ihrer Callback-Funktion aufgelistet sind. Mit etwas Code in der
Funktion FillFontList
lassen sich aber alle doppelten Einträge eliminieren, sobald
das Listenfeld gefüllt ist. Dazu modifizieren Sie die Funktion FillFontList
gemäß Listing
7.5.
Listing 7.5: Die modifizierte Funktion FillFontList
1: void CTag7Dlg::FillFontList()
2: {
3: int iCount; // Anzahl der Schriften
4: int iCurCount; // Aktuelle Schrift
5: CString strCurFont; // Aktueller Schriftname
6: CString strPrevFont = ""; // Vorheriger Schriftname
7: LOGFONT lf;
8:
9: // LOGFONT-Struktur initialisieren
10: lf.lfCharSet = DEFAULT_CHARSET;
11: strcpy(lf.lfFaceName, "");
12: // Listenfeld leeren
13: m_ctlFontList.ResetContent();
14: // Gerätekontextvariable erzeugen
15: CClientDC dc (this);
16: // Schriftfamilien aufzählen
17: ::EnumFontFamiliesEx((HDC) dc, &lf,
18: (FONTENUMPROC) EnumFontFamProc, (LPARAM) this, 0);
19: // Anzahl der Schriften im Listenfeld ermitteln
20: iCount = m_ctlFontList.GetCount();
21: // Schleife vom letzten zum ersten Eintrag im Listenfeld,
22: // um doppelte Einträge zu suchen und zu löschen
23: for (iCurCount = iCount; iCurCount > 0; iCurCount--)
24: {
25: // Aktuellen Schriftname holen
26: m_ctlFontList.GetText((iCurCount - 1), strCurFont);
27: // Mit vorherigem Schriftnamen identisch?
28: if (strCurFont == strPrevFont)
29: {
30: // Wenn ja, dann löschen
31: m_ctlFontList.DeleteString((iCurCount - 1));
32: }
33: // Vorherigen auf aktuellen Schriftname setzen
34: strPrevFont = strCurFont;
35: }
36: }
Beachten Sie, daß die for
-Schleife am Ende der Liste beginnt und rückwärts nach
oben arbeitet. Damit kann man den aktuellen Eintrag löschen, ohne sich um den
Schleifenzähler kümmern zu müssen. Ansonsten müßte man den Zähler korrigieren,
um keine Zeile zu überspringen. Wenn Sie jetzt die Anwendung kompilieren und ausführen,
sollten keine doppelten Einträge mehr in der Liste der verfügbaren Schriften
zu sehen sein.
Bevor man die Schrift für den Benutzer anzeigen kann, muß man etwas Text in den Anzeigebereich schreiben. In das Eingabefeld im oberen Teil des Dialogfelds gibt der Benutzer den Text ein, der in der ausgewählten Schrift anzuzeigen ist. Führen Sie die folgenden Schritte aus, um die Schriftprobe darzustellen:
1. Übernehmen Sie in die Funktion OnInitDialog
den Code aus Listing 7.6, um das
Eingabefeld zu initialisieren und Text anzuzeigen.
Listing 7.6: Die modifizierte Funktion OnInitDialog
1: BOOL CTag7Dlg::OnInitDialog()
2: {
3: CDialog::OnInitDialog();
4: .
5: .
6: .
7: // ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen
8:
9: ///////////////////////
10: // EIGENER CODE, ANFANG
11: ///////////////////////
12:
13: // Listenfeld für Schriften füllen
14: FillFontList();
15:
16: // Einzugebenden Text initialisieren
17: m_strSampText = "Testen";
18: // Text in Feld für Schriftbeispiel kopieren
19: m_strDisplayText = m_strSampText;
20: // Dialogfeld aktualisieren
21: UpdateData(FALSE);
22:
23: ///////////////////////
24: // EIGENER CODE, ENDE
25: ///////////////////////
26:
27: return TRUE; // Geben Sie TRUE zurück, außer ein Steuerelement soll den
ÂFokus erhalten
28: }
2. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Nachricht EN_CHANGE
für das Eingabefeld IDC_ESAMPTEXT
hinzu.
3. Schreiben Sie in die Funktion den Code aus Listing 7.7.
Listing 7.7: Die Funktion OnChangeEsamptext
1: void CTag7Dlg::OnChangeEsamptext()
2: {
3: // TODO: Wenn dies ein RICHEDIT-Steuerelement ist, sendet das
ÂSteuerelement diese
4: // Benachrichtigung nicht, bevor Sie nicht die Funktion
ÂCDialog::OnInitDialog()
5: // überschreiben und CRichEditCrtl().SetEventMask() aufrufen, wobei
6: // eine ODER-Operation mit dem Attribut ENM_CHANGE und der Maske erfolgt.
7:
8: // TODO: Fügen Sie hier Ihren Code für die
ÂBenachrichtigungsbehandlungsroutine des Steuerelements hinzu
9:
10: ///////////////////////
11: // EIGENER CODE, ANFANG
12: ///////////////////////
13:
14: // Variablen mit Steuerelementen des Dialogfelds aktualisieren
15: UpdateData(TRUE);
16:
17: // Aktuellen Text in Schriftbeispiel kopieren
18: m_strDisplayText = m_strSampText;
19:
20: // Dialogfeld mit Variablen aktualisieren
21: UpdateData(FALSE);
22:
23: ///////////////////////
24: // EIGENER CODE, ENDE
25: ///////////////////////
26: }
Wenn Sie die Anwendung jetzt kompilieren und ausführen, sollten Sie in das Eingabefeld Text eingeben können, der gleichzeitig im Anzeigebereich für die Schrift im darunterliegenden Gruppenfeld erscheint.
Bevor Sie die Schrift für den Anzeigebereich ändern können, brauchen Sie eine
CFont
-Elementvariable der Dialogfeldklasse, über die Sie die anzuzeigende Schrift festlegen
und ändern können. Führen Sie die folgenden Schritte aus, um diese Variable
hinzuzufügen:
1. Klicken Sie im Arbeitsbereich auf der Registerkarte Klassen mit der rechten
Maustaste auf die Klasse CTag7Dlg
. Wählen Sie aus dem Kontextmenü den Befehl
Member-Variable hinzufügen.
2. Legen Sie den Typ der Variablen als CFont
, den Variablennamen mit
m_fSampFont
und den Zugriff als Privat fest. Klicken Sie auf OK, um das Dialogfeld
zu schließen und die Variable hinzuzufügen.
Wenn Sie den Code hinzufügen, um die ausgewählte Schrift zu verwenden, schreiben Sie dafür eine eigene Funktion, die nicht mit einem Steuerelement verbunden ist. Der Grund dafür zeigt sich, wenn Sie weiter an der heutigen Beispielanwendung arbeiten. Um die Funktion für die Anzeige und Verwendung der ausgewählten Schrift hinzuzufügen, führen Sie die folgenden Schritte aus:
1. Klicken Sie im Arbeitsbereich auf der Registerkarte Klassen mit der rechten
Maustaste auf die Klasse CTag7Dlg
. Wählen Sie aus dem Kontextmenü den Befehl
Member-Funktion hinzufügen.
2. Legen Sie den Funktionstyp als void
, die Funktionsdeklaration mit SetMyFont
und
den Zugriffsstatus als Privat fest. Klicken Sie auf OK, um das Dialogfeld zu schließen
und die Funktion hinzuzufügen.
3. Übernehmen Sie in die Funktion den Code aus Listing 7.8.
Listing 7.8: Die Funktion SetMyFont
1: void CTag7Dlg::SetMyFont()
2: {
3: CRect rRect; // Rechteck des Anzeigebereichs
4: int iHeight; // Höhe des Anzeigebereichs
5:
6: // Wurde eine Schrift ausgewählt?
7: if (m_strFontName != "")
8: {
9: // Abmessungen des Feldes für Schriftbeispiel ermitteln
10: m_ctlDisplayText.GetWindowRect(&rRect);
11: // Höhe des Feldes berechnen
12: iHeight = rRect.top - rRect.bottom;
13: // Sicherstellen, daß Höhe positiv
14: if (iHeight < 0)
15: iHeight = 0 - iHeight;
16: // Aktuelle Schrift freigeben
17: m_fSampFont.Detach();
18: // Zu verwendende Schrift erzeugen
19: m_fSampFont.CreateFont((iHeight - 5), 0, 0, 0, FW_NORMAL,
20: 0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
21: CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
22: FF_DONTCARE, m_strFontName);
23:
24: // Schrift für Anzeigefeld der Schriftprobe festlegen
25: m_ctlDisplayText.SetFont(&m_fSampFont);
26: }
27: }
4. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Nachricht
LBN_SELCHANGE
für das Listenfeld IDC_LFONTS
hinzu, und übernehmen Sie in diese
Funktion den Code aus Listing 7.9.
Listing 7.9: Die Funktion OnSelchangeLfonts
1: void CTag7Dlg::OnSelchangeLfonts()
2: {
3: // TODO: Code für die Behandlungsroutine der Steuerelement-
ÂBenachrichtigung hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Variablen mit Steuerelementen des Dialogfelds aktualisieren
10: UpdateData(TRUE);
11:
12: // Schrift für Beispiel festlegen
13: SetMyFont();
14:
15: ///////////////////////
16: // EIGENER CODE, ENDE
17: ///////////////////////
18: }
Die Funktion SetMyFont
prüft zuerst, ob eine Schrift ausgewählt wurde. Als nächstes
wird der Bereich des statischen Textfelds abgerufen, der für die Anzeige der Schriftprobe
vorgesehen ist. Damit läßt sich eine Schrifthöhe festlegen, die etwas kleiner als
die Höhe des Bereichs ist, der für die Anzeige der Schrift zur Verfügung steht. Nachdem
die Funktion die Höhe des statischen Textfelds berechnet hat und das Ergebnis
ein positiver Wert ist, wird die ausgewählte Schrift erzeugt und das statische Textfeld
angewiesen, die neu erstellte Schrift zu verwenden.
Die Funktion OnSelchangeLfonts
kopiert die Steuerelementwerte in die zugeordneten
Variablen und ruft dann die Funktion SetMyFont
auf, um die ausgewählte Schrift zu
verwenden. Wenn Sie die Anwendung kompilieren und ausführen, sollten Sie eine
Schrift auswählen und das Ergebnis im Anzeigebereich verfolgen können, wie es Abbildung
7.3 zeigt.
Abbildung 7.3:
Die ausgewählte Schrift anzeigen
Heute haben Sie gelernt, wie man Schriften in Visual C++-Anwendungen verwendet. Dabei haben Sie eine Liste der im System verfügbaren Schriften erstellt und eine Schrift für ein Anzeigeobjekt erzeugt. Weiterhin hat die Lektion gezeigt, wie man Callback-Funktionen erstellt und einsetzt, um eine Liste der Ressourcen vom Betriebssystem Windows zu erhalten. Außerdem haben Sie gelernt, wie man auf Steuerelemente von der Callback-Funktion aus zugreift und dabei einen Fensterzeiger verwendet, den man an die Funktion, die die Ressourcenliste abruft, übergeben hat.
Frage:
Der Funktion CreateFont
muß man eine Vielzahl von Argumenten übergeben.
Kann man auch auf eine alternative Funktion zurückgreifen?
Antwort:
Eine derartige Funktion gibt es, obwohl man trotzdem noch alle Informationen
festlegt. Die Struktur LOGFONT enthält die gleichen Attribute, die man
an die Funktion CreateFont übergibt. Man kann eine Instanz dieser Struktur
deklarieren, die Attribute mit Standardwerten initialisieren und dann diese
Struktur an die Funktion CreateFontIndirect übergeben. Wenn umfangreiche
Schriftänderungen vorzunehmen sind, ist diese Methode zu bevorzugen, weil
man dieselbe Instanz der Struktur verwenden kann und nur diejenigen Attribute
modifiziert, die sich gegenüber den aktuellen Einstellungen geändert haben.
Damit erstellt man dann die verschiedenartigen Schriften.
Um diese alternative Lösung zum Erzeugen einer Schrift zu verwenden, deklariert
man eine Instanz der Struktur LOGFONT
als Element der Dialogfeldklasse und initialisiert
dann alle Attribute, bevor man die Funktion SetMyFont
aufruft. In der Funktion
SetMyFont
nehmen Sie die Modifikationen gemäß Listing 7.10 vor.
Listing 7.10: Die modifizierte Funktion SetMyFont
1: void CTag7Dlg::SetMyFont()
2: {
3:
4: // Wurde eine Schrift ausgewählt?
5: if (m_strFontName != "")
6: {
7: // Annahme, daß Schriftgröße bereits in Struktur
8: // m_lLogFont initialisiert wurde. Damit ist nur
9: // noch der Schriftname zu spezifizieren.
10: _ tcscpy(m_lLogFont.lfFaceName, m_strFontName);
11: // Zu verwendende Schrift erzeugen
12: m_fSampFont.CreateFontIndirect(&m_lLogFont);
13:
14: // Schrift für Anzeigefeld der Schriftprobe festlegen
15: m_ctlDisplayText.SetFont(&m_fSampFont);
16: }
17: }
Frage:
Wie kann ich die Schriften in meiner Liste ausschließlich auf TrueType-Schriften
begrenzen?
Antwort:
Testen Sie das an die Callback-Funktion übergebene Argument nFontType,
um den Schrifttyp zu bestimmen. Wenn Sie zum Beispiel nur TrueType-
Schriften in die Schriftliste aufnehmen wollen, modifizieren Sie die Call-
back-Funktion, um das Argument nFontType mit der Konstanten
TRUETYPE_FONTTYPE zu maskieren, und prüfen dann, ob das Ergebnis
gleich TRUETYPE_FONTTYPE ist, wie es das folgende Listing zeigt:
int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,
LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam)
{
// Zeiger auf Dialogfenster erzeugen
CTag7Dlg* pWnd = (CTag7Dlg*) lParam;
// Liste auf TrueType-Schriften begrenzen
if ((nFontType & TRUETYPE_FONTTYPE) == TRUETYPE_FONTTYPE)
{
// Schriftname in Listenfeld einfügen
pWnd->m_ctlFontList.AddString(
lpelf->elfLogFont.lfFaceName);
}
// 1 zurückgeben, um Schriftauflistung fortzusetzen
return 1;
}
1. Wie kann man festlegen, daß der Text unterstrichen wird?
2. Wie läßt sich Text von oben nach unten ausgeben?
3. Wie oft wird die Callback-Funktion EnumFontFamProc
durch das Betriebssystem
aufgerufen?
1. Fügen Sie gemäß Abbildung 7.4 ein Kontrollkästchen hinzu, um wählen zu können, ob der eingegebene Text die Schrift repräsentieren soll oder der Schriftname in der ausgewählten Schrift anzuzeigen ist.
Abbildung 7.4:
Ausgewählte Schrift mit Bezeichnung der Schrift anzeigen
2. Fügen Sie gemäß Abbildung 7.5 ein Kontrollkästchen hinzu, um die Textprobe kursiv anzuzeigen.
Abbildung 7.5:
Die ausgewählte Schrift kursiv anzeigen