vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 1

Tag 7

Text und Schriften

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

Schriften suchen und verwenden

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.

Die verfügbaren Schriften auflisten

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.

Callback-Funktionen

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

Der Funktionstyp EnumFontFamProc

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.

Eine Schrift verwenden

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);

Die im obigen Code verwendete CFont-Variable sollte man als Member-Variable der Klasse, in der der Code steht, deklarieren. Im Beispielcode ist die Variable unmittelbar über der Stelle, wo sie verwendet wird, deklariert, um die Art und Weise ihrer Deklaration zu zeigen. Die Variable sollte nicht als lokale Variable in einer Funktion deklariert oder verwendet werden.

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.

Tabelle 7.1: Konstanten für die Gewichtung der Schrift

Konstante

Wert

FW_DONTCARE

0

FW_THIN

100

FW_EXTRALIGHT

200

FW_ULTRALIGHT

200

FW_LIGHT

300

FW_NORMAL

400

FW_REGULAR

400

FW_MEDIUM

500

FW_SEMIBOLD

600

FW_DEMIBOLD

600

FW_BOLD

700

FW_EXTRABOLD

800

FW_ULTRABOLD

800

FW_BLACK

900

FW_HEAVY

900

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.

Tabelle 7.2: Zeichensatzkonstanten für Schriften

Konstante

Wert

ANSI_CHARSET

0

DEFAULT_CHARSET

1

SYMBOL_CHARSET

2

SHIFTJIS_CHARSET

128

OEM_CHARSET

255

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.

Schriften verwenden

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.

Das Anwendungsgerüst erstellen

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

Tabelle 7.3: Eigenschaftseinstellungen der Steuerelemente

Objekt

Eigenschaft

Einstellung

Text

ID

Titel

IDC_STATIC

&Text eingeben:

Eingabefeld

ID

IDC_ESAMPTEXT

Text

ID

Titel

IDC_STATIC

Schrift &auswählen

Listenfeld

ID

IDC_LFONTS

Gruppenfeld

ID

Titel

IDC_STATIC

Schriftbeispiel

Text (innerhalb des Gruppenfeldes; so anpassen, daß Gruppenfeld ausgefüllt wird)

ID

Titel

IDC_DISPLAYTEXT

Leerer String

Schaltfläche

ID

Titel

IDC_EXIT

&Beenden

4. Fügen Sie mit dem Klassen-Assistenten die Variablen entsprechend Tabelle 7.4 für die Steuerelemente des Dialogfelds hinzu.

Tabelle 7.4: Variablen der Steuerelemente

Objekt

Name

Kategorie

Typ

IDC_DISPLAYTEST

m_ctlDisplayText

m_strDisplayText

Control

Wert

CStatic

CString

IDC_LFONTS

m_ctlFontList

m_strFontName

Control

Wert

CListBox

CString

IDC_ESAMPTEXT

m_strSampText

Wert

CString

5. Fügen Sie - wie in den vergangenen Lektionen - der Schaltfläche IDC_EXIT eine Funktion hinzu, um die Anwendung zu schließen.

Eine Schriftliste aufbauen

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.

Text für Schriftprobe festlegen

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.

Anzuzeigende Schrift auswählen

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

Zusammenfassung

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.

Fragen und Antworten

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;
}

Workshop

Kontrollfragen

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?

Übungen

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



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH.
Elektronische Fassung des Titels: Visual C++ 6 in 21 Tagen, ISBN: 3-8272-2035-1