Grundlagen
Grundlagen in Visual Studio in Visual Studio MFC, STL
MFC, STL
Dipl.-Inf., Dipl.-Ing. (FH) Michael Wilhelm
Hochschule Harz
FB Automatisierung und Informatik
mwilhelm@hs-harz.de
Raum 2.202
Inhalt
1. Einführung, Windows-Schleife, API, MFC 2. Grafik
3. Dialogfenster
4. MFC-Zusatzklassen 5. SDI-Programme
6. MDI-Programme 7. DLL
8. Standard Template Library
Single Dokument Interface
Übersicht:
Design Pattern: Modell / View / Controller
Dokument / View / Architektur
Erzeugte Klassen
Serialisierung
Beispiele
Software-Entwicklung
Assembler
Prozeduraler Ansatz (Daten und Methoden getrennt)
Modulkonzept (Daten und Methoden zusammen)
Objektorientierte Entwicklung
Strukturierte Analyse / Design
Objektorientierte Analyse / Design
Jeder Lösungsansatz wird immer wieder neu entwickelt
Entwurf objektorientierter Software ist nicht leicht
Was sind Objekte ?
Was sind Attribute / Felder ?
Definition der Schnittstellen (get/set)
Bestimmen der Methoden
Abhängigkeit zwischen Objekten
Verallgemeinerung
Wiederverwendbarkeit
Experte:
Experten wissen zu vermeiden, jedes Problem von Grund auf neu zu anzugehen.
Bibliotheken
Packages
Templates
OOP
Sie verwenden Lösungen, die sie zuvor erfolgreich eingesetzt haben.
Definition von Muster mit Lösungsschemen
Der Begriff des Musters wurde vom Architekten Christopher Alexander 1977 wie folgt definiert:
„Jedes Muster beschreibt ein in unserer Umwelt
beständiges, wiederkehrendes Problem und erläutert den
Kern der Lösung für dieses Problem, so dass Sie diese
Lösung beliebig oft anwenden können, ohne sie jemals
Software-Architektur-Muster:
Ein Software-Architektur-Muster beschreibt ein
bestimmtes, in einem speziellen Entwurfskontext häufig auftretendes Entwurfsproblem und präsentiert ein
erprobtes generisches Schema zu seiner Lösung. [...]
Ein Muster stellt also eine bewährte Lösung nach Art einer Schablone für ein häufiges Probleme bereit. So gesehen sind bereits die länger existierenden Algorithmensammlungen wie z.B. „The Art of Computer Programming“ von Donald Knuth eine Katalogisierung von Mustern (vergl. [Gamma97, S.392]).
Die Beschreibung eines Musters enthält im allgemeinen folgende Abschnitte:
Name: Im Idealfall Charakterisierung der Auswirkungen des Musters in einem Wort.
Zweck: Was macht das Muster? Was ist das Grundprinzip, was ist sein
Zweck? Welches Problem, in welchem Umfeld macht den Einsatz des Musters sinnvoll?
Auch bekannt als
Struktur: Beschreibung des eigentlichen Musters, textuell und Klassen - , eventuell ein Sequenzdiagramm.
Konsequenzen: Vor- und Nachteile, die durch den Einsatz des Musters in
Klassifikation von Patterns
Architektur Patterns
–
Beschreiben die grundlegende Struktur eines Softwaresystems
–
Client- Schicht, Server- Schicht und Daten- Schicht
Design Patterns
–
Beschreiben auch Strukturen, allerdings auf einer niedrigeren Ebene als A.P.
–
Typischer Weise mit Klassen und deren Beziehung zueinander
–
Kann somit zur Ausgestaltung von Teilsystemen führen
Idioms
–
Behandeln Detailprobleme die z.B. bei der Umsetzung von D.P.
entstehen können
–
Implementierungsaspekte
–
Programmiersprachenspezifische Probleme (Sockets/Java)
Entwurfsmuster - Konsequenzen
(+) Robustheit des Entwurfes
(+) Höherer Wiederverwendungsgrad
(+) Kommunikation
–
gemeinsames Vokabular
–
höhere Abstraktion
–
leichtere Dokumentation und Verständnis
Das Standardwerk
Aufgabe
Erzeugungsm. Strukturm. Verhaltensmuster klassenbasiert Fabrik Adapter Interpreter
Schablonenmethode
Gültigkeits
-bereich objektbasiert
Abstrakte Fabrik Erbauer
Prototyp Singleton
Adapter Brücke Dekorierer Fassade
Fliegengewicht Kompositum Proxy
Befehl
Beobachter (MVC) Besucher
Iterator Memento Strategie Vermittler Zustand
Zuständigskette
Entwurfsmuster: Gang of Four
Entwerfen von Systemen:
Klassenvererbung
Schnittstellenvererbung
Komposition
Delegation
Parametisierbare Typen
Klassenbibliotheken
Frameworks
Entwurfsmuster: Klassifizierung 1. Aufgabe: (was macht das Muster)
Erzeugungsmuster betreffen den Prozess der Objekterzeugung.
Strukturmuster befassen sich mit der Zusammensetzung von Klassen und Objekten.
Verhaltensmuster charakterisieren die Art und Weise, in der
Klassen und Objekte zusammenarbeiten und Zuständigkeiten
aufteilen
Entwurfsmuster: Klassifizierung 2. Gültigkeitsbereich:
Klassenbasierte Muster befassen sich mit Klassen und ihren Unterklassen. Beziehungen durch Vererbung (statisch).
Objektbasierte Muster befassen sich mit Objektbeziehungen,
die zur Laufzeit geändert werden können (dynamisch).
Vorgehen:
Finden passender Objekte
Bestimmen von Objektgranularität
Spezifizieren von Objektschnittstellen
Spezifizieren von Objektimplementierungen
Wiederverwendungsmechanismen anwenden
Strukturen der Laufzeit- und Übersetzungszeit aufeinander beziehen
Veränderungen in Entwürfen vorhersehen
Beispiel: Observer Pattern
Beispiel: Observer Pattern
Problem:
Mehrere Objekte einer Gruppe müssen sich benachrichtigen, wenn einige Attribute geändert wurden. Die Viewer sind
nicht „bekannt“.
Smalltalk: Model-View-Controller
MS Visual C++ Dokument-View-Architektur
Java: Model-View-Architektur
MDI-Fenster:
Daten in einer Tabelle Daten in einer Grafik
Dialogfenster Formelrechner
Java:
MFC: Die fünf Hauptklassen eines SDI-Programms
Anwendungsobjekt CWinApp
Zeiger auf
Dokumentvorlage CSingleDocTemplate
Zeiger auf
Dokumentobjekt CDocument
Zeiger auf
Rahmenfenster CFrameWnd
Zeiger auf
Ansichtsobjekt CView Zeiger auf
1) erzeugt
3) erzeugt 2) erzeugt
4) erzeugt
5) get-Methoden
6) set-Methoden UpdateAllViews
Klasse CDokument
Eigenschaften:
Neue Dokumente erstellen ( OnNewDocument )
Laden / Speichern ( Serialize )
Verknüpfung mit einem View ( getDocument )
Liste aller Views ( MDI-Programm )
Weitere Ableitungen (CDAORecordset etc.)
Enthält die Daten und den Controller (MVC)
Klasse CDokument
Ablauf nach dem Wizard:
Ableiten von CDocument (Automatisch)
Eintragen der Attribute (CDocument.h)
Get / Set-Methoden( *.h, *.cpp)
Methode Serialize (*.cpp)
– ar << m_xa; // Storing
– ar >> m_xa; // Loading
Methode OnOpenDocument( lpszPathName )
Klasse CDokument
Methoden von CDocument:
GetFirstViewPosition (MDI)
GetNextView (MDI)
AddView (MDI)
RemoveView(MDI)
UpdateAllViews(); (SDI)
Übergabe von Änderungen mittels einer Klasse
Benutzer fügt Objekt hinzu GetDocument()->Add(obj);
Ansichtsklasse
Add (Object obj) { m_liste.add(obj);
setModifies(true);
UpdateAllViews();
}
Dokumentenklasse
CView:OnUpdate() {
GetDocument()->getData();
Invalidate() }
•Methode OnUpdate wird überschrieben
•Wird das „Neuzeichnen“ auf
Klasse CView
Ablauf:
Ableiten von Cview (Automatisch)
Überschreiben der Methode OnDraw(CDC* pDC)
Zugriff auf das „Dokument“
–
CSdiDoc* pDoc = GetDocument();
–
pDoc->add(obj);
Übermittlung zusätzlicher Parameter an OnUpdate
Single Dokument Interface
Allgemeine Eigenschaften:
Menüs
Schalterleisten
Darstellung von Inhalten
Aufruf von Dialogfenstern
Open- / Savedialog
Laden / Speichern von Daten (Serialisierung)
Beispiel mit der MFC
Eigenschaften:
Anzeige der Inhalte dreier Integer-Zahlen
Z. B, Mittelpunkt, Radius
Speicherung der Daten in CDocument
Open- / Save-Dialog für die Serialisierung
Menü für Änderungen (x, y, r)
Nachricht ans Dokument
Fertiges Programm
Eigenschaften des Views:
Holen der beiden Werte aus dem Dokument
Definieren der GUI-Elemente
Erzeugen der GUI-Elemente
Anzeige der drei Static-Elemente (onDraw)
Anzeige der drei „Edit-Elemente“ (onDraw)
Menü für die Änderung der Inhalte
–
Neuberechnung
Beispiel: sdi_bsp1
SDI, mit Dokument / Ansicht-Architektur
Keine Datenbankanbindung
Keine Datenbank-Anbindung
Keine OLE-Client oder -Server
OLE-Server/Client
GUI-Elemente
Projekt-Format, Kommentare
Ergebnis des Wizards
Klassen
CSdi_bsp1View
CSdi_bsp1App
CSdi_bsp1Doc
CMainFrame
Klasse CDocument.h / CDocument.cpp
Eintragen der Variablen m_X, m_Y, m_R
Eintragen der Default-Werte: m_X=100, m_Y=200, m_R=74
Speichern der Werte
Definieren der Get-Methoden
Definieren der Set-Methoden
Einfügen der Get-Methoden
Einfügen der Set-Methoden
CDocument: Eintragen der Variablen Document.h
Member-Variable
–
int m_X, m_Y, m_R;
Methoden:
–
int getX(); void setX(int x);
Document.cpp
OnNewDocument
–
m_X = 100; m_Y = 200; m_R = 74;
Set- Get-Methoden
Serialize(CArchive& ar)
Klasse CView.cpp
Anzeige der Koordinaten mittels TextOut
Anzeige des Kreises in der Methode onDraw
Open-Dialog
Save-Dialog
CView: Anzeige der Daten
void CSdi_bsp1View::OnDraw(CDC* pDC) {
Csdi_bsp1Doc* pDoc = GetDocument();
CRect rect;
GetClientRect(&rect);
// Zeichnen der Daten int X = pDoc- >getX();
int Y = pDoc- >getY();
int R = pDoc- >getR();
pDC - >Ellipse(X - R, Y - R, X+R, Y+R);
CString sStr;
sStr.Format("P( %d / %d ) Radius %d",X, Y, R);
Klasse CView
Ändern der Inhalte
Durch mehrere Menüeinträge
Durch ein Dialogfenster
Ablauf
Neues Hauptmenü erstellen (Resource)
Neues Untermenü erstellen (Resource)
Nachrichten-Methode erstellen
Holen der Inhalte
Erstellen eines neuen Dialogfensters
Ändern durch die Funktion ein Dialogfenster
Speichern in CDocument
Ablauf
Anklicken der IDR_MAINFRAME
Hauptmenü erstellen (Inhalte)
Untermenü erstellen (X)
Untermenü erstellen (Y)
Untermenü erstellen (R)
Wizard (Strg+W)
ID_GRAFIK_X
Klassenname:
z
Csdi_bsp1View
COMMAND
Funktion hinzufügen
Code bearbeiten
Nachrichtenzuordnungstabellen
void CSdi_bsp1View::OnGrafikX() {
// TODO: Code für Befehlsbehandlungsroutine hier einfügen }
void CSdi_bsp1View::OnGrafikR() {
// TODO: Code für Befehlsbehandlungsroutine hier einfügen }
void CSdi_bsp1View::OnGrafikY()
Klasse CView: Menü Inhalte
void CSdi_bsp1View::OnGrafikX() {
// TODO: Code für Befehlsbehandlungsroutine hier einfügen CSdi_bsp1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int X = pDoc->getX();
X = 22; // Simulation der Eingabe, bzw. eigenes Dialogfenster pDoc->setX(X);
pDoc->UpdateAllViews( this ); // oder NULL // eigener View wird nicht benachrichtigt
Invalidate();
}
Klasse CView: Menü Inhalte
Code für das Update aus der Dokumentenklasse:
void CSdi_bsp1View::OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint) {
// TODO: Speziellen Code hier
// einfügen und/oder Basisklasse aufrufen Invalidate();
}
Klasse CView: onUpdate
Klasse CView: Menü Inhalte
Bezeichnungen
IDC_STATIC_BEZ
IDC_EDIT_ZAHL
Member-Variable
m_Zahl (UINT)
m_Label (CStatic)
Aufruf des Klassen-Assistenten (Strg+W)
Klassennamen: CKreisDialog
Member-Variablen definieren
-
m_Zahl (UINT)
-
m_Label (CStatic)
Aufrufs Dialogfensters
-
include-Datei (#include "KreisDialog.h")
-
Holen der Daten aus den Dokument
-
Erzeugen des Dialogfensters
Eintragen des Wertes (sStr.Format)
Weitere Ablauf:
void CSdi_bsp1View::OnGrafikX() {
// TODO: Code für Befehlsbehandlungsroutine hier einfügen CSdi_bsp1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int X = pDoc->getX();
CKreisDialog dlg;
dlg.m_Label = "Radius";
dlg.m_Zahl = X;
if (dlg.DoModal() == IDOK) { X = dlg.m_Zahl;
pDoc->setX(X);
pDoc->UpdateAllViews( this ); // oder NULL Invalidate();
} // if
}
Datei: CSdi_View.h // Attribute:
CPoint m_p1;
CPoint m_p2;
int m_Gummiband;
Aufgabe: Zeichnen eines Kreises für die Abspeicherung
WM_LBUTTONDOWN: Eine Taste der Maus wurde gedrückt
WM_LBUTTONUP: Eine Taste der Maus wurde losgelassen
WM_MOUSEMOVE: Die Maus wird bewegt
Definieren folgender Nachrichten:
Funktionen erzeugen und Code bearbeiten.
Mit der LButtonDown wird der Anfangspunkt definiert.
Mit der LButtonUp wird der Endpunkt definiert und die daten
werden in das Dokument eingetragen
void CSdi_bsp1View::OnLButtonDown(UINT nFlags, CPoint point) {
m_Gummiband=1;
m_p1 = point;
CView::OnLButtonDown(nFlags, point);
}
Setzt den internen Status auf 1, Kreis wird gezeichnet
Merkt sich den ersten Punkt
void CSdi_bsp1View:OnMouseMove(UINT nFlags, CPoint point) {
if (m_Gummiband>=1) { CDC *pDC;
pDC = CWnd::GetDC();
pDC - >SetROP2(R2_XORPEN);
pDC - >SelectStockObject(NULL_BRUSH);
CPen cpen1(PS_DASH, 1, RGB(255,0,0) );
CPen cpen2(PS_DASH, 1, RGB(0,0,255) );
pDC - >SelectObject(&cpen1);
if (m_Gummiband>=2) {
pDC - >SelectObject(&cpen1);
pDC - >Rectangle(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
}
m_p2 = point;
// pDC - >SelectObject(&cpen2);
pDC - >Rectangle(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
m_Gummiband=2;
ReleaseDC(pDC);
}
CView::OnMouseMove(nFlags, point);
}
void CSdi_bsp1View::OnLButtonUp(UINT nFlags, CPoint point) {
if (m_Gummiband>=2) { CDC *pDC;
pDC = CWnd::GetDC();
pDC - >SetROP2(R2_XORPEN);
pDC - >SelectStockObject(NULL_BRUSH);
pDC - >Rectangle(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
ReleaseDC(pDC);
CSdi_bsp1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
m_p2 = point;
pDoc- >setX( (m_p1.x+m_p2.x) >> 1 );
pDoc- >setY( (m_p1.y+m_p2.y) >> 1 );
pDoc- >setR( (m_p2.x - m_p1.x) >> 1 );
}
Standard-Dialoge
CFileDialog(
BOOL, // Oeffnen (true) oder Schliessen (false) LPCTSTR, // Default Extension =NULL, sDef
LPCTSTR, // Initial FileName =NULL, sFileName DWORD, // Flags wie OPENFILENAME
LPCTSTR, // Stringpaare als Filter CWnd * ) // Parentwindow
CFileDialog hat eine Membervariable m_ofn vom Typ OPENFILENAME.
Wichtige Memberfunktionen sind:
int DoModal() startet den Dialog
CString GetPathName() ermittelt den Dateinamen inklusive Pfad
CString GetFileName() ermittelt den reinen Dateinamen (ohne Pfad)
CString GetFileExt() ermittelt die Extension der Datei
Standard-Dialoge
Opendialog
CFileDialog dlg(TRUE);
dlg.DoModal();
AfxMessageBox( dlg.m_ofn.lpstrFile);
Savedialog
CFileDialog dlg(FALSE);
dlg.DoModal();
AfxMessageBox( dlg.m_ofn.lpstrFile);
Einbau spezieller Open- SaveDialoge
CString sDefExt, sFileName, sFilter, sCaption; // Defaultkonstruktor
sDefExt = ".html";
sCaption = "Datei öffnen";
sFilter = "HTML - Dateien (*.html;*.htm)|*.html;*.htm|Alle Dateien (*.*)|*.*||";
sFileName="test.html";
CFileDialog dlg(FALSE, sDefExt, sFileName,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, sFilter);
dlg.m_ofn.lpstrTitle = sCaption; // Überschrift if (dlg.DoModal() != IDOK) return;
CString sOutput = dlg.GetPathName();
AfxMessageBox( dlg.m_ofn.lpstrFile);
AfxMessageBox( sOutput);
OFN_READONLY
OFN_OVERWRITEPROMPT OFN_HIDEREADONLY
OFN_NOCHANGEDIR OFN_SHOWHELP OFN_ENABLEHOOK
OFN_ENABLETEMPLATE
OFN_ENABLETEMPLATEHANDLE OFN_NOVALIDATE
OFN_ALLOWMULTISELECT OFN_EXTENSIONDIFFERENT OFN_PATHMUSTEXIST
OFN_FILEMUSTEXIST OFN_CREATEPROMPT OFN_SHAREAWARE
OFN_NOREADONLYRETURN
#if(WINVER >= 0x0400) // new look commdlg OFN_EXPLORER
OFN_NODEREFERENCELINKS // force long names for 3.x modules OFN_LONGNAMES
// send include message to callback OFN_ENABLEINCLUDENOTIFY OFN_ENABLESIZING
#endif /* WINVER >= 0x0400 */
Open-Dialoge mit Filter: Konstanten
Eigener Open-Dialog mit Filter
void CSdi_bsp1App::OnFileOpen() {
CString sDefExt, sFileName, sFilter, sCaption;
sDefExt = ".txt";
sCaption = "Datei öffnen";
sFilter = "BIN-Dateien (*.bin)|*.bin|Alle Dateien (*.*)|*.*||";
sFileName="test.bin";
// TRUE = open
CFileDialog dlg(TRUE, sDefExt, sFileName,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, sFilter);
dlg.m_ofn.lpstrTitle = sCaption; // Überschrift
Aufruf des Dialog, Weiterleitung
Eigener Save-Dialog mit Filter
Eigener Save-Dialog mit Filter
void CSdi_bsp1View::OnFileSaveAs() {
CString sDefExt, sFileName, sFilter, sCaption; // Defaultkonstruktor sDefExt = ".txt";
sCaption = "Datei speichern";
sFilter = "BIN - Dateien (*.bin)|*.bin|Alle Dateien (*.*)|*.*||";
CSdi_bsp1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
sFileName=pDoc- >GetPathName();
CFileDialog dlg(FALSE, sDefExt, sFileName,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, sFilter);
dlg.m_ofn.lpstrTitle = sCaption; // Überschrift if (dlg.DoModal() == IDOK) {
pDoc- >SetPathName( dlg.GetPathName() );
Eingabe-Dialog für die Daten I1 und I2
Ablauf
Erstellen eines Dialogs mit Editorzeile
Klasse hinzufügen
Member-Variable einfügen
Dialogfenster in der Menü-Methode (Change_I1)
z
Definieren und erzeugen
z
I1 aus dem Document holen
z
I1 in die Editorzeile einfügen
z
Dialogfenster modal aufrufen
z
Ergebnis als String holen und umwandeln in eine Zahl
Eingabe-Dialog für die Daten I1 und I2:
ID: IDC_EDIT1
Formate: Number
Klassenname: CMyDialog
Eingabe-Dialog für die Daten I1 und I2:
void CSdi_bsp1View::OnInhalteI1() { CMyDialog dlg;
CString sStr;
CSdi_bsp1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int i1 = pDoc- >getI1();
sStr.Format("%d",i1);
dlg.m_Edit = sStr;
if (dlg.DoModal() == IDOK) { sStr = dlg.m_Edit;
int n = sscanf(sStr,"%d",&i1);
if (n==1 ) {
pDoc- >setI1(i1);
pDoc- >UpdateAllViews( this ); // oder NULL Invalidate();
} // if } // if
}
Viewklassen im Dialogfenster
CEditView
CFormView
CHtmlView
CListView
CRichEditView
CSrollView
CTreeView
CView
Beispiel mit CFormView
Wizard mit Dokumenten /Architektur
CFormView
Dokument mit zwei Integerzahlen (m_I1, m_I2)
set- und get-Methoden
SDI-Fenster in Dialogform
Speichern der Daten
Laden der Daten
Aufgabe:
-
I3 = I1 + I2
Methoden des Views
OnInitialUpdate
void CSdi_dialogView::OnInitialUpdate() {
// Holen des Dokuments
// setzen der Member-Variablen, VOR onInitialUpdate CSdi_dialogDoc* pDoc = GetDocument();
m_I1 = pDoc->getI1();
m_I2 = pDoc->getI2();
m_I3 = m_I1 + m_I2;
CFormView::OnInitialUpdate();
Methoden des Views
OnChangeEditI1
void CSdi_dialogView::OnChangeEditI1() {
UpdateData(TRUE); // Aus GUI nach Membervariablen m_I3 = m_I1 + m_I2;
UpdateData(FALSE);
// holen des Dokuments, speichern des Wertes CSdi_dialogDoc* pDoc = GetDocument();
pDoc->setI1( m_I1 );
pDoc->SetModifiedFlag(true);
pDoc->UpdateAllViews(this); // wenn this, dann kein Aufruf ! }
UpdateAllView hier nicht notwendig
Methoden des Views
OnChangeEditI2
void CSdi_dialogView::OnChangeEditI2() {
UpdateData(TRUE); // Aus GUI nach Membervariablen m_I3 = m_I1 + m_I2;
UpdateData(FALSE);
// holen des Dokuments, speichern des Wertes CSdi_dialogDoc* pDoc = GetDocument();
pDoc->setI2( m_I2 );
pDoc->SetModifiedFlag(true);
pDoc->UpdateAllViews(this); // wenn this, dann kein Aufruf !
Ergebnis:
Weitere Eigenschaften
Automatisches Laden
Automatisches Speichern
Abfrage: Änderungen speichern?
Neues Dokument enthalten
Liste der alten Dateien
Begrenzung der Nummern (Bereiche)