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:

Die Ansichtsklasse

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:

Methode zum Auslesen der Dok-Daten

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.



Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470