vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Tag B

Antworten

Antworten für Tag 1

Antworten zu den Kontrollfragen

1. Markieren Sie im Dialog-Editor die zu ändernde Schaltfläche. Klicken Sie mit der rechten Maustaste, und wählen Sie Eigenschaften aus dem Kontextmenü. Ändern Sie den Wert im Feld Titel.

2. Mit dem Anwendungs-Assistenten erstellen Sie das Gerüst Ihrer Anwendung basierend auf dem Typ der Anwendung und der geplanten Funktionalität. Das Gerüst unterstützt dann die in den Dialogfeldern des Assistenten spezifizierte Funktionalität.

3. Mit dem Klassen-Assistenten erzeugen Sie eine Funktion und verbinden sie mit einem Objekt, das eine bestimmte Windows-Nachricht behandelt. Der Klassen-Assistent erzeugt die Funktion und öffnet die zugehörige Quelldatei bereits an der richtigen Stelle, so daß Sie sofort mit der Eingabe des eigenen Codes beginnen können.

Lösung zur Übung

Führen Sie die folgenden Schritte aus:

1. Gehen Sie im Arbeitsbereich auf die Registerkarte Ressourcen.

2. Erweitern Sie den Zweig Dialog, und doppelklicken Sie auf das Dialogfeld IDD_ABOUTBOX. Dieses Dialogfeld wird daraufhin im Editor des Visual Studios zur Bearbeitung angezeigt.

3. Klicken Sie in der Werkzeugleiste auf das Symbol für Schaltfläche.

4. Klicken und ziehen Sie die Maus an die Stelle im Dialogfeld, wo Sie die Schaltfläche plazieren möchten.

5. Öffnen Sie den Eigenschaftsdialog für die neue Schaltfläche. Ändern Sie die ID und den Titel, um den Text auf der Schaltfläche festzulegen. Schließen Sie das Eigenschaftsdialogfeld.

6. Öffnen Sie den Klassen-Assistenten, und fügen Sie eine neue Funktion für die Klicknachricht (BN_CLICKED) für Ihre neue Schaltfläche hinzu.

7. Klicken Sie im Klassen-Assistenten auf die Schaltfläche Code bearbeiten. Der Klassen-Assistent öffnet daraufhin die Quelldatei und zeigt die neue Funktion zur Bearbeitung an.

8. Fügen Sie die Funktion MessageBox hinzu, um die Nachricht für den Benutzer auszugeben.

9. Kompilieren und starten Sie die Anwendung, um die neue Schaltfläche zu testen.

Antworten für Tag 2

Antworten zu den Kontrollfragen

1. Indem Sie die Tabulator-Reihenfolge der Steuerelemente in Ihrem Anwendungsfenster festlegen, können Sie bestimmen, auf welchem Weg der Benutzer durch das Anwendungsfenster navigiert. Wenn der Benutzer mit der Tastatur arbeitet, kann er mit der <Tab>-Taste von einem Steuerelement zum nächsten wechseln und über die Zugriffstasten ein bestimmtes Steuerelement direkt anspringen. Die Tabulator-Reihenfolge bietet dem Benutzer ein einheitliches und vorhersagbares Verhalten, wenn er im Anwendungsfenster navigiert.

2. Wenn man eine Zugriffstaste für ein Textfeld vorsieht und dann sicherstellt, daß sich das Textfeld unmittelbar vor dem zugeordneten Eingabefeld befindet, kann der Benutzer die Zugriffstaste des Textfelds wählen, um direkt zum Eingabefeld zu gelangen.

3. Die eindeutigen Objekt-IDs für die beiden statischen Textfelder sind erforderlich, da diese beiden Steuerelemente in Abhängigkeit vom Zustand der Kontrollkästchen, die bestimmte Gruppen von Steuerelementen aktivieren oder deaktivieren, manipuliert werden sollen.

4. Wenn der Benutzer den Wert des Steuerelements auf dem Bildschirm ändert, ist die Funktion UpdateData mit dem Argument TRUE aufzurufen, um die Werte aus den Steuerelementen im Fenster in die Variablen, die mit diesen Steuerelementen verbunden sind, zu übertragen. Ruft man UpdateData nicht auf, spiegeln die Variablen nicht die aktuellen Änderungen wider, die der Benutzer in den Steuerelementen auf dem Bildschirm vorgenommen hat.

Lösungen zu den Übungen

1. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Klicknachricht der Schaltfläche Standardnachricht (IDC_DFLTMSG) hinzu. In die Funktion schreiben Sie den Code gemäß Listing B.1.

Listing B.1: Tag2Dlg.cpp - Dieser Code plaziert eine Standardnachricht im Eingabefeld.

1: void CTag2Dlg::OnDfltmsg()
2: {
3: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Nachricht auf Standardnachricht setzen
10: m_strMessage = "Nachricht hier eingeben";
11:
12: // Bildschirm aktualisieren
13: UpdateData(FALSE);
14:
15: ///////////////////////
16: // EIGENER CODE, ENDE
17: ///////////////////////
18: }

2. Fügen Sie Funktionen für die Kontrollkästchen Programmaktion aktivieren und Programmaktion zeigen hinzu. In diese Funktionen übernehmen Sie den Code aus Listing B.2.

Listing B.2: Tag2Dlg.cpp - Die zur Ausführung von Programmen relevanten Steuerelemente werden mit diesem Code aktiviert/deaktiviert und angezeigt/ausgeblendet.

1: void CTag2Dlg::OnCkenblpgm()
2: {
3: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Aktuelle Werte vom Bildschirm holen
10: UpdateData(TRUE);
11:
12: // Kontrollkästchen 'Programmaktion aktivieren' eingeschaltet?
13: if (m_bEnablePgm == TRUE)
14: {
15: // Ja, dann alle Steuerelemente aktivieren, die
16: // für den Start eines Programms relevant sind.
17: GetDlgItem(IDC_PROGTORUN)->EnableWindow(TRUE);
18: GetDlgItem(IDC_RUNPGM)->EnableWindow(TRUE);
19: GetDlgItem(IDC_STATICPGM)->EnableWindow(TRUE);
20: }
21: else
22: {
23: // Nein, dann alle Steuerelemente deaktivieren, die
24: // für den Start eines Programms relevant sind.
25: GetDlgItem(IDC_PROGTORUN)->EnableWindow(FALSE);
26: GetDlgItem(IDC_RUNPGM)->EnableWindow(FALSE);
27: GetDlgItem(IDC_STATICPGM)->EnableWindow(FALSE);
28: }
29:
30: ///////////////////////
31: // EIGENER CODE, ENDE
32: ///////////////////////
33: }
34:
35: void CTag2Dlg::OnCkshwpgm()
36: {
37: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
38:
39: ///////////////////////
40: // EIGENER CODE, ANFANG
41: ///////////////////////
42:
43: // Aktuelle Werte vom Bildschirm holen
44: UpdateData(TRUE);
45:
46: // Kontrollkästchen 'Programmaktion zeigen' eingeschaltet?
47: if (m_bShowPgm == TRUE)
48: {
49: // Ja, dann alle Steuerelemente anzeigen, die
50: // für den Start eines Programms relevant sind.
51: GetDlgItem(IDC_PROGTORUN)->ShowWindow(TRUE);
52: GetDlgItem(IDC_RUNPGM)->ShowWindow(TRUE);
53: GetDlgItem(IDC_STATICPGM)->ShowWindow(TRUE);
54: }
55: else
56: {
57: // Nein, dann alle Steuerelemente ausblenden, die
58: // für den Start eines Programms relevant sind.
59: GetDlgItem(IDC_PROGTORUN)->ShowWindow(FALSE);
60: GetDlgItem(IDC_RUNPGM)->ShowWindow(FALSE);
61: GetDlgItem(IDC_STATICPGM)->ShowWindow(FALSE);
62: }
63:
64: ///////////////////////
65: // EIGENER CODE, ENDE
66: ///////////////////////
67: }

3. Modifizieren Sie die Funktion OnRunpgm gemäß Listing B.3.

Listing B.3: Tag2Dlg.cpp - Dieser Code startet das Programm, dessen Name der Benutzer in das Kombinationsfeld Programm starten eingibt.

1: void CTag2Dlg::OnRunpgm()
2: {
3: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Aktuelle Werte vom Bildschirm holen
10: UpdateData(TRUE);
11:
12: // Lokale Variable zur Aufnahme des Programmnamens deklarieren
13: CString strPgmName;
14:
15: // Programmname in die lokale Variable kopieren
16: strPgmName = m_strProgToRun;
17:
18: // Programmname in Großbuchstaben umwandeln
19: strPgmName.MakeUpper();
20:
21: // Programm Paint gewählt?
22: if (strPgmName == "PAINT")
23: // Ja, Paint starten
24: WinExec("pbrush.exe", SW_SHOW);
25:
26: // Editor (Notepad) gewählt?
27: if (strPgmName == "EDITOR")
28: // Ja, Editor starten
29: WinExec("notepad.exe", SW_SHOW);
30:
31: // Solitär gewählt?
32: if (strPgmName == "SOLITÄR")
33: // Ja, Solitär starten
34: WinExec("sol.exe", SW_SHOW);
35:
36: // Programm zu eingegebenem Namen in das Kombinationsfeld starten
37: if ((strPgmName != "PAINT") && (strPgmName != "EDITOR") &&
38: (strPgmName != "SOLITÄR"))
39: // Ja, eingegebenen Programmnamen aufrufen
40: WinExec(strPgmName, SW_SHOW);
41:
42: ///////////////////////
43: // EIGENER CODE, ENDE
44: ///////////////////////
45: }

Antworten für Tag 3

Antworten zu den Kontrollfragen

1. WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK, WM_MOUSEMOVE und WM_MOUSEWHEEL.

2. Man kann die Flags, die an die Funktion OnMouseMove übergeben werden, mit dem Flag MK_LBUTTON wie folgt maskieren:

((nFlags & MK_LBUTTON) == MK_LBUTTON)

3. Geben Sie in der Behandlungsroutine OnSetCursor den Wert TRUE zurück. Damit verhindern Sie, daß die Funktion OnSetCursor der Basisklasse aufgerufen wird.

Lösungen zu den Übungen

1. Fügen Sie eine Funktion für die Nachricht WM_RBUTTONDOWN hinzu, und schreiben Sie in die Funktion den Code aus Listing B.4.

Listing B.4: MausDlg.cpp - Die Funktion OnRButtonDown

1: void CMausDlg::OnRButtonDown(UINT nFlags, CPoint point)
2: {
3: // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen und/oder Standard aufrufen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Aktuellen Punkt als Startpunkt setzen
10: m_iPrevX = point.x;
11: m_iPrevY = point.y;
12:
13: ///////////////////////
14: // EIGENER CODE, ENDE
15: ///////////////////////
16:
17: CDialog::OnRButtonDown(nFlags, point);
18: }

Erweitern Sie die Funktion OnMouseMove gemäß Listing B.5.

Listing B.5: MausDlg.cpp - Die modifizierte Funktion OnMouseMove

1: void CMausDlg::OnMouseMove(UINT nFlags, CPoint point)
2: {
3: // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen und/oder Standard aufrufen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Linke Maustaste gedrückt?
10: if ((nFlags & MK_LBUTTON) == MK_LBUTTON)
11: {
12: // Gerätekontext holen
13: CClientDC dc(this);
14:
15: // Neuen Stift erzeugen
16: CPen lpen(PS_SOLID, 16, RGB(255, 0, 0));
17:
18: // Den neuen Stift verwenden
19: dc.SelectObject(&lpen);
20:
21: // Linie vom letzten zum aktuellen Punkt zeichnen
22: dc.MoveTo(m_iPrevX, m_iPrevY);
23: dc.LineTo(point.x, point.y);
24:
25: // Aktuellen Punkt als letzten Punkt speichern
26: m_iPrevX = point.x;
27: m_iPrevY = point.y;
28: }
29:
30: // Rechte Maustaste gedrückt?
31: if ((nFlags & MK_RBUTTON) == MK_RBUTTON)
32: {
33: // Gerätekontext holen
34: CClientDC rdc(this);
35:
36: // Neuen Stift erzeugen
37: CPen rpen(PS_SOLID, 16, RGB(0, 0, 255));
38:
39: // Den neuen Stift verwenden
40: rdc.SelectObject(&rpen);
41:
42: // Linie vom letzten zum aktuellen Punkt zeichnen
43: rdc.MoveTo(m_iPrevX, m_iPrevY);
44: rdc.LineTo(point.x, point.y);
45:
46: // Aktuellen Punkt als letzten Punkt speichern
47: m_iPrevX = point.x;
48: m_iPrevY = point.y;
49: }
50:
51: ///////////////////////
52: // EIGENER CODE, ENDE
53: ///////////////////////
54:
55: CDialog::OnMouseMove(nFlags, point);
56: }

2. Die modifizierte Funktion OnKeyDown kann etwa folgendermaßen aussehen:

void CMausDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen und/ oder Standard aufrufen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

char lsChar; // Zeichen der gedrückten Taste
HCURSOR lhCursor; // Handle zum anzuzeigenden Cursor

// Code der gedrückten Taste in Zeichen umwandeln
lsChar = char(nChar);

// Ist Zeichen ein "A"
if (lsChar == 'A')
{
// Pfeilcursor laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "B"
if (lsChar == 'B')
{
// Balkencursor laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "C"
if (lsChar == 'C')
{
// Sanduhrzeiger laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "D"
if (lsChar == 'D')
{
// Fadenkreuz-Cursor laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_CROSS);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "E"
if (lsChar == 'E')
{
// Cursor mit Pfeil nach oben laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_UPARROW);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "F"
if (lsChar == 'F')
{
// Cursor zur Größenveränderung laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZEALL);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "G"
if (lsChar == 'G')
{
// Cursor zur Größenveränderung, von Nordwest nach Südost, laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZENWSE);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "H"
if (lsChar == 'H')
{
// Cursor zur Größenveränderung, von Nordost nach Südwest, laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZENESW);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "I"
if (lsChar == 'I')
{
// Cursor zur Größenveränderung, von West nach Ost, laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "J"
if (lsChar == 'J')
{
// Cursor zur Größenveränderung, von Nord nach Süd, laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZENS);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

if (lsChar == 'K')
{
// "Kein" Cursor laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_NO);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

if (lsChar == 'L')
{
// Cursor beim Starten der Anwendung (Pfeil und Sanduhr) laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_APPSTARTING);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

if (lsChar == 'M')
{
// Hilfe-Cursor (Pfeil und Fragezeichen) laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_HELP);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
}

// Ist Zeichen ein "X"
if (lsChar == 'X')
{
// Pfeilcursor laden
lhCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
// Cursorflag setzen
m_bCursor = TRUE;
// Bildschirmcursor setzen
SetCursor(lhCursor);
// Anwendung beenden
OnOK();
}

///////////////////////
// EIGENER CODE, ENDE
///////////////////////

CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}

Antworten für Tag 4

Antworten zu den Kontrollfragen

1. Durch die Definition der beiden IDs sind diese als Konstanten durch die gesamte Anwendung hindurch verfügbar.

2. Fügen Sie sie als #define-Konstanten in die Header-Datei der Klasse (TimerDlg.h) wie folgt ein:

/////////////////////////////////////////////////////////////////////// CTimerDlg Dialogfeld

#define ID_CLOCK_TIMER 1
#define ID_COUNT_TIMER 2

class CTimerDlg : public CDialog
{
.
.
.

3. Anhand der Timer-ID läßt sich bestimmen, welcher Timer das Ereignis ausgelöst hat.

4. Genau ein Timer-Ereignis.

Lösung zur Übung

Um das Intervall zu ändern, mit dem ein Timer läuft, müssen Sie zuerst den Timer stoppen und dann erneut starten, wie es aus Listing B.6 hervorgeht.

Listing B.6: Die überarbeiteten Funktionen OnStarttime und OnStoptimer

1: void CTimerDlg::OnStarttime()
2: {
3: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Variablen aktualisieren
10: UpdateData(TRUE);
11:
12: // Zähler aktualisieren
13: m_iCount = 0;
14: // Zähler für Anzeige formatieren
15: m_sCount.Format("%d", m_iCount);
16:
17: // Dialogfeld aktualisieren
18: UpdateData(FALSE);
19: // Timer starten
20: SetTimer(ID_COUNT_TIMER, m_iInterval, NULL);
21:
22: // Uhren-Timer anhalten
23: KillTimer(ID_CLOCK_TIMER);
24: // Uhren-Timer mit dem Intervall des Zählers neu starten
25: SetTimer(ID_CLOCK_TIMER, m_iInterval, NULL);
26:
27: // Schaltfläche Timer anhalten aktivieren
28: m_cStopTime.EnableWindow(TRUE);
29: // Schaltfläche Timer starten deaktivieren
30: m_cStartTime.EnableWindow(FALSE);
31:
32: ///////////////////////
33: // EIGENER CODE, ENDE
34: ///////////////////////
35: }
36:
37: void CTimerDlg::OnStoptimer()
38: {
39: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
40:
41: ///////////////////////
42: // EIGENER CODE, ANFANG
43: ///////////////////////
44:
45: // Timer anhalten
46: KillTimer(ID_COUNT_TIMER);
47:
48: // Uhren-Timer anhalten
49: KillTimer(ID_CLOCK_TIMER);
50: // Uhren-Timer mit Intervall 1 Sekunde neu starten
51: SetTimer(ID_CLOCK_TIMER, 1000, NULL);
52:
53: // Schaltfläche Timer anhalten deaktivieren
54: m_cStopTime.EnableWindow(FALSE);
55: // Schaltfläche Timer starten aktivieren
56: m_cStartTime.EnableWindow(TRUE);
57:
58: ///////////////////////
59: // EIGENER CODE, ENDE
60: ///////////////////////
61: }

Antworten für Tag 5

Antworten zu den Kontrollfragen

1. IDRETRY und IDCANCEL.

2. Als MFC-Klassen sind die folgenden allgemeinen Windows-Dialogfelder definiert:

3. Ein modales Dialogfeld stoppt alle Programmabläufe in der Anwendung, bis der Benutzer auf das Dialogfeld antwortet. Ein nichtmodales Dialogfeld gestattet dem Benutzer, mit der übrigen Anwendung weiterzuarbeiten, während das Dialogfeld geöffnet bleibt und Eingaben entgegennehmen kann.

4. In der Variablendeklaration der Klasseninstanz übergeben Sie FALSE statt TRUE. Damit sieht die Variablendeklaration folgendermaßen aus:

CFileDialog m_ldFile(FALSE);

5. Im benutzerdefinierten Dialogfeld ist lediglich die Funktion UpdateData aufzurufen, bevor das Dialogfeld geschlossen wird. Da Sie die Schaltflächen OK und Abbrechen nicht aus dem Dialogfeld gelöscht haben, führt die Schaltfläche OK automatisch diese Funktion aus.

Lösungen zu den Übungen

1. Modifizieren Sie die Funktion OnFileopen wie folgt:

void CDialogeDlg::OnFileopen()
{
// TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

CFileDialog m_ldFile(TRUE);

// Dialogfeld Öffnen zeigen und Ergebnis auffangen
if (m_ldFile.DoModal() == IDOK)
{
// Gewählten Dateinamen ermitteln
m_sResults = m_ldFile.GetPathName();
// Dialogfeld aktualisieren
UpdateData(FALSE);
}

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

Die Funktion GetPathName gibt den Pfad und Dateinamen zurück, so daß bei Änderung des Funktionsaufrufs von GetFileName in GetPathName der Pfad zusammen mit dem Dateinamen in der Anzeige erscheint.

2. Führen Sie die folgenden Schritte aus:

1. Fügen Sie über den Klassen-Assistenten eine Member-Variable in die Klasse CMsgDlg ein. Legen Sie den Variablentyp als int, den Namen mit m_iYesNo und den Zugriff als Public fest.

2. Gehen Sie auf die Registerkarte Ressourcen, und öffnen Sie das benutzerdefinierte Dialogfeld zur Bearbeitung. Ziehen Sie eine Schaltfläche auf das Dialogfeld, tragen Sie IDC_YESNO als ID und &Ja oder Nein als Titel ein.

3. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die neue Schaltfläche hinzu, und schreiben Sie in diese Funktion den folgenden Code:

void CMsgDlg::OnYesno()
{
// TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Benutzer fragen
m_iYesNo = MessageBox("Ja oder Nein wählen",
"Ja oder Nein",
MB_YESNO);

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

4. Nehmen Sie in das Hauptdialogfeld eine Schaltfläche mit der ID IDC_YESNO und dem Titel Ja oder &Nein auf.

5. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die neue Schaltfläche hinzu, und schreiben Sie den folgenden Code in diese Funktion:

void CDialogeDlg::OnYesno()
{
// TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Was hat Benutzer geantwortet?
switch (m_dMsgDlg.m_iYesNo)
{
case IDYES: // Mit Ja geantwortet?
m_sResults = "Ja!";
break;
case IDNO: // Mit Nein geantwortet?
m_sResults = "Nein!";
break;
}

// Dialogfeld aktualisieren
UpdateData(FALSE);

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

Antworten für Tag 6

Antworten zu den Kontrollfragen

1. COMMAND

2. Öffnen Sie im Dialog-Editor den Eigenschaftsdialog für das Fenster, und wählen Sie das Menü aus der Dropdown-Liste der Menüs aus.

3. Die Dialogklasse für das Fenster, in dem das Menü erscheint.

4. Durch das Ereignis WM_CONTEXTMENU.

Lösungen zu den Übungen

1. Führen Sie die folgenden Schritte aus:

1. Nehmen Sie eine Schaltfläche in das Dialogfeld auf. Tragen Sie für diese Schaltfläche die ID IDC_HELLO und den Titel &Hello ein.

2. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Schaltfläche hinzu. Verwenden Sie OnHello als Funktionsname.

2. Führen Sie die folgenden Schritte aus:

1. Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Nachricht WM_CONTEXTMENU in Ihr Dialogfeld ein.

2. Schreiben Sie den folgenden Code in die Funktion:

void CMenusDlg:: OnContextMenu(CWnd* pWnd, CPoint point)
{
// TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Lokale Variablen deklarieren
CMenu *m_lMenu; // Zeiger auf Menü
CPoint m_pPoint; // Kopie der Mausposition

// Mausposition in lokale Variable kopieren
m_pPoint = point;
// Position in Bildschirmkoordinaten konvertieren
ClientToScreen(&m_pPoint);
// Zeiger auf Fenstermenü holen
m_lMenu = GetMenu();
// Zeiger auf zweites Untermenü holen
m_lMenu = m_lMenu->GetSubMenu(1);
// Popup-Menü anzeigen
m_lMenu->TrackPopupMenu(TPM_CENTERALIGN + TPM_LEFTBUTTON,
m_pPoint.x, m_pPoint.y, this, NULL);

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

Antworten für Tag 7

Antworten zu den Kontrollfragen

1. Übergeben Sie für das Argument bUnderline an die Funktion CreateFont den Wert 1.

2. Übergeben Sie für das Argument nEscapement an die Funktion CreateFont den Wert 1800.

3. Diese Funktion wird für jede im System verfügbare Schrift je einmal aufgerufen, außer wenn die Callback-Funktion den Wert 0 zurückgibt und die Auflistung der Schriften stoppt.

Lösungen zu den Übungen

1. Nehmen Sie das Kontrollkästchen in das Dialogfeld auf. Legen Sie für die Eigenschaft ID den Wert IDC_CBUSETEXT und als Titel Text &verwenden fest.

Weisen Sie dem Steuerelement mit dem Klassen-Assistenten eine Variable zu. Legen Sie den Variablentyp als BOOL und den Namen mit m_bUseText fest.

Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Nachricht BN_CLICKED des Kontrollkästchens hinzu. In die Funktion schreiben Sie den folgenden Code:

void CTag7Dlg::OnCbusetext()
{
// TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Variablen mit den Steuerelementen des Dialogfelds aktualisieren
UpdateData(TRUE);
// Schriftname für Schriftprobe verwenden?
if (!m_bUseText)
// Schriftname verwenden
m_strDisplayText = m_strFontName;
else
// Eingegebenen Text verwenden
m_strDisplayText = m_strSampText;

// Dialogfeld mit Variablen aktualisieren
UpdateData(FALSE);

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

Um das Kontrollkästchen zu initialisieren, modifizieren Sie die Funktion OnInitDialog wie folgt:

BOOL CTag7Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
.
.
.
// ZU ERLEDIGEN: Hier zusätzliche Initialisierung einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Listenfeld für Schriften füllen
FillFontList();

// Einzugebenden Text initialisieren
m_strSampText = "Testen";
// Text in Feld für Schriftbeispiel kopieren
m_strDisplayText = m_strSampText;
// Kontrollkästchen initialisieren
m_bUseText = TRUE;
// Dialogfeld aktualisieren
UpdateData(FALSE);

///////////////////////
// EIGENER CODE, ENDE
///////////////////////

return TRUE; // Geben Sie TRUE zurück, außer ein Steuerelement soll den Fokus erhalten
}

Modifizieren Sie die Funktion OnSelchangeLfonts wie folgt:

void CTag7Dlg::OnSelchangeLfonts()
{
// TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Variablen mit Steuerelementen des Dialogfelds aktualisieren
UpdateData(TRUE);
// Schriftname für Schriftprobe verwenden?
if (!m_bUseText)
{
// Schriftname in Feld für Schriftbeispiel kopieren
m_strDisplayText = m_strFontName;
// Dialogfeld mit Variablen aktualisieren
UpdateData(FALSE);
}
// Schrift für Beispiel festlegen
SetMyFont();

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

Schließlich ändern Sie noch die Funktion OnChangeEsamptext folgendermaßen ab:

void CTag7Dlg::OnChangeEsamptext()
{
// TODO: Wenn dies ein RICHEDIT-Steuerelement ist, sendet das Steuerelement diese
// Benachrichtigung nicht, bevor Sie nicht die Funktion CDialog::OnInitDialog()
// überschreiben und CRichEditCrtl().SetEventMask() aufrufen, wobei
// eine ODER-Operation mit dem Attribut ENM_CHANGE und der Maske erfolgt.

// TODO: Fügen Sie hier Ihren Code für die Benachrichtigungsbehandlungsroutine des Steuerelements hinzu

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Variablen mit Steuerelementen des Dialogfelds aktualisieren
UpdateData(TRUE);
// Text für Schriftprobe verwenden?
if (m_bUseText)
{
// Aktuellen Text in Schriftprobe kopieren
m_strDisplayText = m_strSampText;
// Dialogfeld mit Variablen aktualisieren
UpdateData(FALSE);
}

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

2. Nehmen Sie das Kontrollkästchen in das Dialogfeld auf. Legen Sie für die Eigenschaft ID den Wert IDC_CBITALIC und als Titel &Kursiv fest.

Weisen Sie dem Steuerelement mit dem Klassen-Assistenten eine Variable zu. Legen Sie den Variablentyp als BOOL und den Namen mit m_bItalic fest.

Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Nachricht BN_CLICKED des Kontrollkästchens hinzu. In die Funktion schreiben Sie den folgenden Code:

void CTag7Dlg::OnCbitalic()
{
// TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Variablen mit Steuerelementen des Dialogfelds aktualisieren
UpdateData(TRUE);
// Schrift für Beispiel festlegen
SetMyFont();

///////////////////////
// EIGENER CODE, ENDE
///////////////////////
}

Ändern Sie die Funktion SetMyFont entsprechend dem nachstehenden Listing ab:

void CTag7Dlg::SetMyFont()
{
CRect rRect; // Rechteck des Anzeigebereichs
int iHeight; // Höhe des Anzeigebereichs
int iItalic = 0; // Schrift kursiv ausgeben?

// Wurde eine Schrift ausgewählt?
if (m_strFontName != "")
{
// Abmessungen des Feldes für Schriftbeispiel ermitteln
m_ctlDisplayText.GetWindowRect(&rRect);
// Höhe des Feldes berechnen
iHeight = rRect.top - rRect.bottom;
// Sicherstellen, daß Höhe positiv
if (iHeight < 0)
iHeight = 0 - iHeight;
// Schrift kursiv ausgeben?
if (m_bItalic)
iItalic = 1;
// Zu verwendende Schrift erzeugen
m_fSampFont.CreateFont((iHeight - 5), 0, 0, 0,
FW_NORMAL, iItalic, 0, 0, DEFAULT_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH |
FF_DONTCARE, m_strFontName);

// Schrift für Anzeigefeld der Schriftprobe festlegen
m_ctlDisplayText.SetFont(&m_fSampFont);
}
}

Antworten für Tag 8

Antworten zu den Kontrollfragen

1. Rot, Grün und Blau.

2. Mit dem Gerätekontext.

3. 8 mal 8 Pixel.

4. Die Nachricht WM_PAINT.

5. Rufen Sie die Funktion Invalidate auf diesem Fenster auf.

Lösungen zu den Übungen

1. Öffnen Sie das zweite Dialogfeld im Dialog-Editor. Öffnen Sie das Eigenschaftsdialogfeld für das Fenster. Gehen Sie auf die Registerkarte Formate. Wählen Sie im Dropdown-Listenfeld Rand den Eintrag Größe ändern. Öffnen Sie den Klassen-Assistenten, und fügen Sie eine Behandlungsroutine für die Nachricht WM_SIZE hinzu. In der eben erzeugten Funktion rufen Sie die Funktion Invalidate auf, wie es Listing B.7 zeigt.

Listing B.7: Die Funktion OnSize

1: void CPaintDlg::OnSize(UINT nType, int cx, int cy)
2: {
3: CDialog::OnSize(nType, cx, cy);
4:
5: // TODO: Code für die Behandlungsroutine für Nachrichten hier einfügen
6: // Fenster neu zeichnen
7: Invalidate();
8:}

2. Gehen Sie im Arbeitsbereich auf die Registerkarte Ressourcen. Klicken Sie mit der rechten Maustaste auf den obersten Ordner des Ressourcenbaumes. Aus dem Kontextmenü wählen Sie den Befehl Einfügen. Im Dialogfeld Ressource einfügen markieren Sie in der Liste Ressourcentyp den Eintrag Bitmap. Zeichnen Sie ein Muster in das gerade erstellte Bitmap. Klicken Sie mit der rechten Maustaste auf die Bitmap-ID im Arbeitsbereich. Öffnen Sie den Eigenschaftsdialog, und ändern Sie die Objekt-ID in IDB_BITMAPBRUSH. Öffnen Sie den Quellcode für die Funktion DrawRegion. Fügen Sie in diese Funktion die Zeilen 22 bis 24 und 105 bis 112 von Listing B.8 ein. Vergrößern Sie die Anzahl der Schleifen in der for- Anweisung in Zeile 39.

Listing B.8: Die Funktion DrawRegion

1: void CPaintDlg::DrawRegion(CPaintDC *pdc, int iColor, int iTool, int iShape)
2: {
3: // Stifte deklarieren und erzeugen
.
.
.
19: CBrush lVertBrush(HS_VERTICAL, m_crColors[iColor]);
20: CBrush lNullBrush(RGB(192, 192, 192));
21:
22: CBitmap lBitmap;
23: lBitmap.LoadBitmap(IDB_BITMAPBRUSH);
24: CBrush lBitmapBrush(&lBitmap);
25:
26: // Größe der Zeichenbereiche berechnen
27: CRect lRect;
28: GetClientRect(lRect);
.
.
.
37: int i;
38: // Schleife durch alle Pinsel und Stifte
39: for (i = 0; i < 8; i++)
40: {
41: switch (i)
42: {
.
.
.
103: pdc->SelectObject(&lVertBrush);
104: break;
105: case 7: // Null - Bitmap
106: // Position für diese Figur bestimmen.
107: lDrawRect.left = lDrawRect.left + liHorz;
108: lDrawRect.right = lDrawRect.left + liWidth;
109: // Passenden Stift und Pinsel auswählen
110: pdc->SelectObject(&lNullPen);
111: pdc->SelectObject(&lBitmapBrush);
112: break;
113: }
114: // Welches Werkzeug wird verwendet?
.
.
.
126: pdc->SelectObject(lOldBrush);
127: pdc->SelectObject(lOldPen);
128:}

Antworten für Tag 9

Antworten zu den Kontrollfragen

1. Über die IDispatch-Schnittstelle kann der Container die Methode Invoke aufrufen. An diese Methode übergibt er die DISPID derjenigen Steuerelementmethode, die der Container aufrufen möchte.

2. Die Containeranwendung hat ihre eigene IDispatch-Schnittstelle, über die das Steuerelement Ereignisse auslösen kann.

3. Schalten Sie im zweiten Dialogfeld des MFC-Anwendungs-Assistenten das Kontrollkästchen ActiveX-Steuerelemente ein.

4. Visual C++ erzeugt C++-Klassen, die die Funktionalität des Steuerelements verkapseln.

5. Ältere Steuerelemente liefern möglicherweise nicht die Informationen, die Visual C++ braucht, um C++-Klassen zu generieren und damit die Funktionalität des Steuerelements zu verkapseln.

Lösung zur Übung

Fügen Sie mit dem Klassen-Assistenten eine Funktion für die Nachricht DblClick des Grid-Steuerelements (IDC_MSFGRID) hinzu. In die Funktion schreiben Sie den folgenden Code:

void CActiveXDlg::OnDblClickMsfgrid()
{
// TODO: Code für die Behandlungsroutine der Steuerelement-Benachrichtigung hier einfügen

///////////////////////
// EIGENER CODE, ANFANG
///////////////////////

// Hat Benutzer auf eine Datenzeile und nicht die
// Kopfzeile geklickt?
if (m_ctlFGrid.GetMouseRow() != 0)
{
// Wenn ja, dann Spaltenvariable auf 0 setzen
// und beenden
m_iMouseCol = 0;
return;
}
// Angeklickte Spalte sichern
m_iMouseCol = m_ctlFGrid.GetMouseCol();
// Wenn es die erste Spalte war, dann
// ist nichts zu tun.
if (m_iMouseCol == 0)
return;
// Neuzeichnen des Steuerelements abschalten
m_ctlFGrid.SetRedraw(FALSE);
// Gewählte Spaltenposition ändern
m_ctlFGrid.SetColPosition(m_iMouseCol, 0);
// Tabelle neu sortieren
DoSort();
// Neuzeichnen wieder einschalten
m_ctlFGrid.SetRedraw(TRUE);

///////////////////////
// EIGENER CODE, ENDE
///////////////////////

Antworten für Tag 10

Antworten zu den Kontrollfragen

1. Single Document Interface - Einzeldokumentschnittstelle.

2. Die Ansichtsklasse ist für die Anzeige des Dokuments verantwortlich.

3. Um das Dokument neu zu zeichnen, wird die Funktion OnDraw in der Ansichtsklasse aufgerufen.

4. Um das aktuelle Dokument zu löschen, schreibt man den entsprechenden Code in die Funktion DeleteContents der Dokumentklasse.

5. Die Dokumentklasse verwaltet und manipuliert die Daten. Sie verwaltet die abstrakte Repräsentation des zu bearbeitenden Dokuments.

Lösung zur Übung

Führen Sie die folgenden Schritte aus:

1. Markieren Sie im Arbeitsbereich auf der Registerkarte Klassen die Klasse CLine. Klicken Sie mit der rechten Maustaste, und wählen Sie aus dem Kontextmenü den Befehl Member-Variable hinzufügen.

2. Legen Sie den Variablentyp mit UINT, die Deklaration mit m_nWidth und den Zugriffsstatus mit Privat fest. Klicken Sie auf OK, um die Variable hinzuzufügen.

3. Klicken Sie mit der rechten Maustaste im Baum der Registerkarte Klassen auf den Konstruktor von CLine. Wählen Sie aus dem Kontextmenü den Befehl Gehe zu Deklaration.

4. Fügen Sie UINT nWidth als viertes Argument in die Konstruktordeklaration ein.

5. Klicken Sie mit der rechten Maustaste im Baum der Registerkarte Klassen auf den Konstruktor von CLine. Wählen Sie aus dem Kontextmenü den Befehl Gehe zu Definition.

6. Fügen Sie im Konstruktor das vierte Argument hinzu, und setzen Sie das Element m_nWidth auf das neue Argument, wie es Listing B.9 zeigt.

Listing B.9: Der modifizierte Konstruktor von CLine

1: CLine::CLine(CPoint ptFrom, CPoint ptTo, COLORREF crColor, UINT nWidth)
2: {
3: // Anfangs- und Endpunkte initialisieren
4: m_ptFrom = ptFrom;
5: m_ptTo = ptTo;
6: m_crColor = crColor;
7: m_nWidth = nWidth;
8: }

7. Gehen Sie nach unten zur Funktion Draw, und nehmen Sie die Änderungen gemäß Listing B.10 vor.

Listing B.10: Die modifizierte Funktion Draw

1: void CLine::Draw(CDC * pDC)
2: {
3: // Stift erzeugen
4: CPen lpen (PS_SOLID, m_nWidth, m_crColor);
5:
6: // Neuen Stift als Zeichenobjekt festlegen
7: CPen* pOldPen = pDC->SelectObject(&lpen);
8: // Linie zeichnen
9: pDC->MoveTo(m_ptFrom);
10: pDC->LineTo(m_ptTo);
11: // Vorherigen Stift zurücksetzen
12: pDC->SelectObject(pOldPen);
13: }

8. Gehen Sie nach unten zur Funktion Serialize, und modifizieren Sie die Funktion gemäß Listing B.11.

Listing B.11: Die modifizierte Funktion Serialize

1: void CLine::Serialize(CArchive &ar)
2: {
3: CObject::Serialize(ar);
4:
5: if (ar.IsStoring())
6: ar << m_ptFrom << m_ptTo << (DWORD) m_crColor << m_nWidth;
7: else
8: ar >> m_ptFrom >> m_ptTo >> (DWORD) m_crColor >> m_nWidth;
9: }

9. Markieren Sie im Arbeitsbereich auf der Registerkarte Klassen die Klasse CTag10Doc. Klicken Sie mit der rechten Maustaste, und wählen Sie aus dem Kontextmenü den Befehl Member-Variable hinzufügen.

10. Legen Sie den Variablentyp mit UINT, die Deklaration mit m_nWidth und den Zugriffsstatus als Privat fest. Klicken Sie auf OK, um die Variable hinzuzufügen.

11. Öffnen Sie den Quellcode von CTag10Doc (Tag10.Doc.cpp). Gehen Sie nach unten zur Funktion OnNewDocument, und bearbeiten Sie die Funktion entsprechend Listing B.12.

Listing B.12: Die modifizierte Funktion OnNewDocument

1: BOOL CTag10Doc::OnNewDocument()
2: {
3: if (!CDocument::OnNewDocument())
4: return FALSE;
5:
6: // ZU ERLEDIGEN: Hier Code zur Reinitialisierung einfügen
7: // (SDI-Dokumente verwenden dieses Dokument)
8:
9: ///////////////////////
10: // EIGENER CODE, ANFANG
11: ///////////////////////
12:
13: // Farbe mit Schwarz initialisieren
14: m_nColor = ID_COLOR_BLACK - ID_COLOR_BLACK;
15: // Breite mit Dünn initialisieren
16: m_nWidth = ID_WIDTH_VTHIN - ID_WIDTH_VTHIN;
17:
18: ///////////////////////
19: // EIGENER CODE, ENDE
20: ///////////////////////
21:
22: return TRUE;
23: }

12. Gehen Sie nach unten zur Funktion AddLine, und nehmen Sie die Änderungen gemäß Listing B.13 vor.

Listing B.13: Die modifizierte Funktion AddLine

1: CLine * CTag10Doc::AddLine(CPoint ptFrom, CPoint ptTo)
2: {
3: static UINT nWidths[5] = { 1, 8, 16, 24, 32};
4:
5: // Ein neues CLine-Objekt erzeugen
6: CLine *pLine = new CLine(ptFrom, ptTo, m_crColors[m_nColor], nWidths[m_nWidth]);
7: try
8: {
9: // Die neue Linie in das Objektarray einfügen
10: m_oaLines.Add(pLine);
11: // Dokument als bearbeitet markieren
12: SetModifiedFlag();
13: }
14: // Ist Speicherausnahme aufgetreten?
15: catch (CMemoryException* perr)
16: {
17: // Meldung für Benutzer, schlechte Neuigkeiten
18: // mitteilen
19: AfxMessageBox("Speichermangel", MB_ICONSTOP | MB_OK);
20: // Wurde Linienobjekt erzeugt?
21: if (pLine)
22: {
23: // Objekt löschen
24: delete pLine;
25: pLine = NULL;
26: }
27: // Ausnahmeobjekt löschen
28: perr->Delete();
29: }
30: return pLine;
31: }

13. Fügen Sie eine neue Member-Funktion in die Klasse CTag10Doc ein. Legen Sie den Funktionstyp als UINT, die Deklaration als GetWidth und den Zugriffsstatus als Public fest.

14. Fügen Sie den Code aus Listing B.14 in die Funktion GetWidth ein.

Listing B.14: Die Funktion GetWidth

1: UINT CTag10Doc::GetWidth()
2: {
3: // Aktuelle Breite zurückgeben
4: return ID_WIDTH_VTHIN + m_nWidth;
5: }

15. Gehen Sie im Arbeitsbereich auf die Registerkarte Ressourcen. Erweitern Sie den Baum, so daß Sie den Inhalt des Ordners Menu sehen. Doppelklicken Sie auf die Menüressource.

16. Ziehen Sie den leeren Menübefehl der obersten Ebene (am rechten Ende der Menüleiste) nach links, und legen Sie ihn vor dem Menü Ansicht ab.

17. Öffnen Sie den Eigenschaftsdialog für den leeren Menübefehl. Legen Sie den Titel mit Brei&te fest. Schließen Sie das Eigenschaftsdialogfeld.

18. Fügen Sie die Menübefehle für das Menü Breite in der Reihenfolge und mit den Eigenschaften gemäß Tabelle B.1 hinzu.

Tabelle B.1: Einstellungen der Menüeigenschaften

Objekt

Eigenschaft

Einstellung

Menübefehl

ID

Titel

ID_WIDTH_VTHIN

Sehr d&ünn

Menübefehl

ID

Titel

ID_WIDTH_THIN

&Dünn

Menübefehl

ID

Titel

ID_WIDTH_MEDIUM

&Mittel

Menübefehl

ID

Titel

ID_WIDTH_THICK

D&ick

Menübefehl

ID

Titel

ID_WIDTH_VTHICK

&Sehr dick

19. Öffnen Sie den Klassen-Assistenten. Wählen Sie CTag10Doc im Kombinationsfeld Klassenname.

20. Fügen Sie Funktionen für die Nachrichten COMMAND und UPDATE_COMMAND_UI für alle Menübefehle des Menüs Breite hinzu.

21. Nachdem Sie den letzten Menübefehl hinzugefügt haben, klicken Sie auf die Schaltfläche Code bearbeiten.

22. Bearbeiten Sie die Funktionen des Menübefehls Sehr dünn entsprechend Listing B.15.

Listing B.15: Die Funktionen des Menübefehls Sehr dünn

1: void CTag10Doc::OnWidthVthin()
2: {
3: // TODO: Code für Befehlsbehandlungsroutine hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Aktuelle Breite auf Sehr dünn setzen
10: m_nWidth = ID_WIDTH_VTHIN - ID_WIDTH_VTHIN;
11:
12: ///////////////////////
13: // EIGENER CODE, ENDE
14: ///////////////////////
15: }
16:
17: void CTag10Doc::OnUpdateWidthVthin(CCmdUI* pCmdUI)
18: {
19: // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren der Benutzeroberfläche hier einfügen
20:
21: ///////////////////////
22: // EIGENER CODE, ANFANG
23: ///////////////////////
24:
25: // Prüfen, ob Menübefehl Sehr dünn mit Kontrollhäkchen zu versehen ist
26: pCmdUI->SetCheck(GetWidth() == ID_WIDTH_VTHIN ? 1 : 0);
27:
28: ///////////////////////
29: // EIGENER CODE, ENDE
30: ///////////////////////
31: }

23. Bearbeiten Sie die Funktionen des Menübefehls Dünn entsprechend Listing B.16. Gehen Sie bei den restlichen Menüfunktionen in der gleichen Weise vor, wobei Sie die jeweiligen Menü-IDs anstelle von ID_WIDTH_THIN einsetzen.

Listing B.16: Die Funktionen des Menüs Dünn

1: void CTag10Doc::OnWidthThin()
2: {
3: // TODO: Code für Befehlsbehandlungsroutine hier einfügen
4:
5: ///////////////////////
6: // EIGENER CODE, ANFANG
7: ///////////////////////
8:
9: // Aktuelle Breite auf Dünn setzen
10: m_nWidth = ID_WIDTH_THIN - ID_WIDTH_VTHIN;
11:
12: ///////////////////////
13: // EIGENER CODE, ENDE
14: ///////////////////////
15: }
16:
17: void CTag10Doc::OnUpdateWidthThin(CCmdUI* pCmdUI)
18: {
19: // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren der Benutzeroberfläche hier einfügen
20:
21: ///////////////////////
22: // EIGENER CODE, ANFANG
23: ///////////////////////
24:
25: // Prüfen, ob Menübefehl Dünn mit Kontrollhäkchen zu versehen ist
26: pCmdUI->SetCheck(GetWidth() == ID_WIDTH_THIN ? 1 : 0);
27:
28: ///////////////////////
29: // MY CODE ENDS HERE
30: ///////////////////////
31: }

Antworten für Tag 11

Antworten zu den Kontrollfragen

1. Die fünf Basisklassen in MDI-Anwendungen sind die von CWinApp abgeleitete Klasse, die von CMDIFrameWnd abgeleitete Klasse, die von CMDIChildWnd abgeleitete Klasse, die von CDocument abgeleitete Klasse und die von CView abgeleitete Klasse.

2. Der Klassen-Assistent versteht den Eintrag ON_COMMAND_RANGE in der Nachrichtenzuordnungstabelle nicht und wird ihn demzufolge entweder entfernen oder verfälschen.

3. Die ID der Nachricht.

4. WM_CONTEXTMENU.

Lösung zur Übung

Führen Sie die folgenden Schritte aus:

1. Fügen Sie den Behandlungscode für die Breite wie in der gestrigen Übung hinzu.

2. Fügen Sie die Menübefehle für das Menü Breite mit den gleichen Einstellungen wie gestern hinzu.

3. Öffnen Sie die Header-Datei Tag11Doc.h.

4. Gehen Sie in der Header-Datei nach unten bis zum geschützten Abschnitt, in dem die Nachrichtenzuordnungstabelle AFX_MSG deklariert ist (suchen Sie nach // {{AFX_MSG(CTag11Doc)).

5. Fügen Sie die Funktionsdeklarationen aus Listing B.17 vor der gesuchten Zeile ein. (Der gesuchte String markiert den Beginn der vom Klassen-Assistenten verwalteten Nachrichtenzuordnungstabelle. Alles zwischen dieser Markierung und der Endemarkierung //}}AFX_MSG wird vom Klassen-Assistenten möglicherweise entfernt oder verfälscht.)

Listing B.17: Die Deklarationen der Behandlungsroutinen in Tag11Doc.h

.
.
.
1: #ifdef _DEBUG
2: virtual void AssertValid() const;
3: virtual void Dump(CDumpContext& dc) const;
4: #endif
5:
6: protected:
7:
8: // Generierte Message-Map-Funktionen
9: protected:
10: afx_msg void OnColorCommand(UINT nID);
11: afx_msg void OnWidthCommand(UINT nID);
12: afx_msg void OnUpdateColorUI(CCmdUI* pCmdUI);
13: afx_msg void OnUpdateWidthUI(CCmdUI* pCmdUI);
14: //{{AFX_MSG(CTag11Doc)
15: ...
16: //}}AFX_MSG
17: DECLARE_MESSAGE_MAP()
18: private:
19: UINT m_nColor;
20: UINT m_nWidth;
21: CObArray m_oaLines;
22: };

6. Öffnen Sie die Quellcodedatei Tag11Doc.cpp.

7. Suchen Sie nach der Zeile BEGIN_MESSAGE_MAP, und fügen Sie unmittelbar danach die Zeilen aus Listing B.18 ein. Es ist wichtig, daß dieser Code zwischen der Zeile BEGIN_MESSAGE_MAP und der Zeile //{{AFX_MSG_MAP steht. Wenn Sie diese Befehle zwischen die Zeilen //{{AFX_MSG_MAP und //}}AFX_MSG_MAP schreiben, entfernt oder verstümmelt sie der Klassen-Assistent.

Listing B.18: Die Einträge der Behandlungsroutinen in der Nachrichtenzuordnungstabelle in Tag11Doc.cpp

1:///////////////////////////////////////////////////////////////////////
2: // CTag11Doc
3:
4: IMPLEMENT_DYNCREATE(CTag11Doc, CDocument)
5:
6: BEGIN_MESSAGE_MAP(CTag11Doc, CDocument)
7: ON_COMMAND_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnColorCommand)
8: ON_COMMAND_RANGE(ID_WIDTH_VTHIN, ID_WIDTH_VTHICK, OnWidthCommand)
9: ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_BLACK, ID_COLOR_WHITE, OnUpdateColorUI)
10: ON_UPDATE_COMMAND_UI_RANGE(ID_WIDTH_VTHIN, ID_WIDTH_VTHICK, OnUpdateWidthUI)
11: //{{AFX_MSG_MAP(CTag11Doc)
12: ...
13: //}}AFX_MSG_MAP
14: END_MESSAGE_MAP()
15:
16: const COLORREF CTag11Doc::m_crColors[8] = {
17: RGB( 0, 0, 0), // Schwarz
18: RGB( 0, 0, 255), // Blau
.
.
.

8. Gehen Sie ans Ende der Datei, und fügen Sie die beiden Behandlungsroutinen aus Listing B.19 ein.

Listing B.19: Die Behandlungsroutinen für die Nachrichten des Menüs Breite

1: void CTag11Doc::OnWidthCommand(UINT nID)
2: {
3: // Aktuelle Breite setzen
4: m_nWidth = nID - ID_WIDTH_VTHIN;
5: }
6:
7: void CTag11Doc::OnUpdateWidthUI(CCmdUI* pCmdUI)
8: {
9: // Prüfen, ob Menübefehl mit Kontrollhäkchen zu versehen ist
10: pCmdUI->SetCheck(GetWidth() == pCmdUI->m_nID ? 1 : 0);
11: }

9. Öffnen Sie IDR_CONTEXTMENU im Menü-Editor.

10. Fügen Sie in das überlappende Menü Breite die Menübefehle genau wie beim Menü IDR_TAG11TYPE und mit den gleichen Eigenschaftseinstellungen hinzu. Die ID können Sie aus der Dropdown-Liste der IDs auswählen, wenn Sie lieber danach suchen als sie einzutippen.

Antworten für Tag 12

Antworten zu den Kontrollfragen

1. Vergeben Sie für die Schaltfläche der Symbolleiste dieselbe Objekt-ID wie für den Menübefehl.

2. Bei beiden muß das Andocken auf den gleichen Seiten in der Funktion OnCreate der Rahmenklasse aktiviert sein (mit Hilfe der Funktion EnableDocking).

3. Löschen Sie den Eintrag ID_INDICATOR_NUM aus der Tabelle für die Statusleistenanzeige am Anfang der Quellcodedatei des Hauptrahmens.

4. In die Symbolleiste ist eine Trennlinie als Platzhalter einzufügen. Der Symbolleisten-Editor hindert Sie in gut gemeinter Absicht daran, Trennlinien einzufügen, weil er hier einen Fehler vermutet.

Lösungen zu den Übungen

1. Fügen Sie einen Eintrag in die Zeichenfolgentabelle mit der ID ID_INDICATOR_WIDTH und dem Titel SEHR DICK ein.

Nehmen Sie einen weiteren Eintrag in die Tabelle der Statusleistenanzeige am Anfang der Datei CMainFrame.cpp auf:

static UINT indicators[] =
{
ID_SEPARATOR, // Statusleistenanzeige
ID_INDICATOR_WIDTH,
ID_INDICATOR_COLOR,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};

Fügen Sie der Klasse CToolbarDoc eine neue Member-Funktion hinzu. Legen Sie den Funktionstyp als afx_msg void, die Funktionsdeklaration als OnUpdateIndicatorWidth (CCmdUI *pCmdUI) und den Zugriffsstatus als Protected fest. In die Funktion übernehmen Sie den folgenden Code:

void CToolbarDoc::OnUpdateIndicatorWidth(CCmdUI *pCmdUI)
{
CString strWidth;

// Aktuelle Breite?
switch (m_nWidth)
{
case 0: // Sehr dünn
strWidth = "SEHR DÜNN";
break;
case 1: // Dünn
strWidth = "DÜNN";
break;
case 2: // Mittel
strWidth = "MITTEL";
break;
case 3: // Dick
strWidth = "DICK";
break;
case 4: // Sehr dick
strWidth = "SEHR DICK";
break;
}
// Statusleistenausschnitt aktivieren
pCmdUI->Enable(TRUE);
// Test des Statusleistenausschnitts auf aktuelle
// Breite festlegen
pCmdUI->SetText(strWidth);
}

Fügen Sie der Nachrichtenzuordnungstabelle von CToolbarDoc einen Eintrag für die Behandlungsroutine der Nachricht ON_UPDATE_COMMAND_UI wie folgt hinzu:

/////////////////////////////////////////////////////////////////////////////
// CToolbarDoc

IMPLEMENT_DYNCREATE(CToolbarDoc, CDocument)

BEGIN_MESSAGE_MAP(CToolbarDoc, CDocument)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_WIDTH, OnUpdateIndicatorWidth)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_COLOR, OnUpdateIndicatorColor)
//{{AFX_MSG_MAP(CToolbarDoc)
ON_COMMAND(ID_COLOR_BLACK, OnColorBlack)
ON_UPDATE_COMMAND_UI(ID_COLOR_BLACK, OnUpdateColorBlack)
...
ON_COMMAND(ID_WIDTH_VTHIN, OnWidthVthin)
ON_UPDATE_COMMAND_UI(ID_WIDTH_VTHIN, OnUpdateWidthVthin)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

2. Öffnen Sie die Symbolleiste IDR_MAINFRAME im Symbolleisten-Editor. Zeichnen Sie ein Symbol für die leere Schaltfläche am Ende der Symbolleiste. Doppelklicken Sie auf die Schaltfläche, um das zugehörige Eigenschaftsdialogfeld zu öffnen. Legen Sie die ID mit ID_VIEW_COLORBAR fest, und geben Sie einen passenden Statuszeilentext ein. Kompilieren Sie die Anwendung erneut, und führen Sie sie aus. Auf der Hauptsymbolleiste sollte nun die Umschaltung der Farbsymbolleiste funktionieren.

Antworten für Tag 13

Antworten zu den Kontrollfragen

1. DECLARE_SERIAL und IMPLEMENT_SERIAL.

2. Rufen Sie dazu die Funktion IsStoring oder IsLoading auf.

3. Den Klassennamen, den Namen der Basisklasse und die Versionsnummer.

4. CFormView.

5. CFile.

Lösung zur Übung

Im Dialog-Editor fügen Sie zwei Optionsfelder und das Textfeld mit der Aufforderung hinzu. Legen Sie die Eigenschaften der Steuerelemente gemäß Tabelle B.2 fest.

Tabelle B.2: Einstellungen der Steuerelementeigenschaften

Objekt

Eigenschaft

Einstellung

Text

ID

Titel

IDC_STATIC

Geschlecht:

Optionsfeld

ID

Titel

Gruppe

IDC_RMALE

&männlich

eingeschaltet

Optionsfeld

ID

Titel

IDC_RFEMALE

wie&blich

Verbinden Sie mit den neuen Optionsfeldern eine Variable laut Tabelle B.3.

Tabelle B.3: Variable der Steuerelemente

Objekt

Name

Kategorie

Typ

IDC_RMALE

m_iSex

Wert

int

Inkrementieren Sie die Versionsnummer im Makro IMPLEMENT_SERIAL in der Klasse CPerson. Fügen Sie eine neue Member-Variable in die Klasse CPerson ein. Legen Sie den Typ als int, den Namen als m_iSex und den Zugriffsstatus als Privat fest. Aktualisieren Sie die Konstruktorfunktion von CPerson, indem Sie die Variable m_iSex in die Initialisierungen wie in Zeile 8 von Listing B.20 einfügen.

Listing B.20: Der modifizierte Konstruktor von CPerson

1: CPerson::CPerson()
2: {
3: // Klassenvariablen initialisieren
4: m_iMaritalStatus = 0;
5: m_iAge = 0;
6: m_bEmployed = FALSE;
7: m_sName = "";
8: m_iSex = 0;
9: }

Fügen Sie entsprechend der Zeilen 10 und 16 von Listing B.21 die Inline-Funktionen in die Klassendeklaration von CPerson ein, um den Wert der neuen Variablen zu setzen und abzurufen.

Listing B.21: Die modifizierte Deklaration der Klasse CPerson

1: class CPerson : public CObject
2: {
3: DECLARE_SERIAL (CPerson)
4: public:
5: virtual void Serialize(CArchive &ar);
6: // Funktionen zum Setzen der Variablen
7: void SetEmployed(BOOL bEmployed) { m_bEmployed = bEmployed;}
8: void SetMaritalStat(int iStat) { m_iMaritalStatus = iStat;}
9: void SetAge(int iAge) { m_iAge = iAge;}
10: void SetSex(int iSex) { m_iSex = iSex; }
11: void SetName(CString sName) { m_sName = sName;}
12: // Funktionen zum Holen der aktuellen Variableneinstellungen
13: BOOL GetEmployed() { return m_bEmployed;}
14: int GetMaritalStatus() { return m_iMaritalStatus;}
15: int GetAge() {return m_iAge;}
16: int GetSex() {return m_iSex;}
17: CString GetName() {return m_sName;}
18: CPerson();
19: virtual ~CPerson();
20:
21: private:
22: int m_iSex;
23: int m_iMaritalStatus;
24: CString m_sName;
25: int m_iAge;
26: BOOL m_bEmployed;
27: };

Aktualisieren Sie die Funktion Serialize in der Klasse CPerson, um die Variable m_iSex entsprechend der Zeilen 9 und 12 von Listing B.22 einzubinden.

Listing B.22: Die modifizierte Funktion Serialize der Klasse CPerson

1: void CPerson::Serialize(CArchive &ar)
2: {
3: // Funktion der Basisklasse aufrufen
4: CObject::Serialize(ar);
5:
6: // Wird geschrieben?
7: if (ar.IsStoring())
8: // Alle Variablen in der richtigen Reihenfolge schreiben
9: ar << m_sName << m_iAge << m_iMaritalStatus << m_bEmployed <<m_iSex;
10: else
11: // Alle Variablen in der richtigen Reihenfolge lesen
12: ar >> m_sName >> m_iAge >> m_iMaritalStatus >> m_bEmployed >>m_iSex;
13:
14: }

Modifizieren Sie die Funktion PopulateView im Ansichtsobjekt, um die Variable m_iSex in den Datenaustausch aufzunehmen, wie es Zeile 19 von Listing B.23 zeigt.

Listing B.23: Die modifizierte Funktion PopulateView der Klasse CSerializeView

1: void CSerializeView::PopulateView()
2: {
3: // Einen Zeiger auf das aktuelle Dokument holen
4: CSerializeDoc* pDoc = GetDocument();
5: if (pDoc)
6: {
7: // Aktuelle Datensatzposition in der Menge anzeigen
8: m_sPosition.Format("Datensatz %d von %d", pDoc->GetCurRecordNbr(),
9: pDoc->GetTotalRecords());
10: }
11: // Ist Datensatzobjekt gültig?
12: if (m_pCurPerson)
13: {
14: // Ja, alle Werte des Datensatzes holen
15: m_bEmployed = m_pCurPerson->GetEmployed();
16: m_iAge = m_pCurPerson->GetAge();
17: m_sName = m_pCurPerson->GetName();
18: m_iMaritalStatus = m_pCurPerson->GetMaritalStatus();
19: m_iSex = m_pCurPerson->GetSex();
20: }
21: // Anzeige aktualisieren
22: UpdateData(FALSE);
23: }

Fügen Sie eine Behandlungsroutine für das Klickereignis der beiden neuen Optionsfelder hinzu. Nutzen Sie für beide Behandlungsroutinen dieselbe Funktion. Aktualisieren Sie das Feld des Datensatzobjekts mittels der Set-Funktion wie in Listing B.24.

Listing B.24: Die Funktion OnSex der Klasse CSerializeView

1: void CSerializeView::OnSex()
2: {
3: // TODO: Code für die Behandlungsroutine der Steuerelement- Benachrichtigung hier einfügen
4:
5: // Daten im Formular mit Variablen synchronisieren
6: UpdateData(TRUE);
7: // Wenn gültiges Person-Objekt vorhanden, Daten an das Objekt übergeben
8: if (m_pCurPerson)
9: m_pCurPerson->SetSex(m_iSex);
10: // Zeiger auf das Dokument holen
11: CSerialDoc * pDoc = GetDocument();
12: if (pDoc)
13: // Veränderungsflag im Dokument setzen
14: pDoc->SetModifiedFlag();
15: }

Antworten für Tag 14

Antworten zu den Kontrollfragen

1. Open Database Connectivity, d.h. eine offene, herstellerunabhängige Schnittstelle für den Zugriff auf Datenbanken.

2. Move, MoveNext, MovePrev, MoveFirst, MoveLast und SetAbsolutePosition.

3. CRecordView.

4. AddNew, Update und Requery.

5. Edit

Lösung zur Übung

1. Erstellen Sie ein neues Dialogfeld mit dem in Abbildung B.1 gezeigten Layout. Konfigurieren Sie die Steuerelemente nach Tabelle B.4.

Abbildung B.1:
Das Dialogfeld Gehe zu

Tabelle B.4: Einstellungen der Dialogfeldeigenschaften

Objekt

Eigenschaft

Einstellung

Text

ID

Titel

IDC_STATIC

Gehe zu Datensatz:

Eingabefeld

ID

IDC_ERECNBR

2. Öffnen Sie den Klassen-Assistenten. Erzeugen Sie eine neue Klasse für das neue Dialogfeld. Nennen Sie die neue Klasse CMoveToDlg. Fügen Sie dann eine Variable für das Eingabefeld hinzu. Legen Sie den Variablentyp als long und den Namen als m_lRowNbr fest.

3. Fügen Sie einen weiteren Menübefehl in das Hauptmenü der Anwendung ein. Legen Sie die Menüeigenschaften gemäß Tabelle B.5 fest.

Tabelle B.5: Eigenschaften des neuen Menübefehls

Objekt

Eigenschaft

Einstellung

Menübefehl

ID

Titel

Statuszeilentext

IDM_RECORD_MOVE

&Gehe zu ...

Geht zu einem bestimmten Datensatz\nGehe zu

4. Öffnen Sie den Klassen-Assistenten, und fügen Sie eine Behandlungsfunktion für die Nachricht COMMAND des neuen Menübefehls in die Ansichtsklasse ein. In die Funktion übernehmen Sie den Code aus Listing B.25.

Listing B.25: Die Funktion OnRecordMove der Klasse CDbOdbcView

1: void CDbOdbcView::OnRecordMove()
2: {
3: // TODO: Code für Befehlsbehandlungsroutine hier einfügen
4: // Instanz des Dialogfelds 'Gehe zu' erzeugen
5: CMoveToDlg dlgMoveTo;
6: // Zeilennummer des Ziels ermitteln
7: if (dlgMoveTo.DoModal() == IDOK)
8: {
9: // Zeiger auf den Recordset holen
10: CRecordset* pSet = OnGetRecordset();
11: // Sicherstellen, daß alle Änderungen am aktuellen Datensatz gespeichert wurden.
12: if (pSet->CanUpdate() && !pSet->IsDeleted())
13: {
14: pSet->Edit();
15: if (!UpdateData())
16: return;
17:
18: pSet->Update();
19: }
20: // Neue Position einstellen
21: pSet->SetAbsolutePosition(dlgMoveTo.m_lRowNbr);
22: // Formular aktualisieren
23: UpdateData(FALSE);
24: }
25: }

5. Binden Sie die Header-Datei für das neue Dialogfeld in den Quellcode der Ansichtsklasse ein, wie es aus Zeile 10 von Listing B.26 hervorgeht.

Listing B.26: Die Include-Anweisungen der Klasse CDbOdbcView

1: // DbOdbcView.cpp : Implementierung der Klasse CDbOdbcView
2: //
3:
4: #include "stdafx.h"
5: #include "DbOdbc.h"
6:
7: #include "DbOdbcSet.h"
8: #include "DbOdbcDoc.h"
9: #include "DbOdbcView.h"
10: #include "MoveToDlg.h"

6. Fügen Sie eine Symbolleistenschaltfläche für den neuen Menübefehl ein.

Antworten für Tag 15

Antworten zu den Kontrollfragen

1. ActiveX Data Objects.

2. OLE DB.

3. Connection, Command, Parameter, Error, Recordset und Field.

4. ::CoInitialize(NULL);

5. pCmd->ActiveConnection = pConn;

6. Die erste Möglichkeit:

_RecordsetPtr pRs;
pRs = pCmd->Execute();

Die zweite:

_RecordsetPtr pRs;
pRs.CreateInstance(__uuidof(Recordset));
pRs->PutRefSource(pCmd);

Lösung zur Übung

Fügen Sie der Dokumentklasse Behandlungsfunktionen für die Nachricht UPDATE_COMMAND_UI der Befehle des Navigationsmenüs hinzu. In die Funktionen für die Menübefehle Erster und Vorheriger schreiben Sie den Code aus Listing B.27, für die Menübefehle Letzter und Nächster den Code aus Listing B.28.

Listing B.27: Die Funktion OnUpdateDataFirst der Klasse CDbAdoDoc

1: void CDbAdoDoc::OnUpdateDataFirst(CCmdUI* pCmdUI)
2: {
3: // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren der Benutzeroberfläche hier einfügen
4: // Existiert der Datensatz?
5: if (m_pRs)
6: {
7: // Am Anfang der Datei (BOF) angelangt?
8: if (m_pRs->BOF)
9: pCmdUI->Enable(FALSE);
10: else
11: pCmdUI->Enable(TRUE);
12: }
13: }

Listing B.28: Die Funktion OnUpdateDataLast der Klasse CDbAdoDoc

1: void CDbAdoDoc::OnUpdateDataLast(CCmdUI* pCmdUI)
2: {
3: // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren der Benutzeroberfläche hier einfügen
4: // Existiert der Datensatz?
5: if (m_pRs)
6: {
7: // Am Ende der Datei (EOF) angelangt?
8: if (m_pRs->EndOfFile)
9: pCmdUI->Enable(FALSE);
10: else
11: pCmdUI->Enable(TRUE);
12: }
13: }

Antworten für Tag 16

Antworten zu den Kontrollfragen

1. Wenn man eine neue Klasse erstellen muß, die von einer existierenden MFC-Klasse abgeleitet ist.

2. Alle Anwendungen sind erneut zu linken.

3. MFC, Allgemein und Formular.

4. Die LIB-Bibliotheksdatei und die Header-Dateien für die Objekte im Modul.

5. Kapselung und Vererbung. Das dritte Prinzip ist der Polymorphismus, der in der heutigen Lektion nicht behandelt wurde.

Lösung zur Übung

1. Erstellen Sie ein neues Projekt. Wählen Sie dazu im Dialogfeld Neu auf der Registerkarte Projekte den Eintrag Win32-Bibliothek (statische). Tragen Sie einen Projektnamen wie etwa Line ein.

2. Aktivieren Sie die Unterstützung für MFC und vorkompilierte Header.

3. Kopieren Sie die Dateien Line.cpp und Line.h in das Projektverzeichnis. Fügen Sie beide Dateien dem Projekt hinzu. Kompilieren Sie das Bibliotheksmodul.

4. Öffnen Sie das originale Projekt des Bibliotheksmoduls. Löschen Sie die Dateien Line.cpp und Line.h aus dem Projekt. Fügen Sie am Beginn der Quellcodedatei für das Zeichenobjekt die Header-Datei Line.h aus dem Verzeichnis des Projekts Line ein, wie es Zeile 9 von Listing B.29 zeigt. Kompilieren Sie das Projekt neu.

Listing B.29: Die Include-Anweisungen und die Farbtabelle der Klasse CModArt

1: // ModArt.cpp: Implementierung der Klasse CModArt.
2: //
3: //////////////////////////////////////////////////////////////////////
4:
5: #include <stdlib.h>
6: #include <time.h>
7:
8: #include "stdafx.h"
9: #include "..\Line\Line.h"
10: #include "ModArt.h"

5. Öffnen Sie das Projekt der Testanwendung. Fügen Sie die Bibliotheksdatei Line in das Projekt ein. Erstellen Sie das Projekt.

Antworten für Tag 17

Antworten zu den Kontrollfragen

1. Eine erweiterte MFC-DLL.

2. Das Makro AFX_EXT_CLASS in der Klassendeklaration.

3. Eine Standard-DLL.

4. Normalerweise nicht. Allerdings müssen Sie die Anwendungen, die auf die DLL zurückgreifen, neu kompilieren, wenn Sie in der exportierten Schnittstelle der DLL Änderungen vorgenommen haben.

5. Die LIB-Datei enthält Funktionsrümpfe der Funktionen in der DLL und den erforderlichen Code, um den Funktionsaufruf zu lokalisieren und an die eigentliche Funktion in der DLL weiterzuleiten.

Lösungen zu den Übungen

1. Erstellen Sie ein neues Projekt. Wählen Sie dazu im Dialogfeld Neu auf der Registerkarte Projekte den Eintrag MFC-Anwendungs-Assistent (dll). Tragen Sie einen Projektnamen wie beispielsweise LineDll ein.

Wählen Sie im ersten Schritt des MFC-Anwendungs-Assistenten die Option Erweiterungs-MFC-DLL .

Nachdem Sie das Projektgerüst erstellt haben, kopieren Sie die Quellcode- und Header-Dateien für die Klasse Line in das Projektverzeichnis. Nehmen Sie diese Dateien in das Projekt auf.

Fügen Sie in die Deklaration der Klasse CLine das Makro AFX_EXT_CLASS hinzu.

Kompilieren Sie die DLL. Kopieren Sie die DLL in das Debug-Verzeichnis für die Testanwendung.

Öffnen Sie das Projekt der Standard-DLL. Löschen Sie über die Registerkarte Dateien des Arbeitsbereichs die Quellcode- und Header-Dateien der Klasse Line aus dem Projekt. Fügen Sie die Linien-DLL-LIB-Datei in das Projekt ein. Ändern Sie in der Quellcodedatei die Zeichenfunktionalität, indem Sie den Header der Linienklasse in der Version aus dem Projektverzeichnis der CLine-DLL einbinden, wie es Listing B.30 zeigt.

Listing B.30: Die Include-Anweisungen der Klasse CModArt

1: // ModArt.cpp: Implementierung der Klasse CModArt.
2: //
3: /////////////////////////////////////////////////////////////////////
4:
5: #include <stdlib.h>
6: #include <time.h>
7:
8: #include "stdafx.h"
9: #include "..\LineDll\Line.h"
10: #include "ModArt.h"

Kompilieren Sie das Projekt. Kopieren Sie die DLL in das Debug-Verzeichnis des Projekts für die Testanwendung.

Starten Sie die Testanwendung.

2. Öffnen Sie das Projekt der Linienklasse-DLL, das Sie in der vorherigen Übung erstellt haben. Ersetzen Sie im Konstruktor der Klasse die Initialisierung der Variablen m_nWidth durch einen konstanten Wert, wie es Listing B.31 zeigt.

Listing B.31: Der Konstruktor der Klasse CLine

1: CLine::CLine(CPoint ptFrom, CPoint ptTo, UINT nWidth, COLORREF crColor)
2: {
3: m_ptFrom = ptFrom;
4: m_ptTo = ptTo;
5: m_nWidth = 1;
6: m_crColor = crColor;
7: }

Kompilieren Sie die DLL. Kopieren Sie die DLL in das Debug-Verzeichnis des Projekts für die Testanwendung. Starten Sie die Testanwendung.

Antworten für Tag 18

Antworten zu den Kontrollfragen

1. Wenn die Anwendung im Leerlauf arbeitet und keine Nachrichten in der Nachrichtenwarteschlange stehen.

2. Die Rückgabe des Wertes TRUE bewirkt, daß die Funktion OnIdle wiederholt aufgerufen wird, solange die Anwendung im Leerlauf bleibt.

3. Eine OnIdle-Task wird nur ausgeführt, wenn die Anwendung im Leerlauf ist und keine Nachrichten in der Nachrichtenwarteschlange stehen. Ein Thread läuft dagegen vollkommen unabhängig von der übrigen Anwendung.

4. Kritische Abschnitte, Mutexe, Semaphoren und Ereignisse.

5. Die verbleibenden Threads und Prozesse, die auf dem Computer laufen, erhalten wesentlich weniger Prozessorzeit.

Lösungen zu den Übungen

1. Bearbeiten Sie die Funktion OnIdle gemäß Listing B.32.

Listing B.32: Die modifizierte Funktion OnIdle der Klasse CTaskingApp

1: BOOL CTaskingApp::OnIdle(LONG lCount)
2: {
3: // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
4:
5: // Leerlaufverarbeitung der Basisklasse aufrufen
6: BOOL bRtn = CWinApp::OnIdle(lCount);
7:
8: // Position der ersten Dokumentvorlage holen
9: POSITION pos = GetFirstDocTemplatePosition();
10: // Ist Position der Dokumentvorlage gültig?
11: if (pos)
12: {
13: // Zeiger auf die Dokumentvorlage ermitteln
14: CDocTemplate* pDocTemp = GetNextDocTemplate(pos);
15: // Ist Zeiger gültig?
16: if (pDocTemp)
17: {
18: // Position des ersten Dokuments holen
19: POSITION dPos = pDocTemp->GetFirstDocPosition();
20: // Ist Position des Dokuments gültig?
21: if (dPos)
22: {
23: // Zeiger auf das Dokument ermitteln
24: CTaskingDoc* pDocWnd =
25: (CTaskingDoc*)pDocTemp->GetNextDoc(dPos);
26: // Ist Zeiger gültig?
27: if (pDocWnd)
28: {
29: // Position der Ansicht ermitteln
30: POSITION vPos = pDocWnd->GetFirstViewPosition();
31: // Ist Ansichtsposition gültig?
32: if (vPos)
33: {
34: // Zeiger auf die Ansicht holen
35: CTaskingView* pView = (CTaskingView*)pDocWnd- >GetNextView(vPos);
36: // Ist Zeiger gültig?
37: if (pView)
38: {
39: // Fächer für ersten Leerlauf-Thread drehen?
40: if (pView->m_bOnIdle1)
41: {
42: // Ersten Leerlauf-Thread drehen
43: pDocWnd->DoSpin(0);
44: bRtn = TRUE;
45: }
46: // Zweiten Leerlauf-Thread drehen?
47: if (pView->m_bOnIdle2)
48: {
49: // Zweiten Leerlauf-Thread drehen
50: pDocWnd->DoSpin(2);
51: bRtn = TRUE;
52: }
53: }
54: }
55: }
56: }
57: }
58: }
59: return bRtn;
60: }

2. Beim Starten der unabhängigen Threads geben Sie einem Thread die Priorität THREAD_PRIORITY_NORMAL und dem anderen die Priorität THREAD_PRIORITY_LOWEST.

Bearbeiten Sie die Funktion SuspendSpinner entsprechend Listing B.33.

Listing B.33: Die modifizierte Funktion SuspendSpinner der Klasse CTaskingDoc

1: void CTaskingDoc::SuspendSpinner(int nIndex, BOOL bSuspend)
2: {
3: // Thread anhalten?
4: if (!bSuspend)
5: {
6: // Ist Zeiger für den Thread gültig?
7: if (m_pSpinThread[nIndex])
8: {
9: // Handle für den Thread holen
10: HANDLE hThread = m_pSpinThread[nIndex]->m_hThread;
11: // Auf Absterben des Threads warten
12: ::WaitForSingleObject (hThread, INFINITE);
13: }
14: }
15: else // Thread laufen lassen
16: {
17: int iSpnr;
18: int iPriority;
19: // Welchen Fächer verwenden?
20: switch (nIndex)
21: {
22: case 0:
23: iSpnr = 1;
24: iPriority = THREAD_PRIORITY_NORMAL;
25: break;
26: case 1:
27: iSpnr = 3;
28: iPriority = THREAD_PRIORITY_LOWEST;
29: break;
30: }
31: // Thread starten, Zeiger auf Fächer übergeben
32: m_pSpinThread[nIndex] = AfxBeginThread(ThreadFunc,
33: (LPVOID)&m_cSpin[iSpnr], iPriority);
34: }
35: }

Antworten für Tag 19

Antworten zu den Kontrollfragen

1. Eigenschaften, Methoden und Ereignisse.

2. Um dem Benutzer die Möglichkeit zu geben, die Eigenschaften des Steuerelements festzulegen.

3. Ambient, erweitert, vordefiniert und benutzerdefiniert.

4. Die Schnittstellenparameter werden zu einer standardisierten, maschinenunabhängigen Struktur gepackt und über Prozeßgrenzen hinweg gesendet. Diesen Vorgang bezeichnet man als Marshaling.

5. Mit dem Testcontainer für ActiveX-Steuerelemente.

Lösungen zu den Übungen

1. Öffnen Sie den Klassen-Assistenten, und gehen Sie auf die Registerkarte Automatisierung . Klicken Sie auf die Schaltfläche Methode hinzufügen. Geben Sie einen Methodennamen wie etwa GenNewDrawing ein, und legen Sie den Rückgabetyp als void fest. Klicken Sie auf OK, um die Methode hinzuzufügen. In die Methode übernehmen Sie den Code aus Listing B.34.

Listing B.34: Die Funktion GenNewDrawing der Klasse CSquiggleCtrl

1: void CSquiggleCtrl:: GenNewDrawing()
2: {
3: // ZU ERLEDIGEN: Fügen Sie hier den Code für Ihre Dispatch- Behandlungsroutine ein
4: // Flag setzen, so daß neue Zeichnung generiert wird
5: m_bGenNewDrawing = TRUE;
6: // Steuerelement ungültig machen, um Funktion OnDraw auszulösen
7: Invalidate();
8: }

2. Öffnen Sie den Klassen-Assistenten, und gehen Sie auf die Registerkarte Automatisierung . Klicken Sie auf die Schaltfläche Methode hinzufügen. Geben Sie einen Methodennamen wie SaveDrawing ein, und legen Sie den Rückgabetyp als BOOL fest. Nehmen Sie einen einzelnen Parameter, sFileName, mit dem Typ LPCSTR auf. Klicken Sie auf OK, um die Methode hinzuzufügen. In die Methode schreiben Sie den Code aus Listing B.35.

Listing B.35: Die Funktion SaveDrawing der Klasse CSquiggleCtrl

1: BOOL CSquiggleCtrl::SaveDrawing(LPCTSTR sFileName)
2: {
3: // ZU ERLEDIGEN: Fügen Sie hier den Code für Ihre Dispatch- Behandlungsroutine ein
4: try
5: {
6: // Ein CFile-Objekt erzeugen
7: CFile lFile(sFileName, CFile::modeWrite);
8: // CArchive-Objekt erzeugen, um die Datei zu speichern
9: CArchive lArchive(&lFile, CArchive::store);
10: // Datei speichern
11: m_maDrawing.Serialize(lArchive);
12: }
13: catch (CFileException err)
14: {
15: return FALSE;
16: }
17: return TRUE;
18: }

Antworten für Tag 20

Antworten zu den Kontrollfragen

1. Die Netzwerkadresse (oder den Namen) des Computers und den Anschluß, den der Server abhört.

2. Listen.

3. OnReceive.

4. OnConnect.

5. Send.

Lösung zur Übung

Führen Sie die folgenden Schritte aus:

1. Nehmen Sie eine Member-Variable in die Dialogfeldklasse (CSockDlg) auf. Legen Sie den Variablentyp als BOOL, den Namen als m_bConnected und den Zugriffsstatus als Privat fest.

2. Initialisieren Sie die Variable in der Funktion OnInitDialog als FALSE.

3. Setzen Sie die Variable in der Dialogfeldfunktion OnAccept auf TRUE, nachdem die Verbindung angenommen wurde.

4. Setzen Sie die Variable in der Dialogfeldfunktion OnClose auf FALSE.

5. Modifizieren Sie die Dialogfeldfunktion OnAccept gemäß Listing B.36.

Listing B.36: Die modifizierte Funktion OnAccept der Klasse CSockDlg

1: void CSockDlg::OnAccept()
2: {
3: if (m_bConnected)
4: {
5: // Ablehnungs-Socket erzeugen
6: CAsyncSocket sRjctSock;
7: // Zu sendende Meldung erzeugen
8: CString strMsg = "Zu viele Verbindungen, bitte später noch einmal versuchen.";
9: // Mittels Ablehnungs-Socket annehmen
10: m_sListenSocket.Accept(sRjctSock);
11: // Ablehnungsmeldung senden
12: sRjctSock.Send(LPCTSTR(strMsg), strMsg.GetLength());
13: // Socket schließen
14: sRjctSock.Close();
15: }
16: else
17: {
18: // Verbindungsanforderung annehmen
19: m_sListenSocket.Accept(m_sConnectSocket);\
20: // Socket als verbunden markieren
21: m_bConnected = TRUE;
22: // Steuerelemente für Text und Nachrichten aktivieren
23: GetDlgItem(IDC_EMSG)->EnableWindow(TRUE);
24: GetDlgItem(IDC_BSEND)->EnableWindow(TRUE);
25: GetDlgItem(IDC_STATICMSG)->EnableWindow(TRUE);
26: }
27: }

Antworten für Tag 21

Antworten zu den Kontrollfragen

1. Den Webbrowser Internet Explorer.

2. Mit der Funktion GetLocationURL().

3. IDOK.

4. GoBack() und GoForward().

5. Mit der Funktion Stop().

Lösungen zu den Übungen

1. Nehmen Sie einen Menübefehl in das Menü Wechseln zu auf. Legen Sie die Eigenschaften des Menübefehls nach Tabelle B.6 fest.

Tabelle B.6: Einstellungen der Menüeigenschaft

Objekt

ID

Einstellung

Menübefehl

ID

Titel

Statuszeilentext

IDM_GO_SEARCH

Suchen im &Web

Öffnet die Seite »Suchen im Web"\nSuchen

Fügen Sie der Ansichtsklasse mit dem Klassen-Assistenten eine Behandlungsroutine für die ID IDM_GO_SEARCH für die Nachricht COMMAND hinzu. In die Funktion schreiben Sie den Code entsprechend Listing B.37.

Listing B.37: Die Funktion OnGoSearch der Klasse CWebBrowseView

1: void CWebBrowseView::OnGoSearch()
2: {
3: // TODO: Code für Befehlsbehandlungsroutine hier einfügen
4:
5: // Zur Suchseite gehen
6: GoSearch();
7: }

Fügen Sie eine Symbolleistenschaltfläche für die Menü-ID IDM_GO_SEARCH hinzu.

2. Nehmen Sie einen Menübefehl in das Menü Wechseln zu auf. Legen Sie die Eigenschaften des Menübefehls entsprechend Tabelle B.7 fest.

Tabelle B.7: Einstellungen des Menübefehls

Objekt

Eigenschaft

Einstellung

Menübefehl

ID

Titel

Statuszeilentext

IDM_GO_START

&Startseite

Öffnet die Startseite\nStartseite

Fügen Sie der Ansichtsklasse mit dem Klassen-Assistenten eine Behandlungsfunktion für die ID IDM_GO_START für die Nachricht COMMAND hinzu. Übernehmen Sie den Code aus Listing 38 in die Funktion.

Listing B.38: Die Funktion OnGoStart der Klasse CWebBrowseView

1: void CWebBrowseView::OnGoStart()
2: {
3: // TODO: Code für Befehlsbehandlungsroutine hier einfügen
4:
5: // Zur Startseite gehen
6: GoHome();
7: }

Fügen Sie eine Symbolleistenschaltfläche für die Menü-ID IDM_GO_START hinzu.

3. Fügen Sie der Ansichtsklasse mit dem Klassen-Assistenten eine Behandlungsroutine für die Objekt-ID IDM_VIEW_STOP für die Nachricht UPDATE_COMMAND_UI hinzu. Übernehmen Sie den Code aus Listing B.39 in diese Funktion.

Listing B.39: Die Funktion OnUpdateViewStop der Klasse CWebBrowseView

1: void CWebBrowseView::OnUpdateViewStop(CCmdUI* pCmdUI)
2: {
3: // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren der Benutzeroberfläche hier einfügen
4:
5: // Schaltfläche aktivieren, wenn beschäftigt
6: pCmdUI->Enable(GetBusy());
7: }



vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH.
Elektronische Fassung des Titels: Visual C++ 6 in 21 Tagen, ISBN: 3-8272-2035-1