14.04.2015 Aufrufe

Programmieren - GIS-Management

Programmieren - GIS-Management

Programmieren - GIS-Management

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!