Ansichtenklasse
Kommen wir jetzt zum wichtigsten Teil des Doc/View Modells, wenigstens aus der
Sicht des Anwenders. Nur über ein Ansichtsobjekt werden die Dokumentdaten letztendlich
dargestellt. Ebenfalls werden Änderungen von Daten in der Regel über ein Ansichtsobjekt
angestoßen, schließlich ist es dieses Objekt das der Anwender zu sehen bekommt.
Wurden Daten über ein Ansichtsobjekt geändert, so müssen die Änderungen anschließend
an das Dokument übergeben werden das dann entscheidet, ob die Änderungen übernommen
werden oder nicht. Durch die strikte Trennung der Daten von ihrer Darstellung ergibt
sich noch ein weiterer, wichtiger Gesichtspunkt: jedes Dokument kann zwar ein oder
mehrere Ansichten besitzen aber jede Ansicht besitzt nur genau ein Dokument. Stellen
Sie dazu sich einmal vor, Sie wollen in einer Anwendung Bitmap-Dateien darstellen.
Die Bitmap selbst gehört laut obiger Definition zum Dokument, da sie die darzustellenden
Daten enthält. Sie können jetzt aber ein und die selbe Bitmap in einem Ansichtsobjekt
grafisch darstellen und z.B. in einem weiteren Ansichtsobjekt deren Kenndaten wie
die Größe oder die Anzahl der enthaltenen Farben. Beide Ansichtsobjekt greifen aber
auf das gleiche Dokument zu.
Sehen wir uns aber zuerst in der Klassenansicht einmal an, welche Methoden der
Anwendungs-Assistent für das Ansichtsobjekt CDVBasisView generiert hat:

Auch hier finden Sie die beiden, bis jetzt noch nicht behandelten, Methoden
AssertValid(...) und Dump(...). Außerdem enthält die Ansichtsklasse
einen protected-Konstruktor, d.h. ein Objekt der Klasse CDVBasisView
kann, genauso wie das vorher besprochene Dokumentenobjekt, nicht direkt erstellt
werden. Auch das Ansichtsobjekt wird wieder durch die im Anschluss an diese Lektion
behandelte Dokumentenverwaltung erstellt.
Neu ist die Methode GetDocument(...). Mit Hilfe dieser Methode erhält
das Ansichtsobjekt Zugriff auf das Dokumentenobjekt der Anwendung. Ebenfalls bisher
noch nicht behandelt wurde die Methode OnDraw(...). OnDraw(...)
entspricht von der Aufgabe her der bisherigen CWnd-Methode OnPaint(...),
d.h. in dieser Methode werden die Daten des Dokuments ausgegeben. Als Parameter
erhält die Methode einen Zeiger pDC auf den aktuellen Device Context. Was
ein Device Context ist erfahren Sie im nächsten Kapitel. Einer der Unterschiede
zwischen der CWnd-Methode OnPaint(...) und der CView-Methode
OnDraw(...) besteht darin, dass für die Methode OnDraw(...) kein
Eintrag in die Nachrichtentabelle explizit vorgenommen werden muss. Außerdem wird
OnDraw(...), in Gegensatz zu OnPaint(...), auch für die Druckerausgabe
mit verwendet.
Die letzte Methode PreCreateWindow(...) kennen Sie ja schon. Mit ihrer
Hilfe können bestimmte Voreinstellungen des zum Ansichtsobjekt gehörenden WINDOWS
Fensters vorgenommen werden.
Aber kommen wir nun zur Praxis. Zuerst müssen wir unserem bisherigen Dokument
noch irgendwelche Daten hinzufügen um überhaupt etwas zu sehen. Normalerweise werden
die Daten eines Dokuments in der Serialize(...) Methode des Dokumentenobjekts
eingelesen. Da wir aber das Einlesen von Daten bisher noch nicht behandelt haben,
erzeugen wir die Daten im Programm selbst. Die Daten des Dokuments sollen aus einer
Anzahl von short-Werten bestehen, die dann im Ansichtsobjekt nachher ausgegeben
werden.
 |
Falls noch nicht geschehen, öffnen Sie jetzt das Beispiel
DVBasis aus der vorherigen Lektion wieder. Zuerst definieren wir
uns eine Struktur in der das Dokumentenobjekt die Daten ablegt. Wir könnten
die Daten auch direkt im Dokumentenobjekt ablegen, müssten dann aber an das
Ansichtsobjekt nachher anstelle eines Zeigers auf die neue Struktur zwei Werte
zurückliefern: die Anzahl der vorhandenen short-Werte und den Zeiger
auf die short-Werte selbst. Fügen Sie in die Datei DVBasisDoc.h
folgende Strukturdeklaration von Hand ein:
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Struktur fuer die Aufnahme der Dokumentdaten
struct strData
{
short nNoOfData;
// Anzahl der Daten
short
*pnData; // Die Daten
};
class CDVBasisDoc : public CDocument
{
....
}; |
Anschließend fügen Sie der Dokumentenklasse noch eine entsprechende private
Membervariable m_strData hinzu. Sie können die Definition direkt eingeben
oder den Klassen-Assistent hierfür verwenden.
class CDVBasisDoc : public
CDocument
{
....
private:
bool m_bLoaded;
strData m_strData;
}; |
Beachten Sie bitte, dass die Membervariable private definiert wurde.
Schließlich schreiben wir ein C++ Programm und nutzen auch dessen Datenkapselung
aus.
Als nächstes gilt es die Daten zu initialisieren. Dies führen wir im Konstruktor
des Dokumentenobjekts durch.
CDVBasisDoc::CDVBasisDoc()
{
// ZU ERLEDIGEN: Hier Code für One-Time-Konstruktion einfügen
TRACE("ctor DVBasisDoc\n");
m_bLoaded = false;
// Anzahl der Daten
festlegen
m_strData.nNoOfData = 10;
// und Speicher fuer die
Daten reservieren
m_strData.pnData = new short[m_strData.nNoOfData];
// Daten nun initialisieren
for (int iIndex=0; iIndex<m_strData.nNoOfData;
iIndex++)
m_strData.pnData[iIndex] = iIndex*2;
} |
Und eigentlich sollten bei der Eingabe des obigen Codes schon sämtliche Alarmglocken
läuten. Im Konstruktor wird mittels des new Operators Speicherbereich
reserviert den Sie auch irgend wann einmal wieder freigeben sollten. Erweitern
wir den Destruktor des Dokuments daher um den Aufruf des delete Operators:
CDVBasisDoc::~CDVBasisDoc()
{
TRACE("dtor DVBasisDoc\n");
delete [] m_strData.pnData;
} |
|
Noch ein Hinweis am Rande:
 |
Sollten Sie sich beim Anlegen einer Membervariable einmal verschrieben
haben, so korrigieren Sie einfach deren Definition. Die Klassenansicht wird
beim Eingeben von Member automatisch aktualisiert. Das gleiche gilt übrigens
auch für Methoden. Beachten Sie dabei bitte, dass Sie sowohl die Deklaration
als auch die Definition hier korrigieren müssen. Wollen Sie eine Methode entfernen,
so tun Sie dies am Besten mittels des Klassen-Assistent. Wenn Sie mit der rechten
Maustaste die Methode einer Klasse anklicken, erhalten Sie im Kontextmenü den
Eintrag Löschen. Der Klassen-Assistent löscht darauf hin die Deklaration
und kommentiert die Definition aus. Dadurch haben Sie dann immer noch die Möglichkeit,
den Code der Methode an anderer Stelle einzusetzen.
|
Doch eine Kleinigkeit fehlt noch in der Dokumentenklasse. Die Dokumentdaten selbst
sind, wie es unter C++ sein sollte, als private-Member der Dokumentenklasse
spezifiziert. Wenn nun das Ansichtsobjekt diese Daten aber ausgeben soll, so benötigt
es einen Zugriff darauf. Wir müssen also noch eine Methode implementieren die z.B.
einen Zeiger auf die Daten des Dokuments zurückliefert.
 |
Fügen wir jetzt der Dokumentenklasse eine Methode GetData(...)
hinzu die einen Zeiger auf die aktuellen Dokumentdaten zurückliefert:

Beachten Sie bitte, dass die Methode GetData(...) einen const-Zeiger
auf die Dokumentdaten zurückliefert, so dass die Daten außerhalb der Klasse
nicht verändert werden können. Außerdem ist die Methode selbst vom Typ 'const',
d.h. auch sie kann keine Daten der Klasse verändern. Sinn und Zweck der Methode
ist ja, nur einen Zeiger auf die Dokumentdaten zurückzuliefern. Selbstverständlich
muss diese Methode public sein damit sie auch von der Ansichtsklasse
aus aufgerufen werden kann.
Erweitern Sie dann die neu hinzugefügte Methode wie folgt:
const strData* CDVBasisDoc::GetData()
const
{
return &m_strData;
} |
|
Damit haben wir die 'Verarbeitung' der Dokumentdaten und deren Bereitstellung
abgeschlossen. Gehen wir jetzt an die Darstellung der Daten. Wie Sie bereits weiter
oben erfahren haben, werden die Daten in der Methode OnDraw(...) des Ansichtsobjektes
ausgegeben. Der Anwendungs-Assistent hat innerhalb dieser Methode bereits den Aufruf
der Methode GetDocument(...) eingefügt, so dass über diesen Zeiger die
Dokumentenmethode GetData(...) aufgerufen werden kann.
 |
Um die Daten in der OnDraw(...) Methode auszugeben,
holen wir uns zunächst den Zeiger auf das zum Ansichtsobjekt gehörende Dokument
und rufen dann dessen Methode GetData(...) auf. GetData(...)
liefert, wie oben bereits implementiert, einen const-Zeiger auf das Daten-Objekt
des Dokuments zurück.
void CDVBasisView::OnDraw(CDC*
pDC)
{
CDVBasisDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// ZU ERLEDIGEN: Hier Code zum Zeichnen der ursprünglichen Daten
hinzufügen
TRACE("Dok-Daten: ");
// const-Zeiger auf die
Dok-Daten holen
const strData *pData = pDoc->GetData();
// und Daten ausgeben
for (int iIndex=0; iIndex<pData->nNoOfData;
iIndex++)
TRACE("%d, ",pData->pnData[iIndex]);
TRACE("\n");
} |
Übersetzen und starten Sie nun das Beispiel.
|
Wenn Sie alles richtig eingegeben haben, sollten Sie folgende Ausgabe im Debuggerfenster
erhalten:
Beginn InitInstance()
ctor DVBasisDoc
DeleteContents()
OnNewDocument()
Dok-Daten: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18,
Ende InitInstance() |
Sie sehen, die Daten werden noch vor dem Verlassen der InitInstance(...)
Methode des Anwendungsobjektes zum erstenmal ausgegeben.
Was bisher sozusagen unterschlagen wurde ist die Tatsache, dass die Ansichtsklasse
auch das Makro DECLARE_DYNCREATE(...) enthält um ein Objekt dieser Klasse
dynamisch zu erstellen sowie eine Nachrichtentabelle.
Damit haben wir den Minimalumfang der Klasse CView behandelt. Wir wollen
uns aber noch zwei weitere, wichtige Methoden der CView Klasse ansehen.
Beginn wir mit der Beschreibung der Methode OnInitialUpdate(...). Diese
Methode wird vom aktuellen Dokument aufgerufen bevor das Ansichtsobjekt zum ersten
Mal die Dokumentdaten darstellen soll. In dieser Methode können Sie zum Beispiel
Werte berechnen die sich im Verlaufe der Darstellung eines Dokuments nicht oder
nur sehr selten verändern. Die Standard-Implementierung der Methode ruft die zweite
wichtige CView-Methode auf, die Methode OnUpdate(...). OnUpdate(...)
wird immer dann durch das Dokumentenobjekt aufgerufen wenn sich der Inhalt des Dokuments
verändert hat. Diese Methode wird aber nicht direkt durch das Dokument aufgerufen
sondern vielmehr als Reaktion auf den Aufruf der CDocument-Methode
UpdateAllViews(...).
Zum Schluss dieser Einführung in die CView Klasse soll noch erwähnt
werden, dass von der Klasse CView eine ganze Reihe weiterer, spezialisierter
Ansichtsklassen abgeleitet sind. Die nachfolgende Tabelle enthält nur die wichtigsten
Ableitungen.
|
CView Klasse
|
Eigenschaften
|
| CScrollView |
Erweitert die Klasse CView
um die Eigenschaft den Fensterinhalt über Scrollbars scrollen zu können. |
| CFormView |
Der Inhalt des Views wird
mit Hilfe einer Dialog-Ressource erstellt. Dieser View-Typ wird hauptsächlich
dann eingesetzt, wenn der Inhalt des Views aus Kontrollelementen wie Buttons,
Comboboxen usw. besteht. |
| CEditView |
Erweitert die Klasse CView
um die Eigenschaft eines Edit-Controls. Mit Hilfe des Edit-Controls kann auf
relativ einfache Weise eine kleiner Texteditor erstellt werden der sogar Cut&Paste
Funktionalität enthält. |
| CRichEditView |
Erweitert die Klasse CEditView
zusätzlich um Formatierungsmöglichkeiten. Außerdem können in einem CRichEditView
Objekte eingebettet werden (z.B. Bitmaps). |
| CListView |
Erweitert die Klasse CView
um die Eigenschaft Listen darzustellen wie z.B. im rechten Teil des Explorers. |
| CTreeView |
Erweitert die Klasse CView
um die Eigenschaft Trees darzustellen wie z.B. im linken Teil des Explorers. |
Einige der aufgeführten Klassen werden in den nachfolgenden Kapiteln noch eingesetzt.
In der nächsten Lektion geht's dann weiter mit der Dokumentenverwaltung.
|