Microsoft Foundation Class (MFC)
Willemers Informatik-Ecke
MFC 1.0 kam mit Microsoft C/C++ 7.0 im April 1992 heraus. Vom Abstraktionsgrad und Ansatz ist sie mit der OWL von Borland vergleichbar. Die Einbindung in den Visual C++-Compiler wird insbesondere durch den Class-Wizard erheblich unterstützt.

Visual C++ besitzt zwei Codegeneratoren: den AppWizard und den ClassWizard. AppWizard ist derjenige, der nach File-New- Projects - MFC-AppWizard gestartet wird. Er erstellt das Grundgerüst, nachdem er erfragt hat, ob es eine SDI oder MDI ist.

Trennung zwischen Dokument und View

Das Dokument hält die Daten, während ein View eine Sicht der Daten realisiert. Die Kommunikation findet vom View zum Dokument durch den Aufruf der Funktion GetDocument statt, die einen Zeiger auf die Dokumentklasse liefert. Ein Dokument kann eine Änderung in den Daten durch die Funktion UpdateAllViews auslösen. Ein View erhält eine solche Information durch die Member-Funktion OnUpdate.

Auf der Document-View-Architektur setzen verschiedene durch den AppWizard erzeugte Elemente auf.

Der AppWizard

Der AppWizard erstellt für eine normale SDI-Anwendung die vier Klassen:

CxxxApppublic CWinApp
CxxxDocpublic CDocument
CxxxViewpublic CView
CMainFramepublic CFrameWin

Er erstellt eine Instanz namens theApp von CxxxApp.

Soll in einer SDI-Anwendung das Hauptfenster ein Formular sein, also mit Steuerungselementen versehen sein, wird im letzten Fenster des AppWizard statt CView die Einstellung CFormView verwendet.

Der ClassWizard

Das Einfügen einer neuen Nachricht erfolgt am einfachsten über View - ClassWizard. Hier wird die Klasse ausgewählt. Anschließend wird unter Message ausgewählt, welches Ereignis bearbeitet werden soll. Der Wizard fügt die Funktion im Header in der Klassendefinition ein, erstellt die Funktionen und trägt die Map nach.

Auf diese Weise läßt sich die Funktion OnCreate als Reaktion von WM_CREATE leicht erzeugen.

Erzeugen einer Dialogbox

Zunächst wird im Ressouce-Editor die Box entworfen und eine ID zugeordnet. Die ID sollte nicht nach dem Start des ClassWizard noch geändert werden. Der Wizard wird durch einen Doppelklick auf die Dialogbox erreicht. Es wird der Name der Klasse festgelegt, daraus erstellt der Wizard bereits cpp- und h-Datei.

Sollen Elemente in die Dialogbox gesetzt werden, erfolgt dies ebenfalls zunächst im Ressource-Editor. Anschließend kann im Wizard auf die Lasche Member Variables umgeschaltet werden. Dort findet sich in der Listbox eine Liste der Elemente. Daraus wählt man eines aus, wählt den Button Add Variables und kann im folgenden Dialog den Namen der Variablen festlegen. Unter Category kann gewählt werden, ob es eine Control-Variable sein soll (beispielsweise ein CEdit für ein Eingabefeld oder ein Value, in letztem Fall also ein CString.

Von Radio-Buttons lassen sich keine Member-Variablen erzeugen. Um dennoch die Elemente manipulieren zu können, verwendet man die GetDialogItem-Funktion, die zum Setzen der Markierung folgenden wenig eleganten Code ergibt:

((CButton *)(GetDialogItem(RB_NEW)))->SetCheck(true);

Programmieren mit der MFC

Die Message Map

Die Ereignisverteilung erfolgt über eine Message Map. In der Klasse des Fensters erfolgt der Aufruf DECLARE_MESSAGE_MAP(). Es folgt

BEGIN_MESSAGE_MAP(MeineKlasse, MFCKlasse)
  ON_COMMAND(WM_XXXXX, UserFunktion)
END_MESSAGE_MAP()

Normalerweise wird diese MAP vom ClassWizard erzeugt.

MDI: Multi Document Interface

Das Framewindow

Der Frame wird von CMDIFrameWnd abgeleitet.

IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_COMMAND(IDM_BOUNCE, OnBounce)
	ON_COMMAND(IDM_HELLO, OnHello)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

Über das Menü wird das Erzeugen der Kindfenster gesteuert. Damit ist auch schon die wichtigste Aufgabe des Rahmens erledigt.

void CMainFrame::OnBounce()
{
  CBounceWnd *pBounceWnd = new CBounceWnd;
  if (!pBounceWnd->Create(_T("Bounce"), WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, rectDefault, this))
    return;
}

void CMainFrame::OnHello()
{
	CHelloWnd *pHelloWnd = new CHelloWnd;
	if (!pHelloWnd->Create(_T("Hello"),
		WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
		rectDefault, this))
		return;
}

Der PostNcDestroy-Behandlung wird das Objekt löschen, wenn es zerstört wird.

Das Childwindow

Ein Childwindow leitet sich von der Klasse CMDIChildWnd ab. Ansonsten verhält es sich weitgehend wie ein Standardfenster.

Menübehandlung

Um Menüpunkte zu enablen oder mit einem Haken zu versehen, benutzt man am einfachsten den ClassWizard.

Im ClassWizard wählt man die ID des Menüpunktes. Es erscheinen in der nebenstehenden Dialogbox die Einträge COMMAND und UPDATE_COMMAND_UI. Letzterer wird doppelt angeklickt. Es erscheint das Angebot, eine Funktion zu erstellen mit dem Namen OnUpdateXXXXX. Die entstehende Funktion hat etwa folgendes Aussehen:

void xxxx::OnUpdateXXXXX(CCmdUI *pCmdUI)
{
    pCmdUI->Enable(Bedingung);   // schaltet das Item grau oder enable
    pCmdUI->SetCheck(Bedingung); // setzt bzw. löscht einen Haken
}

Dateiauswahl Dialog

Der Dateiauswahldialog wird in der Klasse CFileDialog gekapselt. Der minimale Konstruktur benötigt lediglich ein Flag, das angibt, ob es ein Open- oder SaveAs-Dialog ist. Beispiel:

CFileDialog DiaOpen(true);
CFileDialog DiaSaveAs(false);

Der Konstruktor lautet vollständig:

CFileDialog(
  BOOL,		// Oeffnen (true) oder Schliessen (false)
  LPCTSTR,	// Default Extension  =NULL
  LPCTSTR,	// Initial FileName   =NULL
  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

Das ListView-Element in der Detaill-Ansicht

Das List-View ist unter MFC vom Typ CListCtrl. Wie jedes Kontrollelement kann es durch Create erzeugt werden oder im Ressource-Editor als Dialogelement.

Es kann in der OnCreate-Funktion des Elternfensters angelegt werden durch Aufruf der Memberfunktion Create. Etwa:

  Liste.Create(WS_VISIBLE|WS_BORDER|LVS_REPORT, CRect(20,20,120,150),this,LIST_ID);

In einer Dialogbox ist ein solcher Create nicht erforderlich, da die Liste durch die Ressource beschrieben ist und im Zuge des Aufbaus der Dialogbox entsteht.

Spalten

Das Anlegen der Spalten erfolgt recht simpel durch den Aufruf der Memberfunktion

Liste.InsertColumn(i, char *Titel);

Wenn Spalten gelöscht werden sollen, betrifft das meist alle Spalten. Dies erfolgt schnell und elegant durch

while (Liste.DeleteColumn(0));

Dabei wird immer die erste Spalte gelöscht, bis als Fehlermeldung eine 0 zurückgegeben wird.

Zeilen

Für jede Zeile wird ein Item dem ListView-Element hinzugefügt.

LVITEM Item = {0};
Item.mask = LVIF_TEXT;
Item.iItem = 0;			// immer oben einhaengen
Item.pszText = TextPointer;
Liste.InsertItem(&Item);

Dadurch wird zunächst die linke Spalte gefüllt. Die anderen Spalten sind nur SubItems, die einfach mit

Liste.SetItemText(iItem, iSubItem, TextPointer);

Dabei ist
iItemdie Zeilennummer
iSubItemdie Spaltennummer
TextPointerder angezeigte Text

Um alle Zeilen zu löschen, kann

Liste.DeleteAllItems();

verwendet werden.

IDE und Editor

Strg-F2 setzt eine Bookmark an der aktuellen Stelle, die man später mit F2 (vorwärts) und Shift-F2 (zurück) anspringen kann. Bookmarks bleiben nach erneutem Laden nicht erhalten.

Dies und das

CWnd hat das HWND in seiner Member-Variablen m_hWnd.

ODBC-Einbindung findet sich unter dem Thema Datenbanken

Literatur