Programmieren - GIS-Management
Programmieren - GIS-Management
Programmieren - GIS-Management
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
<strong>Programmieren</strong><br />
Prof. Dr.-Ing. Franz-Josef Behr<br />
FACHHOCHSCHULE<br />
HOCHSCHULE FÜR<br />
STUTTGART<br />
TECHNIK<br />
Vermessung und Geoinformatik<br />
1 Einleitung<br />
Bisher sind haben wir unsere Klassen nur als einfaches Konsolenapplikationen implementiert. Dabei<br />
haben wir die Möglichkeiten moderner Betriebssystemumgebungen (graphische Benutzeroberfläche)<br />
nicht genutzt. Die Wiederverwendbarkeit blieb ausschließlich auf unsere eigenen Klassen beschränkt.<br />
Bei der Nutzung von Visual C++ haben wir die Funktionalität und den Komfort unserer<br />
Entwicklungsumgebung nur wenig ausgeschöpft. 1<br />
Dies soll in den folgenden Arbeitsschritten geändert werden!<br />
Wo wir uns bislang auf „selbst gebaute“ Klassen beschränkt haben, öffnet sich uns durch die<br />
Verfügbarkeit der Microsoft Foundation Classes (kurz MFC), die uns die Windows-Funktionalität<br />
wiederverwendbar und leicht anpassbar zu Verfügung stellt, eine völlig neue Dimension. Gepaart mit<br />
sehr hilfreichen Funktionen unserer Entwicklungsumgebung gelingt es mit relativ wenig Aufwand,<br />
fensterorientierte Anwendungen zu erstellen, die sich Windows -typisch verhalten.<br />
Man unterscheidet zumindest drei Arten von Anwendungen:<br />
? dialogfeldbasierende Anwendung (ein Fenster, kein Menü)<br />
? SDI-Anwendung (Singe Document Interface, ein Fenster, mit Menüleiste)<br />
? MDI-Anwendung (Multi Document Interface, mehrere Fenster, Menüleiste)<br />
Das Objektklassenmodell der MFC zeigt Abbildung 1.<br />
Die Klasse CObject ist für die meisten Klassen der Microsoft Foundation Class Library (MFC) die<br />
Basisklasse. Die Klasse CObject enthält mehrere nützliche Eigenschaften, die Sie in Ihre eigenen<br />
Programmobjekte übernehmen können, zum Beispiel Unterstützung der Serialisierung (Kap. 4.10),<br />
Laufzeitinformationen über Klassen und Diagnoseausgaben für Objekte. Falls Sie Ihre Klasse von<br />
CObject ableiten, kann die Klasse diese Fähigkeiten von CObject ausnutzen.<br />
CCdmTarget stellt die Basis für die Empfängerlisten-Architektur der Bibliotheksklassen dar, d. h.<br />
bietet die Zuordnung zwischen Nachrichten, Kommandos und den Methoden.<br />
Weitere Klassen, die wir in abgeleiteter Form verwenden werden, sind die Klassen CDocument und<br />
CView.<br />
Hinter vielen Klassen verbergen sich Datentypen und Funktionalitäten des Windows-API. Sie<br />
erkennen das an Klassennamen wie CPaintDC oder CBrush.<br />
1 www.hilf.de/de/site/schulung/kurse/ mfc_programmierung_schulung.pdf<br />
1
<strong>Programmieren</strong> mit der MFC<br />
Abbildung 1: Objektklassenmodell der MFC-Bibliothek.<br />
2 Erstellen einer einfachen dialogfeldbasierenden<br />
Anwendung<br />
Das nachfolgende Beispiel und der dazugehörige Text beruhen weitgehend auf dem Vorlesungsskript<br />
Einführung in die Windows-Programmierung mit Visual C++ (MFC) von Prof. Schröder.<br />
Im ersten Beispiel soll eine recht einfache Anwendung entwickelt werden.<br />
Bevor Sie an die Code-Erstellung gehen, verschaffen Sie sich zunächst eine Überblick über das<br />
gewünschte Aussehen und die Funktionalität:<br />
Auf einem Dialogfenster befinden sich zwei Eingabeelemente über die die Länge und Breite eines<br />
Rechtecks eingegeben werden kann. Nach Betätigung des Berechnen Knopfs wird die berechnete<br />
Fläche in einem Ausgabeelement ausgegeben.<br />
2
<strong>Programmieren</strong> mit der MFC<br />
2.1 Projektgerüst erstellen<br />
Abbildung 2: Zu entwickelnde Applikation.<br />
Im ersten Schritt legen Sie ein neues Projekt an, indem Sie einen entsprechenden Pfad und einen<br />
Projektnamen (im folgenden: WinFlaechenBerechnung) festlegen. Wählen Sie als Projektart MFC-<br />
ANWENDUNGS-ASSISTENT (.EXE):<br />
Wenn Sie den Anwendungs-Assistenten verwenden, müssen Sie in einigen Schritten verschiedene<br />
Detailangaben zur Art des Programms machen<br />
3
<strong>Programmieren</strong> mit der MFC<br />
Im nächsten Schritt kann man sich zwischen verschiedenen Typen von Windows-Programmen<br />
entscheiden. In diesem Beispiel beschränken wir uns auf ein dialogfeldbasiertes Programme, d. h. die<br />
gesamte Applikation besteht nur aus einem Dialogfenster.<br />
?<br />
Welche Windowsapplikation kennen Sie, die nur aus einem Dialog bestehen?<br />
4
<strong>Programmieren</strong> mit der MFC<br />
5
<strong>Programmieren</strong> mit der MFC<br />
6
<strong>Programmieren</strong> mit der MFC<br />
In der Zusammenfassung können Sie nochmals Ihre Projekteinstellungen prüfen, um dann die<br />
Erstellung des Codegerüstes mit OK einzuleiten.<br />
2.2 Arbeitsbereich<br />
2.2.1 Registerkarte Klassen<br />
Die Registerkarte Klassen des Fensters Arbeitsbereich zeigt nach abgeschlossener Erstellung des<br />
Codegerüsts die drei erstellten Klassen und ihre Elemente für alle Projekte des Arbeitsbereichs an. Der<br />
fett gedruckte Ordnername bezeichnet die aktuelle Projektkonfiguration. Wenn Sie den Inhalt des<br />
Projektordners einblenden, werden die zu diesem Projekt gehörenden Klassen angezeigt. Durch<br />
Einblenden einer Klasse werden alle Elemente dieser Klasse angezeigt. Wenn Sie auf ein Element<br />
doppelklicken, öffnet sich, wie Sie bereits aus vorangegangenen Übungen wissen, die entsprechende<br />
Stelle im Quellcode.<br />
7
<strong>Programmieren</strong> mit der MFC<br />
Abbildung 3: Klassenansicht der erzeugten Applikation.<br />
In unserem Projekt sehen Sie, dass CWinFlaechenberechnungDlg bereits mehrere Methoden besitzt<br />
(Abbildung 3), allerdings noch (fast) keine Attribute. Außerdem gibt es die globale Variable theApp<br />
(der Klasse CWinFlaechenberechnungApp).<br />
2.2.2 Dateien-Ansicht<br />
In der Dateien-Ansicht sehen Sie, das insgesamt bereits 11 Dateien zum Projekt gehören sowie eine<br />
readme-Datei:<br />
8
<strong>Programmieren</strong> mit der MFC<br />
Abbildung 4: Dateien der erzeugten Applikation.<br />
Der generierte Quellcode bildet bereits ein vollständiges Programm und kann übersetzt und ausgeführt<br />
werden! Bei der Ausführung sehen Sie, dass das Programm sich wie ein übliches Windows-Programm<br />
verhält:<br />
? Sie können das Fenster verschieben<br />
? Sie können mit der Tabulatortaste den Fokus zwischen den beiden Schaltflächen wechseln<br />
? Sie können das Programm wie gewohnt beenden<br />
? Nur, das Programm tut noch nichts! Aber das werden wir noch ändern...<br />
2.2.3 Ressourcen-Ansicht<br />
In der Ressourcen-Ansicht werden die Bildschirmelemente aufgelistet, aus denen das Programm<br />
aufgebaut ist. Angezeigt werden die Identifier dieser Ressourcen:<br />
? IDD_ABOUTBOX<br />
? IDD_WINFLAECHENBERECHNUNG_DIALOG<br />
9
<strong>Programmieren</strong> mit der MFC<br />
Abbildung 5: Ressourcenansicht.<br />
Die Identifier werden üblicherweise in großen Buchstaben geschrieben, stets beginnend mit ID.<br />
Doppelklicken Sie auf den Identifier IDD_WINFLAECHENBERECHNUNG_DIALOG, so öffnet<br />
sich ein neues Fenster: der Ressourcen-Editor.<br />
Dieser besteht zum einen aus der Arbeitsfläche, auf der das Layout des Formulars entworfen wird (im<br />
10
<strong>Programmieren</strong> mit der MFC<br />
Moment ein Formular mit einem statischen Text und den beiden Schaltflächen OK und Abbrechen).<br />
Zum anderen finden Sie den Werkzeugkasten mit den Steuerelementen (Abbildung 6).<br />
2.3 Layout des Formulars<br />
Abbildung 6: Verfügbare Steuerelemente.<br />
Im nächsten Schritt werden Sie zunächst das Layout des Formulars zu entwerfen. Das Programm<br />
erfordert folgende Steuerelemente:<br />
? Dialogfenster (vorgegeben, ist zu verändern)<br />
? Minimieren-Schaltfläche<br />
? Maximieren-Schaltfläche<br />
? zwei Schaltflächen (Beenden und Berechnung durchführen)<br />
? statische Texte<br />
? Eingabefelder<br />
? ein Gruppenfeld (Rahmen mit Überschrift)<br />
Passen Sie nun das Layout an. Ändern Sie dabei zunächst nicht die OK-Schaltfläche! Sie sollten<br />
folgendes Ergebnis erzielen:<br />
11
<strong>Programmieren</strong> mit der MFC<br />
Zur Bearbeitung eines Elementes ist es zunächst durch einen Mausklick auszuwählen. Nun kann es<br />
verschoben werden, seine Größe verändert werden oder durch die Entf-Taste gelöscht werden. Den<br />
Eintrag ZU ERLEDIGEN: DIALOGFELD-STEUERELEMENTE HIER PLATZIEREN können Sie z. B. auf<br />
diese Weise löschen.<br />
Drückt man die ENTER-Taste, erhält man ein Eigenschaftsfenster, das ebenfalls über das Kontextmenü<br />
(rechte Maustaste) zur Verfügung steht. Hier können weitere Eigenschaften verändert werden (z.B. die<br />
Minimieren- und Maximieren-Schaltflächen als Eigenschaften des Dialogfensters). Mit einem<br />
weiteren ENTER wird das Eigenschaftsfenster wieder geschlossen.<br />
12
<strong>Programmieren</strong> mit der MFC<br />
Beschreibung class = Typ ID<br />
statische Texte CStatic – IDC_STATIC, keine<br />
Änderung nötig –<br />
Rahmen „Ergebnisse“ CStatic – IDC_STATIC, keine<br />
Änderung nötig –<br />
Eingabefeld Breite CEdit IDC_BREITE<br />
Eingabefeld Länge CEdit IDC_LAENGE<br />
Ausgabefeld Fläche CEdit IDC_FLAECHE<br />
Schaltfläche „Berechnung...“ CButton IDC_BERECHNEN<br />
Schaltfläche OK CButton IDOK<br />
Setzen Sie außerdem das Ausgabefeld Fläche auf schreibgeschützt.<br />
!<br />
Experimentieren Sie mit den Funktionen, die über das Menü Layout zur Verfügung stehen!<br />
13
<strong>Programmieren</strong> mit der MFC<br />
2.4 Variablen hinzufügen<br />
Als nächstes müssen wir dafür sorgen, dass das, was der Benutzer in die Eingabefelder eingibt, auch<br />
beim Programm ankommt. Hierzu sind zwei Schritte erforderlich:<br />
Definition von zwei Variablen, die mit den Eingabefeldern verbunden sind. Eingegeben werden<br />
beliebige Zeichen, daher müssen die Variablen vom Typ „Zeichenkette“ sein. In der MFC gibt es<br />
dafür einen speziellen Typ CString.<br />
Ausgewertet werden sollen dagegen Zahlen. Also benötigen wir zwei weitere Variablen, die die<br />
umgewandelten Zahlenwerte aufnehmen sollen. Diese Variablen sind nicht direkt mit den<br />
Eingabefeldern verknüpft.<br />
Eingabefeld ID Edit-Variable Ziel-Variable<br />
Breite IDC_BREITE m_strBreite m_dBreite<br />
Länge IDC_LAENGE m_strLaenge m_dLaenge<br />
Die erste Art von Variablen lässt sich am schnellsten mit dem Klassenassistenten definieren. Sie<br />
finden ihn unter ANSICHT | KLASSEN-ASSISTENT bzw. mit dem Tastaturkürzel STRG+W.<br />
Wählen Sie die Registerkarte MEMBER-VARIABLEN. Tragen Sie anschließend für Breite<br />
(IDC_BREITE) bzw. Länge (IDC_LAENGE) jeweils eine passende Variable ein:<br />
14
<strong>Programmieren</strong> mit der MFC<br />
Beachten Sie die Schreibweise der Variablen. Da es sich um Objektvariablen handelt, beginnt der<br />
Name jeweils mit m_ (Objektvariablen werden in C++ auch als Member bezeichnet), gefolgt von einer<br />
Kurzbezeichnung des Datentyps (ungarische Notation). Dann folgt erst der eigentliche Name, str steht<br />
dabei für CString, d für double (später verwendet).<br />
Danach können Sie den Klassen-Assistenten wieder schließen.<br />
Für die zweite Gruppe von Variablen ist eine andere Vorgehensweise erforderlich. Diese können im<br />
linken Klassenansichtsfenster hinzugefügt werden. Expandieren sie zunächst die Klasse<br />
CWINFLAECHENBERECHNUNGDLG. Sie sehen dort die im vorherigen Schritt hinzugefügten Variablen.<br />
Klicken Sie nun mit der rechten Maustaste auf die Klassenbezeichnung<br />
CWINFLAECHENBERECHNUNGDLG. Aus dem Pop-up-Menü wählen Sie MEMBER-VARIABLE<br />
HINZUFÜGEN..., was Ihnen ja von der Implementierung von Objektklassen her bekannt ist.<br />
Geben Sie die Namen für die neuen Variablen ein und ihren Datentyp double. Belassen Sie den<br />
Zugriffsstatus auf public.<br />
Bei der Namensgebung empfiehlt es sich, sich an folgende Konvention zu halten: Der Name beginnt<br />
mit dem Präfix m_, gefolgt vom Buchstaben d für double, gefolgt vom eigentlichen Namen:<br />
15
<strong>Programmieren</strong> mit der MFC<br />
2.5 Ereignissteuerung<br />
Um das Zusammenspiel der bisher definierten Elemente zu verstehen, müssen wir einen Blick auf die<br />
Arbeitsweise von Windows werfen:<br />
? Jedes Windows-Programm ist eine Ansammlung von Fenstern und Steuerelementen, wobei selbst<br />
Steuerelemente wie „kleine Fenster“ betrachtet werden können 2 .<br />
? Windows übernimmt komplett die Kommunikation mit der Tastatur und der Maus.<br />
? Auf der Tastatur getippte Zeichen beziehen sich stets auf ein Fenster (oder Steuerelement), man<br />
sagt dieses Fenster hat den Fokus. Sie erkennen den Fokus in einem Eingabefeld am senkrechten<br />
Strich (den Texteingabecursor), bei einer Schaltfläche an der doppelten Umrandung.<br />
? Man kann den Fokus mit Hilfe der Maus oder der Tabulator-Taste auf ein anderes Element<br />
wechseln.<br />
? Jede Aktion des Benutzers wie ein Tastendruck, Ziehen der Maus, Mausklick, etc. löst in Windows<br />
ein Ereignis aus 3 . Windows notiert dabei<br />
o wo das Ereignis ausgelöst wurde (z.B. über der Schaltfläche mit der ID<br />
IDC_BERECHNEN)<br />
o was passiert ist (z.B. linke Maustaste wurde gedrückt: BN_CLICKED)<br />
? Anschließend schaut Windows in einer Tabelle (MESSAGE_MAP) nach, zu welchem Ereignis<br />
(wo, was) welches Unterprogramm aufgerufen werden soll 4 .<br />
Ein Teil dieser Ereignisse und der zugehörigen Unterprogramme ist bereits durch den<br />
Anwendungsassistenten definiert. Man kann Sie sich im Klassenassistenten unter der Registerkarte<br />
Nachrichtenzuordnungstabelle ansehen.<br />
2 vgl. Skript: Windows-Programmierung mit dem Windows-API.<br />
3 Ereignisse (Events) sind also Tastatur-Eingaben, Mausbewegungen, Betätigung einer Maustaste, Zeitgeber-<br />
Ereignisse; vgl. Skript: Windows-Programmierung mit dem Windows-API.<br />
4 In den Beispielen mit Windows-API haben wir diese Zuordnung selbst in der Form von Nachrichtenschleifen<br />
kennengelernt.<br />
16
<strong>Programmieren</strong> mit der MFC<br />
Diese Registerkarte ist dreiteilig aufgebaut. Links oben werden die IDs der Steuerelemente angezeigt<br />
(das „Wo“), rechts daneben die Liste der möglichen Nachrichten (das „Was“) und im unteren Bereich<br />
die zugeordneten Methoden.<br />
Über den Klassenassistenten können auch weitere Methoden zugeordnet werden. Dies soll an einem<br />
Beispiel gezeigt werden, in dem zunächst einmal das „merkwürdige“ Verhalten unseres Dialogfensters<br />
korrigiert werden soll, auf die Eingabe von Enter mit dem Ende des Programms zu reagieren, auch<br />
wenn der Fokus z.B. auf einem Eingabefeld liegt. Der Grund dafür ist, dass ein Drücken der Enter-<br />
Taste das Ereignis ONOK auslöst, das eigentlich zu unserem OK-Knopf gehört. Der OK-Knopf<br />
beendet jedoch das Programm. Wenn wir dies verhindern wollen, müssen wir eine Methode dazu<br />
schreiben, die nichts tut.<br />
Rufen Sie dazu den Klassen-Assistenten auf, gehen Sie das was (BN_CLICKED) und wo (IDOK) an<br />
und wählen Sie nacheinander FUNKTION HINZUFÜGEN und CODE BEARBEITEN. Den Namensvorschlag<br />
(OnOK) können Sie übernehmen. Nach Auswahl von Code bearbeiten springt Visual C++ in den<br />
Code-Editor zur ausgewählten Methode. Wie Sie sehen, enthält diese Methode nur eine einzige Zeile<br />
ausführbaren Code. Aus der Oberklasse unseres Dialogfensters (CDialog) wird die Methode OnOK()<br />
aufgerufen, die für die Beendigung des Programms sorgt. Diese Zeile kommentieren Sie einfach aus!<br />
17
<strong>Programmieren</strong> mit der MFC<br />
Wichtig: Danach bearbeiten Sie noch einmal den OK-Button in unserem Dialogfenster. Setzen Sie<br />
18
<strong>Programmieren</strong> mit der MFC<br />
seine ID auf IDC_BEENDEN und seine Beschriftung auf „Beenden“.<br />
Auf die gleiche Weise fügen Sie mit dem Klassen-Assistenten Methoden für folgende Ereignisse ein,<br />
deren Code im Anschluss eingearbeitet wird:<br />
Objekt-ID Nachricht Name der Methode<br />
IDC_BREITE EN_KILLFOCUS OnKillfocusBreite<br />
IDC_LAENGE EN_KILLFOCUS OnkillfocusLaenge<br />
IDC_BERECHNEN BN_CLICKED OnBerechnen<br />
2.6 Methoden mit Anweisungen füllen<br />
Anschließend müssen die Methoden noch mit Leben, sprich mit Anweisungen, gefüllt werden. Die<br />
einzelnen Methoden können entweder vom Klassen-Assistenten aus oder von der Klassenansicht aus<br />
in den Text-Editor geholt werden.<br />
Die Methode CWinFlaechenBerechnungDlg::OnKillfocusBreite() wird aufgerufen,<br />
wenn das zugehörige Eingabefeld den Fokus verliert, die Eingabe in diesem Steuerelement also<br />
beendet ist.<br />
void CWinFlaechenBerechnungDlg::OnKillfocusBreite()<br />
{<br />
// TODO: Code für die Behandlungsroutine der Steuerelement-<br />
Benachrichtigung hier einfügen<br />
}<br />
Leider sind eingegebene Daten nicht automatisch in die zugehörige Variablen gewandert, sondern<br />
stehen noch unter Kontrolle von Windows. Sie müssen erst durch spezielle Funktionen ausgelesen<br />
werden:<br />
void CWinFlaechenBerechnungDlg::OnKillfocusBreite()<br />
{ GetDlgItemText( IDC_BREITE, m_strBreite); // Wert aus Control<br />
holen<br />
m_dBreite = atof(m_strBreite); // in double-Wert konvertieren<br />
}<br />
m_dBreite = ((long int) (10.0 * m_dBreite)) / 10.0;<br />
m_strBreite.Format("%2.1f", m_dBreite);<br />
SetDlgItemText( IDC_BREITE, m_strBreite);<br />
Die Methode CWindow::GetDlgItemText liest den eingegebenen Text aus dem Eingabefeld aus.<br />
Dieser wird in die Zeichenkettenvariable m_strBreite gespeichert, und dann mittels atol() als<br />
double-Wert in unsere Variable m_dBreite übertragen. Danach wird der Wert auf eine<br />
Nachkommastelle gerundet, zurück in die Zeichenkettenvariable übertragen und in das Steuerelement<br />
gesetzt.<br />
19
<strong>Programmieren</strong> mit der MFC<br />
Implementieren Sie nun die entsprechende Methode für das Längeneingabefeld.<br />
In die Methode CWinFlaechenBerechnungDlg::OnBerechnen() fügen wir den eigentlichen<br />
Berechnungscode ein:<br />
void CWinFlaechenBerechnungDlg::OnBerechnen()<br />
{<br />
double flaeche = m_dBreite * m_dLaenge;<br />
CString ergebnis;<br />
}<br />
ergebnis.Format( "%4.2f", flaeche);<br />
SetDlgItemText( IDC_FLAECHE, ergebnis);<br />
Jetzt können Sie das Programm übersetzen und ausführen. Bis auf einige „Macken“ sollte das<br />
Programm seine Aufgabe erfüllen.<br />
2.7 Verbesserungen des ersten Beispielprogramms<br />
Das erste Beispielprogramm funktioniert zwar, weist aber noch einige Mängel auf:<br />
? Nach einer Berechnung lassen sich die Eingabefelder ändern, wodurch ein völlig falscher<br />
Zusammenhang entstehen kann<br />
? Obwohl am Anfang noch keine Daten eingegeben sind, lässt sich der Berechnungsknopf anklicken<br />
? ...<br />
2.7.1 Standardaktion für ENTER setzen<br />
Das Programm reagiert auf zwei Aktionen in gleicher Weise:<br />
• Das Drücken der ENTER-Taste löst das Signal OK aus<br />
• Das Anklicken des Beenden-Knopfes löst ebenfalls das Signal OK aus<br />
Dies liegt daran, dass als Eigenschaft der Schaltfläche Beenden, auf der Registerkarte Formate, diese<br />
als Standardschaltfläche definiert ist. Beseitigen Sie diese Einstellung!<br />
2.7.2 Reihenfolge der Felder festlegen<br />
Mit der Tabulator-Taste kann man die einzelnen Elemente des Dialogs durchlaufen. Die Reihenfolge<br />
der einzelnen Elemente kann man im Menüpunkt LAYOUT -> TABULATORREIHENFOLGE festlegen.<br />
Man ruft den Menüpunkt auf und klickt die einzelnen Elemente mit der Maus an.<br />
20
<strong>Programmieren</strong> mit der MFC<br />
Man beendet diese Arbeit mit der ENTER- oder der ESC-Taste.<br />
2.7.3 Der Fokus und zugehörige Ereignisse<br />
Wie erwähnt, besitzt stets genau ein Steuerelement den Fokus, d.h. es ist virtuell mit der Tastatur<br />
verbunden. Ist es ein Eingabefelder, so enthält es den Cursor und alle Tastendrücke gehen als Zeichen<br />
in dieses Feld. Ist es eine Schaltfläche, so ist dieser durch eine doppelte Linie gekennzeichnet und<br />
reagiert eigentlich nur auf die ENTER-Taste.<br />
In beiden Fällen sagt man, das betreffende Element habe den Fokus. Windows generiert nun zwei<br />
Ereignisse in Bezug auf den Fokus eines jeden Elements:<br />
? Das Ereignis EN_SETFOCUS wird erzeugt, wenn ein Element den Fokus erhält,<br />
? Das Ereignis EN_KILLFOCUS wird erzeugt, wenn ein Element den Fokus abgibt.<br />
Will man, dass das Programm auf diese Ereignisse reagiert, so muss man für jedes Steuerelement und<br />
jedes Ereignis mit Hilfe des Klassenassistenten eine Methode erzeugen. Dabei schlägt der Klassen-<br />
Assistent passende Namen, die man i.a. akzeptiert.<br />
Beispiel:<br />
Element Identifier Methode für<br />
EN_SETFOCUS<br />
Methode für<br />
EN_KILLFOCUS<br />
Breite IDC_BREITE OnSetfocusBreite OnKillfocusBreite<br />
Länge IDC_LAENGE OnSetfocusLaenge OnKillfocusLaenge<br />
Hier haben wir eine Möglichkeit, das Problem der nachträglich geänderten Eingabewerte zu lösen:<br />
Jedes Mal, wenn Länge oder Breite den Fokus erhält, wird das Rechenergebnis gelöscht:<br />
void CWinFlaechenBerechnungDlg::OnSetfocusBreite()<br />
{<br />
21
<strong>Programmieren</strong> mit der MFC<br />
// TODO: Code für die Behandlungsroutine der Steuerelement-<br />
Benachrichtigung hier einfügen<br />
SetDlgItemText(IDC_FLAECHE, "");<br />
}<br />
2.7.4 Eingabeprüfung<br />
Fehlerhafte Eingaben sollten möglichst auf die Eingaberoutine beschränkt bleiben. Um fehlerhafte<br />
Eingaben abzufangen, verwendet man in der prozeduralen Programmierung eine annehmende<br />
Schleife, die solange durchlaufen wird, bis die Eingabe akzeptiert wird.<br />
Hier wird ähnlich vorgegangen. Die Überprüfung der Eingabe erfolgt in der Killfocus-Methode. Ist sie<br />
nicht korrekt, so setzen wir den Fokus zurück auf dasselbe Steuerelement. Dazu modifizieren wir die<br />
Methode CWinFlaechenBerechnungDlg::OnKillfocusBreite()<br />
void CWinFlaechenBerechnungDlg::OnKillfocusBreite()<br />
{<br />
GetDlgItemText(IDC_BREITE, m_strBreite);<br />
m_dBreite = atof(m_strBreite);<br />
m_dBreite = ((long int) (10.0 * m_dBreite)) /10.0;<br />
if (m_dBreite SetFocus();<br />
}<br />
else<br />
{ m_strBreite.Format("%2.1f", m_dBreite);<br />
SetDlgItemText(IDC_BREITE, m_strBreite);<br />
}<br />
}<br />
Die Methode Setfocus() ist eine Memberfunktion der Klasse CWnd. Alle Steuerelemente der MFC<br />
haben diese Klasse als Basisklasse, d.h. alle Steuerelemente erben die Methode Setfocus() und können<br />
sie jederzeit benutzen. Um sie aufrufen zu können, brauchen wir jedoch einen Zeiger auf das<br />
Steuerelement, welchem wir den Fokus geben möchten. Wir definieren dafür einen Zeiger pF vom<br />
Typ Cwnd *, den Wert für den Zeiger liefert uns der Aufruf der Funktion GetDlgItem.<br />
Der dann folgende Aufruf von Setfocus() bewirkt, dass der Fokus auf das Steuerelement mit dem<br />
Identifier IDC_BREITE zurückgesetzt wird.<br />
Passen Sie nun in die Methode CWinFlaechenBerechnungDlg::OnKillfocusHoehe()<br />
entsprechend an!<br />
Wenn wir diese Methode auf beide Eingabefenster anwenden wollen, erhalten wir jedoch ein recht<br />
verwirrendes Ergebnis, bitte probieren Sie es aus! Für ein wirklich benutzerfreundliches Programm<br />
müssen wir uns daher etwas anderes einfallen lassen.<br />
?<br />
Wie können Sie es sicherstellen, dass die Benutzereingabe sinnvoll funktioniert?<br />
22
<strong>Programmieren</strong> mit der MFC<br />
2.7.5 Fehlermeldungen ausgeben<br />
Bei der obigen Eingabeüberprüfung haben wir auch kennen gelernt, wie man eine einfache<br />
Fehlermeldung ausgeben kann. Die Funktion MessageBox() hat wahlweise 1, 2 oder 3 Parameter.<br />
Der erste Parameter gibt den auszugebenden Text an, der zweite den Titel des Fensters. Der dritte<br />
Parameter bewirkt, dass in der Messagebox noch ein Icon erscheint. Zugelassene Parameter sind<br />
hierfür:<br />
• MB_ICONHAND<br />
• MB_ICONQUESTION<br />
• MB_ICONEXCLAMATION<br />
• MB_ICONINFORMATION<br />
Probieren Sie die verschiedenen Optionen aus!<br />
3 Die Ausführung eines Dialogprogramms<br />
Vom klassischen C her, bzw. von den bisher verwendeten Konsolenprogramm her, wissen wir, dass<br />
die Ausführung eines Programms immer mit der main-Funktion beginnt. Bei Windows-Programmen<br />
ist es etwas komplizierter.<br />
Jedes Programm, das Sie mit Hilfe der MFC erstellt haben, enthält ein einziges Anwendungsobjekt,<br />
dass von der Klasse CWinApp abgeleitet ist.<br />
Abbildung 7: Ableitungshierarchie unserer Anwendungsklasse .<br />
Dieses Objekt muss global deklariert sein und darf in einem Programm nur ein einziges Mal<br />
vorkommen. Die Header-Datei unseres Projektes enthält die Klassendefinition für unser<br />
Anwendungsobjekt, die als Member-Funktion aber nur den Konstruktor enthält.<br />
// CWinFlaechenBerechnungApp:<br />
// Siehe WinFlaechenBerechnung.cpp für die Implementierung dieser<br />
Klasse<br />
//<br />
23
<strong>Programmieren</strong> mit der MFC<br />
class CWinFlaechenBerechnungApp : public CWinApp<br />
{<br />
public:<br />
CWinFlaechenBerechnungApp();<br />
// Überladungen<br />
// Vom Klassenassistenten generierte Überladungen virtueller<br />
Funktionen<br />
//{{AFX_VIRTUAL(CWinFlaechenBerechnungApp)<br />
public:<br />
virtual BOOL InitInstance();<br />
//}}AFX_VIRTUAL<br />
// Implementierung<br />
//{{AFX_MSG(CWinFlaechenBerechnungApp)<br />
// HINWEIS - An dieser Stelle werden Member-Funktionen<br />
vom Klassen-Assistenten eingefügt und entfernt.<br />
// Innerhalb dieser generierten Quelltextabschnitte<br />
NICHTS VERÄNDERN!<br />
//}}AFX_MSG<br />
DECLARE_MESSAGE_MAP()<br />
};<br />
In der dazu gehörigen Implementierungsdatei wird das globale Anwendungsobjekt deklariert.<br />
///////////////////////////////////////////////////////////////////<br />
// Das einzige CWinFlaechenBerechnungApp-<br />
ObjektCWinFlaechenBerechnungApp theApp;<br />
Das von CWinApp abgeleitete,a mit globalem Gültigkeitsbereich angelegte Objekt kümmert sich um<br />
die Initialisierung der Anwendung und um die Hauptereignisschleife des Programms. Die Klasse<br />
enthält eine Reihe von Eigenschaften und Methoden, die meisten sind jedoch eher uninteressant für<br />
den normalen Programmierer. Hier in unserem Beispiel wurde nur die Methode InitInstance()<br />
überschrieben.<br />
Warum wird dieses Objekt global angelegt?<br />
Jedes MFC Programm muss eine von CWinApp abgeleitete Klasse deklarieren und ein<br />
entsprechendes globales Objekt definieren. Durch diese globale Definition des Objektes wird dessen<br />
Konstruktor (und der der übergeordneten Klasse CWinApp) noch ausgeführt bevor die WinMain()<br />
Funktion im MFC-Rahmen aufgerufen wird. Dadurch erhält das Objekt die Möglichkeit seine Daten<br />
vor dem Eintritt in die WinMain() Funktion zu initialisieren. Die WinMain() Funktion im MFC-<br />
Rahmen (eigentlich heißt die Funktion AfxWinMain(...)) ruft nach der Initialisierungsphase<br />
irgendwann die Methode InitInstance(...) des Anwendungsobjektes auf. Und innerhalb<br />
dieser Methode wird dann das Hauptfenster der Anwendung erstellt 5 .<br />
Die Aufgabe des Anwendungsobjektes ist es, die Anwendung zu initialisieren und zu kontrollieren. Da<br />
Windows es erlaubt, dass mehrere Instanzen der selben Anwendung laufen, ist die Initialisierung in<br />
5 Quelle: http://www.cpp-tutor.de/mfc/mfc/kap3/lektion2.htm<br />
24
<strong>Programmieren</strong> mit der MFC<br />
der MFC in zwei Methoden aufgeteilt - InitApplication() und InitInstance(). Hier bei<br />
unserem einfachen Beispiel wird nur die Methode InitInstance() verwendet. Sie wird jedes Mal<br />
aufgerufen, wenn eine neue Instanz der Anwendung erzeugt wird.<br />
In dieser Methode laufen verschiedene Dinge ab.<br />
Es wird ein Objekt unseres (Haupt-) Dialogs erstellt.<br />
CWinFlaechenBerechnungDlg dlg;<br />
Dieses Objekt wird der Objektvariablen – einem Zeiger – zugewiesen:<br />
m_pMainWnd = &dlg;<br />
Diese Membervariable, die von der Klasse CWinThread geerbt wurde, enthält das Hauptfenster<br />
unserer Applikation. Sie ist von Typ „Zeiger auf CWnd“, wobei CWnd wiederum von CCmdTarget<br />
abgeleitet ist..<br />
Die Klasse CWnd enthält die Grundeigenschaften aller Fenster. Von CWnd ist unter anderem die<br />
Klasse CFrameWnd abgeleitet. CFrameWnd – hier nicht verwendet! - erweitert CWnd um<br />
Eigenschaften wie z.B. die Menüleiste.<br />
CCmdTarget verleiht den von ihr abgeleiteten Klassen die Eigenschaft Nachrichten verarbeiten zu<br />
können.<br />
Das Dialogfenster wird angezeigt, indem für das dlg-Objekt die DoModal()-Funktion aufgerufen<br />
wird. Dieser Aufruf bewirkt bereits die gesamte Ausführung des Dialogs:<br />
int nResponse = dlg.DoModal();<br />
if (nResponse == IDOK)<br />
{<br />
// ZU ERLEDIGEN: Fügen Sie hier Code ein, um ein<br />
Schließen des<br />
// Dialogfelds über OK zu steuern<br />
}<br />
else if (nResponse == IDCANCEL)<br />
{<br />
// ZU ERLEDIGEN: Fügen Sie hier Code ein, um ein<br />
Schließen des<br />
// Dialogfelds über "Abbrechen" zu steuern<br />
}<br />
Nach Beendigung des Dialogs liefert der ganzzahlige Rückgabewert Auskunft darüber, ob der Dialog<br />
mit OK oder mit Cancel verlassen wurde (IDOK bzw. IDCANCEL).<br />
Dabei ist zu beachten, dass das OK-Ereignis normalerweise auch vom Drücken der ENTER-Taste<br />
ausgelöst wird (von uns abgeklemmt), das Cancel-Ereignis dagegen auch vom Drücken der ESC-Taste<br />
und des Cancel-Kreuzes rechts oben im Fenster.<br />
25
<strong>Programmieren</strong> mit der MFC<br />
4 Eine SDI-Anwendung<br />
4.1 Dokumente und Ansichten<br />
Windows-Anwendungen sind vielfach durch ein Zusammenspiel von Doc- und View-Klassen geprägt.<br />
Während der Begriff Ansichten (View) einen Hinweis auf die bildhafte Darstellung von Daten gibt, ist<br />
die Bezeichnung Dokumente (Doc) vielleicht nicht selbsterklärend. "Doc" enthält die Daten (Zahlen,<br />
Texte, ...), die "View" bildlich darstellt. "Doc" stellt auch einen Mechanismus zur Verfügung, der für<br />
das Schreiben und Lesen der Daten in Dateien sorgt – die sogenannte Serialisierung.<br />
Dafür bietet "View" die Möglichkeit, die Ansicht der Daten nicht nur auf den Bildschirm, sondern<br />
auch z.B. auf einen Drucker auszugeben. "View" stellt darüber hinaus den Kontakt mit den<br />
Benutzereingaben her (Maus-, Tastatureingaben, ...).<br />
Für den Rahmen um "Doc" und "View" sorgt eine Rahmenfensterklasse, z.B. CFrameWnd.<br />
"Doc" beruht auf der Klasse CDocument und "View" auf der von CWnd abgeleiteten Klasse CView<br />
(vergleichen Sie dazu nochmals Abbildung 1). Der Wirkmechanismus der Dokumentvorlagen ist in<br />
der abstrakten MFC-Klasse CDocTemplate enthalten. Diese Klasse wird nicht direkt benutzt,<br />
sondern die davon abgeleiteten Klassen CSingleDocTemplate für SDI und<br />
CMultiDocTemplate für MDI.<br />
Zusätzlich benötigt man eine von CWinApp abgeleitete Anwendungsklasse.<br />
Durch diese Architektur wird die Trennung von Dokument und Ansicht erreicht.<br />
26
<strong>Programmieren</strong> mit der MFC<br />
Nachfolgend ist die Klassenhierarchie (von links nach rechts) für eine SDI-Anwendung dargestellt:<br />
4.2 Beispielprogramm<br />
Das folgende Beispiel entstammt weitgehend Gurewich (1997). Wir legen dazu ein neues Projekt<br />
namens WinCircle an. Wie im vorausgegangenen Beispiel ist es wieder ein Projekt des Typs MFC-<br />
ANWENDUNGSASSISTENT (EXE). Allerdings wählen wir nun Einzelnes Dokument (SDI) mit<br />
UNTERSTÜTZUNG DER DOKUMENT-/ANSICHT-ARCHITEKTUR.<br />
Wir verzichten auf Datenbankunterstützung...<br />
27
<strong>Programmieren</strong> mit der MFC<br />
und auf die Unterstützung für Verbundinstrumente<br />
28
<strong>Programmieren</strong> mit der MFC<br />
29
<strong>Programmieren</strong> mit der MFC<br />
In Schritt 6 zeigt der Assistent dann die zu erstellenden Klassen: Sie erkennen die vier Grundlagen,<br />
auf denen die Anwendung beruht:<br />
? Ansicht (View),<br />
? Daten (Doc),<br />
? Fenster (MainFrame) und<br />
? Anwendung (App).<br />
Es ist wichtig, sich diese Unterteilung gut einzuprägen, damit Sie beim <strong>Programmieren</strong> die Übersicht<br />
behalten!<br />
30
<strong>Programmieren</strong> mit der MFC<br />
Die Anwendung ist erzeugt und bereits lauffähig. Der Assistent hat seine Arbeit geleistet. Inspizieren<br />
Sie das Ergebnis im Arbeitsbereichsfenster!<br />
31
<strong>Programmieren</strong> mit der MFC<br />
Sie haben aber vielleicht mehr oder etwas anderes als Sie wirklich brauchen oder wollen. Führen Sie<br />
das Programm aus! Welche Funktionalitäten sind bereits darin enthalten?<br />
32
<strong>Programmieren</strong> mit der MFC<br />
4.3 Der Source im Detail<br />
Sie können gedanklich das Rahmenfenster von oben nach unten in fünf Zonen einteilen:<br />
1. Systemmenü (auch Fenstermenü genannt)<br />
2. Menü<br />
3. Symbolleiste (Tool Bar)<br />
4. Client-Bereich<br />
5. Statusleiste (Status Bar)<br />
Suchen Sie nun dazu den Bezug im Sourcecode!<br />
Beginnen wir mit der Klassendefinition unserer von CFrameWnd 6 abgeleiteten Klasse CMainFrame.<br />
Dort entdecken wir unsere Statusleiste und Symbolleiste in folgenden Member-Variablen: 7<br />
protected: // Eingebundene Elemente der Steuerleiste<br />
6 CFrameWnd erweitert CWnd um Eigenschaften wie z.B. die Menüleiste, die wie ja beim Anlegen des<br />
Projektes angefordert hatten..<br />
7 Quelle: http://www.henkessoft.de/mfc_einsteigerbuch_kapitel7.htm<br />
33
<strong>Programmieren</strong> mit der MFC<br />
CStatusBar m_wndStatusBar;<br />
CToolBar m_wndToolBar;<br />
Darüber hinaus gibt es dort auch folgende Struktur für unser Rahmenfenster:<br />
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);<br />
In den nachfolgenden Abschnitten sehen wir, wie Änderungen<br />
4.3.1 Änderung der Toolbar<br />
Das ist leicht, da müssen wir einfach die drei Zeilen, wie angegeben, durch einen Kommentar (im C-<br />
Stil) streichen. Kommentieren Sie auf jeden Fall aus, also nicht wirklich wegstreichen, es sei denn Sie<br />
kennen die drei Zeilen auswendig.<br />
/* m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);<br />
EnableDocking(CBRS_ALIGN_ANY);<br />
DockControlBar(&m_wndToolBar);*/<br />
4.3.2 Entfernen der Toolbar<br />
Auch die Erzeugung der Toolbar wird auskommentiert.<br />
4.3.3 Keine Statusleiste<br />
Wir wollen keine Statusleiste, jedoch eine Symbolleiste: Dazu "streichen" wir nur den nachstehenden<br />
Block. Die Teile für die Symbolleiste belassen wir aktiv.<br />
/*<br />
if (!m_wndStatusBar.Create(this) ||<br />
!m_wndStatusBar.SetIndicators(indicators,<br />
sizeof(indicators)/sizeof(UINT)))<br />
{<br />
TRACE0("Statusleiste konnte nicht erstellt werden\n");<br />
}<br />
*/<br />
return -1;<br />
4.3.4 Kein Menü<br />
// Fehler bei Erstellung<br />
Um das Menü zu entfernen, ergänzen Sie die Methode<br />
CMainFrame::PreCreateWindow(...):<br />
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)<br />
{<br />
if ( cs.hMenu != NULL )<br />
{<br />
::DestroyMenu( cs.hMenu ); // Geladenes Menü entfernen<br />
cs.hMenu = NULL; // Rahmenfenster hat kein Menü<br />
34
<strong>Programmieren</strong> mit der MFC<br />
}<br />
if( !CFrameWnd::PreCreateWindow(cs) )<br />
return FALSE;<br />
// ZU ERLEDIGEN: Ändern Sie hier die Fensterklasse oder das<br />
Erscheinungsbild, indem Sie<br />
// CREATESTRUCT cs modifizieren.<br />
}<br />
return TRUE;<br />
4.3.5 Ändern der Fenstergröße<br />
Ergänzen Sie die Methode CMainFrame::PreCreateWindow(...):<br />
cs.cx = 300; // Breite<br />
cs.cy = 200; // Höhe<br />
4.3.6 Systemmenü modifizieren<br />
Fügen Sie in die Methode CMainFrame::OnCreate(...) den folgenden Code ein, um den Eintrag<br />
Schließen zu disablen:<br />
CMenu* pSystemMenu = GetSystemMenu(FALSE);<br />
pSystemMenu->EnableMenuItem( SC_CLOSE, MF_GRAYED );<br />
4.3.7 Ändern der Statusleiste<br />
Das Objekt "Statusleiste" ist eine Instanz der Klasse CStatusBar. In der Klasse CMainFrame wird<br />
dieses Objekt als Member-Variable definiert.<br />
protected: // Eingebundene Elemente der Steuerleiste<br />
CStatusBar m_wndStatusBar;<br />
In der Datei MainFrm.cpp finden wir das globale statische UINT-Array indicators, das die Inhalte der<br />
Statusleiste definiert:<br />
35
<strong>Programmieren</strong> mit der MFC<br />
// CMainFrame<br />
...<br />
static UINT indicators[] =<br />
{<br />
ID_SEPARATOR,<br />
ID_INDICATOR_CAPS,<br />
ID_INDICATOR_NUM,<br />
ID_INDICATOR_SCRL,<br />
};<br />
// Statusleistenanzeige<br />
Für japanische Tastaturen gibt es zusätzlich die Kana-Taste ( ID_INDICATOR_KANA,<br />
Anzeige:"KANA" ).<br />
Wir bauen dies zur Verdeutlichung in unsere Anwendung ein:<br />
static UINT indicators[] =<br />
{<br />
ID_SEPARATOR,<br />
ID_INDICATOR_CAPS,<br />
ID_INDICATOR_NUM,<br />
ID_INDICATOR_SCRL,<br />
ID_INDICATOR_KANA,<br />
};<br />
Somit haben wir ein weiteres Indikatorfeld!<br />
In der Funktion CMainFrame::OnCreate(...) wird die Statusleiste mittels CStatusBar::Create(...)<br />
erzeugt, und die Funktion CStatusBar::SetIndicators(...) ordnet das Array indicators der Statusleiste<br />
zu.<br />
if (!m_wndStatusBar.Create(this) ||<br />
!m_wndStatusBar.SetIndicators(indicators,<br />
sizeof(indicators)/sizeof(UINT)))<br />
{<br />
TRACE0("Statusleiste konnte nicht erstellt werden\n");<br />
return -1; // Fehler bei Erstellung<br />
}<br />
In der Statusbar steht links das Wort „Bereit“. Sie können ihn auf individuelle Bedürfnisse anpassen,<br />
in dem Sie diesen Text in der String Table unter der Bezeichnung AFX_IDS_IDLEMESSAGE<br />
modifizieren.<br />
4.4 Die Datenelemente der Dokumentklasse deklarieren<br />
Die Dokumentklasse ist für Ihre Daten zuständig, einschließlich ihrer Speicherung des des<br />
Wiedereinlesens.<br />
36
<strong>Programmieren</strong> mit der MFC<br />
class CWinCircleDoc : public CDocument<br />
{<br />
protected: // Nur aus Serialisierung erzeugen<br />
CWinCircleDoc();<br />
DECLARE_DYNCREATE(CWinCircleDoc)<br />
// Attribute<br />
public:<br />
// Operationen<br />
public:<br />
// Überladungen<br />
// Vom Klassenassistenten generierte Überladungen virtueller<br />
Funktionen<br />
//{{AFX_VIRTUAL(CWinCircleDoc)<br />
public:<br />
virtual BOOL OnNewDocument();<br />
virtual void Serialize(CArchive& ar);<br />
//}}AFX_VIRTUAL<br />
// Implementierung<br />
public:<br />
virtual ~CWinCircleDoc();<br />
#ifdef _DEBUG<br />
virtual void AssertValid() const;<br />
virtual void Dump(CDumpContext& dc) const;<br />
#endif<br />
protected:<br />
// Generierte Message-Map-Funktionen<br />
protected:<br />
//{{AFX_MSG(CWinCircleDoc)<br />
// HINWEIS - An dieser Stelle werden Member-Funktionen<br />
vom Klassen-Assistenten eingefügt und entfernt.<br />
// Innerhalb dieser generierten Quelltextabschnitte<br />
NICHTS VERÄNDERN!<br />
//}}AFX_MSG<br />
DECLARE_MESSAGE_MAP()<br />
};<br />
Erweitern Sie den public-Bereich um die zwei Member-Variablen, die zur Speicherung der<br />
Koordinaten eines Kreises benötigt werden.:<br />
int m_PosX;<br />
int m_PosY;<br />
4.5 Die Datenelemente der Viewklasse deklarieren<br />
Die Definition der Klasse CWinCircleDOC wird als nächstes um zwei Attribute erweitert:<br />
37
<strong>Programmieren</strong> mit der MFC<br />
int m_PosX;<br />
int m_PosY;<br />
Diese Datenelemente sind ein Spiegelbild der Datenelemente, die Sie der Dokumentklasse<br />
hinzufügten. Legt das Dokument fest, dass ein Kreis an einer bestimmten X- / Y-Position zu zeichnen<br />
ist, zeigt der Code der Viewklasse den Kreis an der spezifizierten Position an.<br />
4.6 Die Datenelemente der Dokumentklasse initialisieren<br />
Datenelemente sind immer entsprechend zu initialisieren. Öffnen Sie dazu die Datei<br />
WinCircleDoc.cpp und ergänzen Sie darin die Funktion CCircleDoc::OnNewDocument():<br />
BOOL CCircleDoc::OnNewDocument()<br />
{<br />
if (!CDocument::OnNewDocument())<br />
return FALSE;<br />
// ZU ERLEDIGEN: Hier Code zur Reinitialisierung einfügen<br />
// (SDI-Dokumente verwenden dieses Dokument)<br />
////////////////////////<br />
// EIGENER CODE, ANFANG<br />
////////////////////////<br />
// Die Datenelemente des Dokuments initialisieren<br />
m_PosX = 200;<br />
m_PosY = 100;<br />
////////////////////////<br />
// EIGENER CODE, ENDE<br />
////////////////////////<br />
}<br />
return TRUE;<br />
4.7 Die Datenelemente der Viewklasse initialisieren<br />
Wir werden diese Member-Variablen der Dokumentenklasse nun initialisieren. Öffnen Sie dazu den<br />
Klassenassistenten:<br />
38
<strong>Programmieren</strong> mit der MFC<br />
Fügen Sie für die Objektklasse CWinCircleView und die Objekt-ID CWinCircleView für die<br />
Nachricht OnInitialUpdate eine Funktion hinzu. Visual C++ fügt daraufhin die Funktion<br />
OnInitialUpdate() hinzu.<br />
Drücken Sie die Schaltfläche CODE BEARBEITEN. Erweitern Sie die Methode gemäß nachfolgendem<br />
Beispiel:<br />
// CWinCircleView Nachrichten-Handler<br />
void CWinCircleView::OnInitialUpdate()<br />
{<br />
CView::OnInitialUpdate();<br />
// TODO: Speziellen Code hier einfügen und/oder Basisklasse<br />
aufrufen<br />
////////////////////////<br />
// EIGENER CODE, ANFANG<br />
////////////////////////<br />
// Einen Zeiger auf das Dokument ermitteln<br />
CWinCircleDoc* pDoc = GetDocument();<br />
// Die Datenelemente der Viewklasse mit den<br />
// entsprechenden Dokument-Werten aktualisieren<br />
m_PosX = pDoc->m_PosX;<br />
m_PosY = pDoc->m_PosY;<br />
// EIGENER CODE, ENDE<br />
}<br />
39
<strong>Programmieren</strong> mit der MFC<br />
Wie der Name deutlich macht, ist diese Elementfunktion für die Initialisierung der Datenelemente in<br />
der Viewklasse zuständig. Wies Sie sehen, werden diese mit den aktuellen Werten der Datenelemente<br />
der Dokumentklasse über die pDoc-Zeigervariable initialisiert, dessen Wert zuvor über die<br />
GetDocument-Funktion gewonnen wurde.<br />
4.8 Code zur Anzeige des Kreises<br />
Nun werden wir den Code schreiben, der für die Bildschirmausgabe eines Kreises zuständig ist.<br />
Öffnen Sie die Datei WinCircleView.cpp und suchen Sie die Funktion OnDraw(). Ergänzen Sie den<br />
nachfolgenden Sourcecode:<br />
// CWinCircleView Zeichnen<br />
void CWinCircleView::OnDraw(CDC* pDC)<br />
{<br />
CWinCircleDoc* pDoc = GetDocument();<br />
ASSERT_VALID(pDoc);<br />
// ZU ERLEDIGEN: Hier Code zum Zeichnen der ursprünglichen<br />
Daten hinzufügen<br />
///////////////////////<br />
// EIGENER CODE, ANFANG<br />
///////////////////////<br />
// Den rechteckigen Umriß des zu zeichnenden<br />
// Kreises definieren<br />
RECT rect;<br />
rect.left = m_PosX -20;<br />
rect.top = m_PosY -20;<br />
rect.bottom = m_PosY + 20;<br />
rect.right = m_PosX +20;<br />
// Den Kreis zeichnen<br />
pDC->Ellipse(&rect);<br />
///////////////////////<br />
// EIGENER CODE, ENDE<br />
///////////////////////<br />
}<br />
Ein Kreis wird in Visual C++ als eine Ellipse mit gleichen Achsenlänge definiert. In unserem Fall<br />
wird die Größe der Ellipse durch ein einhüllendes Rechteck definiert. Das Ergebnis sehen wir in<br />
Abbildung 8.<br />
40
<strong>Programmieren</strong> mit der MFC<br />
Abbildung 8: Unser erster Kreis.<br />
4.9 Zeichnen an beliebigen Positionen<br />
Bis jetzt wird ein Kreis immer an der gleichen Stelle gezeichnet. Im Folgenden soll, verknüpft mit<br />
dem Drücken der Maustaste, das Zeichnen an beliebiger Position möglich werden.<br />
41
<strong>Programmieren</strong> mit der MFC<br />
Visual C++ fügt nun die Methode CWinCircleView::OnLButtonDown(UINT nFlags,<br />
CPoint point) hinzu. Klicken Sie auf die Schaltfläche CODE BEARBEITEN und nehmen Sie die<br />
entsprechende Ergänzung vor:<br />
void CWinCircleView::OnLButtonDown(UINT nFlags, CPoint point)<br />
{<br />
// TODO: Code für die Behandlungsroutine für Nachrichten hier<br />
einfügen und/oder Standard aufrufen<br />
////////////////////////<br />
// EIGENER CODE, ANFANG<br />
////////////////////////<br />
// Die Datenelemente m_PoxX und m_PosY der Viewklasse<br />
// mit den XY-Koordinaten des Punktes aktualisieren,<br />
// an dem mit der Maus geklickt wurde<br />
m_PosX = point.x;<br />
m_PosY = point.y;<br />
// Einen Aufruf der Funktion OnDraw() auslösen<br />
Invalidate();<br />
// Einen Zeiger auf das Dokument ermitteln<br />
CWinCircleDoc* pDoc = GetDocument();<br />
// Die Datenelemente des Dokuments mit den neuen<br />
42
<strong>Programmieren</strong> mit der MFC<br />
// Werten der Datenelemente aus der Viewklasse<br />
// aktualisieren<br />
pDoc->m_PosX = m_PosX;<br />
pDoc->m_PosY = m_PosY ;<br />
// Signalisieren, daß das Dokument geändert wurde<br />
pDoc->SetModifiedFlag(TRUE);<br />
////////////////////////<br />
// EIGENER CODE, ENDE<br />
////////////////////////<br />
}<br />
CView::OnLButtonDown(nFlags, point);<br />
4.10 Speichern und Laden von Dateien<br />
Öffnen Sie die Datei CWinCircleDoc.cpp und gehen Sie zur Methode<br />
CWinCircleDoc::Serialize(CArchive& ar). Diese Methode wird automatisch<br />
ausgeführt, wenn Sie in einem SDI-Programm die Funktionen SPEICHERN, SPEICHERN UNTER oder<br />
ÖFFNEN aus dem Menü DATEI aktivieren.<br />
Der Code dieser Funktion besteht im wesentlichen aus einer einfachen if ... else-Anweisung:<br />
void CWinCircleDoc::Serialize(CArchive& ar)<br />
{<br />
if (ar.IsStoring())<br />
{<br />
// ZU ERLEDIGEN: Hier Code zum Speichern einfügen<br />
}<br />
else<br />
{<br />
// ZU ERLEDIGEN: Hier Code zum Laden einfügen<br />
}<br />
}<br />
Der einzufügende Code ist für Sie vielleicht überraschend einfach:<br />
void CWinCircleDoc::Serialize(CArchive& ar)<br />
{<br />
if (ar.IsStoring())<br />
{<br />
// ZU ERLEDIGEN: Hier Code zum Speichern einfügen<br />
//////////////////////<br />
// EIGENER CODE, ANFANG<br />
//////////////////////<br />
43
<strong>Programmieren</strong> mit der MFC<br />
// m_PosX und m_PosY in der Datei speichern<br />
ar m_PosX;<br />
ar >> m_PosY;<br />
//////////////////////<br />
// EIGENER CODE, ENDE<br />
//////////////////////<br />
}<br />
}<br />
Das Ergebnis ist in der nachfolgenden Abbildung dargestellt.<br />
Beachten Sie:<br />
? Die Titelleiste der Applikation wird automatisch dem aktuellen Dateinamen angepasst.<br />
? Die zuletzt verwendeten Dateien werden im DATEI-Menü automatisch verwaltet.<br />
44
<strong>Programmieren</strong> mit der MFC<br />
In dieser Übung haben wir in sehr mächtiges und wichtiges Konzept der MFC-Bibliothek kennen<br />
gelernt: Die Serialisierung.<br />
Sie legt die von CObject abgeleiteten Objekte beständig ab, beispielsweise durch Speichern in einer<br />
Datei mit nachfolgendem Laden. Derartige Objekte können über die Serialisierung ebenfalls in die<br />
Zwischenablage oder über OLE an andere Applikationen übertragen werden (Toth 1997). Die MFC-<br />
Bibliothek verwendet CArchive-Objekte für die Serialisierung.<br />
5 Literatur<br />
Toth, Viktor, 1997: Visual C++ 5. Das Kompendium. Markt und Technik, Haar bei München, 1184 S.<br />
Gurewich, Ori, Gurewich, Nathan, 1997: Visual C++ in 21 Tagen. SAMS, Haar bei München, 864 S.<br />
Dr. Erhard Henkes (e.henkes@gmx.net) - C++ und MFC - Stand: 06.08.2002 :<br />
http://www.henkessoft.de/mfc_einsteigerbuch_kapitel7.htm<br />
Schröder, W., 2004: C++-Tutor, http://www.cpp-tutor.de/mfc/mfc/kap4/lektion1.htm, letzter Zugriff:<br />
19.11.2004<br />
45