Grundlagen
Beginnen wir den Einstieg in die Dialoge so wie es sich gehört, nämlich mit den
Grundlagen. In dieser Lektion werden wir nicht gleich einen Dialog mit allen möglichen
Elementen erstellen sondern nur einen ganz einfachen Dialog, bestehend aus einem
Text und zwei Buttons. Und selbst dieser einfache Dialog gibt uns schon genug Möglichkeiten
mit ihm zu spielen.
Was ein Dialog prinzipiell ist dürfte bekannt sein. Aber damit die ganze Sache
auch hier nicht zu einfach wird, kennt WINDOWS zwei grundsätzliche Arten von Dialoge:
 |
die modale Dialoge, die geschlossen werden müssen bevor weitere Aktionen
mit dem übergeordneten Fenster durchgeführt werden können. Beispiel: Datei-Öffnen
Dialog |
 |
und die nicht-modale Dialoge, die geöffnet bleiben können während mit dem
übergeordneten Fenster gearbeitet wird. Beispiel: Suchen-Ersetzen Dialog |
Wie diese verschiedenen Dialogarten erzeugt werden, werden wir uns nachher gleich
ansehen. Beginnen wir zunächst mit der Erstellung des Layouts unseres einfachen
Dialogs, der wie erwähnt nur einen Text sowie die Buttons 'OK' und 'Abbrechen' enthält.
Dialoge werden, genauso wie die bisher behandelten Ressourcen, mit dem Ressourcen-Editor
erstellt.
 |
Erstellen Sie wieder, wie gewohnt, ein neues SDI-Projekt. Geben
Sie dem Projekt den Namen BaseDlg. Wechseln Sie dann in die Ressourcen-Ansicht
und klicken dort den Ressourcen-Typ Dialog mit der rechten Maustaste
an. Im daraufhin eingeblendeten Kontext-Menü wählen Sie den Menü-Punkt Dialog
einfügen aus. Der Ressourcen-Editor erzeugt dann folgenden Standard-Dialog:

Sie können nun durch ziehen an den Markern des Markierungsrahmens die Größe
des Dialogs Ihren Wünschen anpassen.
Sehen wir uns jetzt die Eigenschaften des neu erstellten Dialogs einmal an.
Klicken Sie dazu mit der rechten Maustaste in den Dialog und wählen aus dem
eingeblendeten Kontext-Menü den Eintrag Eigenschaften aus. Folgender
Dialog wird eingeblendet:

Jeder Dialog wird, genauso wie z.B. jedes Icon, durch eine eindeutige ID
gekennzeichnet. Standardmäßig werden Dialoge mit den IDs IDD_DIALOGx durchnummeriert.
Sie können jedoch die ID einfach durch überschreiben im Feld ID ändern.
Den Titel des Dialogs können Sie im Feld Beschriftung eingeben. Ändern
Sie den Titel jetzt, wie oben im Eigenschafts-Dialog angegeben, auf Modaler
Dialog. Sehen Sie sich auch ruhig die restlichen Eigenschaften des Dialogs
einmal an. Durch anklicken des Fragezeichens links oben im Dialog erhalten Sie
eine sehr gute Erklärung zu den jeweils gerade angezeigten Eigenschaften. Für
das Beispiel lassen Sie aber bitte alle anderen Eigenschaften auf ihrem Defaultwert.
Schließen Sie den Eigenschafts-Dialog durch anklicken des Schließ-Symbols oben
rechts.
|
 |
Sollte Ihnen die Position der Buttons 'OK' und 'Abbrechen' nicht gefallen,
so können Sie diese Buttons auch unten im Dialog positionieren. Selektieren
Sie dazu die beiden Buttons, gehen Sie dann ins Menü Layout und wählen
dort den Eintrag Schaltflächen anordnen - unten aus. |
Fügen wir nun noch zum Dialog einen Text hinzu.
 |
In der Regel erhalten Sie innerhalb des Dialogeditors folgenden
Toolbar eingeblendet:

Sollte dies bei Ihnen nicht der Fall sein, so klicken Sie mit der rechten
Maustet auf einen nicht belegten Bereich innerhalb des Toolbars im Visual
Studio. Im dann eingeblendeten Kontextmenü wählen Sie dort den Eintrag
Steuerelemente aus.
Um ein Textfeld zum Dialog hinzuzufügen, klicken Sie das folgende Symbol
im Toolbar an:

Anschließend klicken Sie mit der linken Maustaste im Dialog an die Position,
an der das Textfeld eingefügt werden soll, halten aber die linke Maustaste noch
gedrückt. Durch ziehen der Maus bei gedrückter linker Maustaste können Sie jetzt
die Größe des Textfeldes bestimmen. Wenn das Textfeld die gewünschte Größe besitzt,
lassen Sie die Maustaste einfach los. Um im Anschluss daran den Text für das
Feld einzugeben, klicken Sie mit der rechten Maustaste das Textfeld an und wählen
aus dem eingeblendeten Kontextmenü den Eintrag Eigenschaften aus.

Geben Sie im Feld Titel den oben angegebenen Text ein. Schließen
den Eigenschaftsdialog. Der Dialog sollte jetzt etwa folgendes Aussehen haben:

|
 |
Die ID des Textfeldes können Sie hier auf dem vorgegebenen
Wert IDC_STATIC belassen, außer Sie wollen den Text des Textfeldes zur Programmlaufzeit
abändern. In diesem Fall müssen Sie dem Textfeld eine eindeutige ID geben. Was
wir später auch noch tun werden. |
So, damit haben Sie Ihren ersten Dialog erstellt. Er ist zwar recht einfach aufgebaut,
aber für diese Übung jedoch völlig ausreichend. Was noch fehlt ist die Anzeige des
Dialogs aus der Anwendung heraus. Dialoge werden unter der MFC durch die Klasse
CDialog, die letztendlich von der Klasse CWnd abgeleitet ist,
repräsentiert. Und um einen Dialog darstellen zu können, müssen Sie zunächst ein
mit dem Dialog assoziiertes CDialog-Objekt erstellen. Auch hier hilft Ihnen
jedoch der Klassen-Assistent weiter.
 |
Führen Sie einen Doppelklick auf den Dialog aus um den Klassen-Assistent
aufzurufen. Der Klassen-Assistent meldet Ihnen, dass es sich bei dem Dialog
um eine neue Ressource handelt und fragt nach, ob eine neue oder eine bestehende
Klasse mit dem Dialog verknüpft werden soll. In der Regel werden Sie eine neue
Klasse erstellen.

Lassen Sie die Vorgabe Neue Klasse erstellen ausgewählt und klicken
Sie den OK-Button an. Als nächstes fragt der Klassen-Assistent nach
dem Namen der Klasse, die dem Dialog zugeordnet werden soll:

Geben Sie im Feld Name für das Beispiel CModal ein. Gleichzeitig
mit der Eingabe des Klassennamens wird auch der Name der Datei für die Implementierung
des Dialog-Codes durch den Klassen-Assistent festgelegt. Sie können, wenn Sie
wollen, den Dateinamen ändern. Klicken Sie dazu den Button Ändern an.
Als Basisklasse des Dialogs wurde automatisch die MFC-Klasse CDialog
eingesetzt und auch die ID des Dialogs IDD_DIALOG1 wurde richtig eingesetzt.
Schließen Sie den Dialog mittels des OK-Buttons.
Anschließend erlaubt der Klassen-Assistent für diverse Nachrichten Methoden
zu definieren. Für unser Beispiel ist dies zum jetzigen Zeitpunkt noch nicht
erforderlich. Schließen Sie daher den Klassen-Assistent mittels des OK-Buttons.

|
 |
Der Klassen-Assistent hat Ihrer Dialogklasse bereits eine Methode
DoDataExchange(...) hinzugefügt, zu erkennen am durch Fettschrift hervorgehobenen
Eintrag im Feld Nachrichten. Diese Methode dient zum späteren Datenaustausch
zwischen der Anwendung und dem Dialog. Wir kommen auf den Datenaustausch in
der nächsten Lektion noch zu sprechen. |
So, und jetzt dürfen Sie einmal wieder Ihr Glück versuchen.
 |
Erstellen Sie nun wie auf die gleiche Art und Weise wie vorhin
einen weiteren Dialog. Der Dialog soll folgendes Aussehen besitzen:

Übernehmen Sie für den Dialog die vorgegebene ID IDD_DIALOG2. Geben Sie dem
Textfeld im Dialog die ID IDC_CURPOS und der zum Dialog gehörenden Dialogklasse
den Namen CNonModal.
 |
Aber wenn Sie wollen kann ich Ihnen auch meine Lösung anzeigen. |
Lösung zur Erstellung des Dialogs
Zuerst fügen Sie dem neuen Dialog ein Textfeld hinzu und
stellen dessen Eigenschaften wie folgt ein:

Anschließend definieren Sie durch einen Doppelklick auf
den Dialog dessen Dialogklasse:

Ende der Lösung
|
|
Wenn Sie bis hierher alles richtig eingegebenen haben, so sollte Ihre Anwendung
jetzt zwei neue Klassen für die Dialoge enthalten:

Um die Dialoge nachher darzustellen, erweitern wir das vom Assistenten erstellte
Standard-Menü um zwei neue Menüpunkte.
 |
Fügen Sie zu dem Standard-Menü die nachfolgend angegebenen
zwei Menüpunkte mit den entsprechenden IDs hinzu. Beachten Sie bitte bei der
Beschriftung des Popup-Menüs, dass Sie als Shortcut für das Popup-Menü nicht
die Tasten <ALT>-<D> verwenden dürfen, da diese bereits für das Dateimenü verwendet
werden. Verwenden Sie als Shortcut hier die Tasten <ALT>-<I>.

Anschließend erstellen Sie über den Klassen-Assistenten die entsprechenden
Methoden OnModal(...) bzw. OnNonmodal(...) für die Bearbeitung
der Menüeinträge. Fügen Sie die Methoden zur Klasse des Ansichtsobjektes
CBaseDlgView hinzu (und nicht zum Rahmenfenster).
|
Wir hätten die Nachrichtenbearbeiter für die neuen Menüpunkte im Prinzip auch
dem Rahmenfenster hinzufügen können. Da wir aber nachher noch mit dem nicht-modalen
Dialog etwas experimentieren wollen, ist es in unserem Fall besser die Nachrichtenbearbeiter
gleich dem Ansichtsobjekt hinzuzufügen.
Nun kommt der entscheidende Schritt, die eigentliche Darstellung des Dialogs.
Um einen modalen Dialog (in manchen Büchern auch als gebundener Dialog bezeichnet)
darzustellen, müssen Sie zuerst ein Objekt der mit dem Dialog verbundenen CDialog-Klasse
(hier CModal) erstellen. Damit wird der Dialog zunächst nur erstellt, aber
noch nicht angezeigt. Um den Dialog anzuzeigen, rufen Sie die Methode DoModal(...)
der Dialogklasse auf. DoModal(...) ist eine virtuelle Methode der Basisklasse
CDialog. Diese Methode kehrt erste dann wieder zurück, wenn der Dialog
geschlossen wurde. Als Returnwert liefert die Methode in unserem Fall entweder IDOK
oder IDCANCEL, je nach dem, über welchen Button der Dialog geschlossen wurde.
 |
Und jetzt dürfen Sie wieder Ihr Können unter Beweis stellen.
Zeigen Sie den Dialog mit dem Titel Modaler Dialog an wenn der Menüeintrag
Modaler Dialog ausgewählt wird. Ein kleiner Hinweis noch am Rande.
Sie müssen die Header-Datei für die Dialogklasse des modalen Dialogs manuell
zum entsprechenden CPP-File hinzufügen, sonst erhalten Sie beim Übersetzen des
Beispiels einige Fehlermeldungen.
 |
Na, haben Sie's geschafft? Wenn nicht, hier meine Lösung.. |
Lösung zur Darstellung des modalen Dialogs
Zuerst fügen Sie, wie erwähnt, die Header-Datei des modalen
Dialogs Modal.h zur CPP-Datei des Ansichtsobjektes hinzu.
// BaseDlgView.cpp
: Implementierung der Klasse CBaseDlgView
//
#include "stdafx.h"
#include "BaseDlg.h"
#include "BaseDlgDoc.h"
#include "BaseDlgView.h"
#include "Modal.h"
#ifdef _DEBUG
.... |
Anschließend kann dann die Methode OnModal(...)
des Ansichtsobjekts vervollständigt werden.
void CBaseDlgView::OnModal()
{
// TODO: Code für Befehlsbehandlungsroutine hier einfügen
// Dialogobjekt
fuer modalen Dialog erstellen
CModal
CModalDlg;
// Returnwert des Dialogs
int
nRetValue;
// Dialog darstellen
nRetValue = CModalDlg.DoModal();
// Returnwert auswerten
if ( nRetValue == IDOK)
TRACE("Modaler Dialog mit OK-Button geschlossen\n");
else
TRACE("Modaler Dialog mit Abbruch-Button
geschlossen\n");
} |
War eigentlich nicht besonders schwer, oder?
In der oben angegebenen Lösung wurde das Dialogobjekt als lokales Objekt
erstellt. Sie hätten aber genauso gut das Dialogobjekt auch als Objekt der
Ansichtsklasse erstellen können, da das Schließen des modalen Dialogs nicht
das Dialogobjekt (und den Dialog selbst) zerstört. Vielmehr wird der Dialog
sozusagen nur versteckt.
Ende der Lösung
|
Übersetzen und starten Sie das Beispiel nun. Wenn Sie den Menüpunkt Dialog-Modaler
Dialog auswählen wird der Dialog angezeigt.
|
Sie können bei angezeigtem modalen Dialogen nicht mehr mit dem Fenster der Anwendung
arbeiten, so wie es von einem modalem Dialog erwartet wird.
Wollen Sie übrigens das Schließ-Menü oben rechts im Dialog entfernen, so entfernen
Sie im Dialog die Eigenschaft Systemmenü und übersetzen das Programm nochmals.
Soweit die prinzipielle Darstellung eines modalen Dialogs. Sehen wir uns nun
an wie ein nicht-modaler Dialog dargestellt wird. Nicht-modale Dialoge verhalten
sich im Prinzip wie eigenständige, untergeordnete Fenster, d.h. das dem Dialog übergeordnete
Fenster ist für die Erstellung und Zerstörung des Dialogs verantwortlich. Damit
der nicht-modale Dialog mit seinem übergeordneten Fenster kommunizieren kann ist
es unter anderem notwendig, dem Konstruktor des Dialog-Objektes einen Zeiger auf
sein übergeordnetes Fenster zu übergeben. Der Klassen-Assistent hat der Dialog-Klasse
bereits einen Konstruktor hinzugefügt, der als Parameter einen CWnd-Zeiger
auf das übergeordnete Fenster besitzt. Dieser Zeiger muss nun nur noch abgespeichert
werden.
 |
Fügen Sie der Klasse des nicht-modalen Dialogs nun eine Membervariable
m_pParent vom Typ CWnd-Zeiger hinzu.

Erweitern Sie den Konstruktor anschließend wie folgt:
CNonModal::CNonModal(CWnd*
pParent /*=NULL*/)
: CDialog(CNonModal::IDD, pParent)
{
//{{AFX_DATA_INIT(CNonModal)
// HINWEIS: Der Klassen-Assistent fügt
hier Elementinitialisierung ein
//}}AFX_DATA_INIT
m_pParent = pParent;
} |
|
Damit wäre der leichtere Teil der Arbeit getan. Wir haben bis jetzt einen Dialog
erstellt, die dazugehörige Klasse deklariert und können jetzt das Dialog-Objekt
dazu definieren. Während aber das Dialog-Objekt des modalen Dialogs in der Regel
als lokales Objekt erstellt und der Dialog dann mittels DoModal(...) angezeigt
wird, muss bei der Erstellung und Anzeige des nicht-modalen Dialogs ein etwas anderer
Weg eingeschlagen werden. Da das Dialog-Objekt des nicht-modalen Dialogs auch dann
noch gültig sein muss, wenn die Methode die den Dialog anzeigt verlassen wird, wird
zunächst ein Zeiger auf die Klasse des nicht-modalen Dialogs zu der Klasse hinzugefügt,
deren Methode später den Dialog anzeigen soll (hier die Klasse des Ansichtsobjekts).
Dieser Zeiger wird dann im Konstruktor der Klasse mit NULL initialisiert.
 |
Definieren Sie nie ein Dialog-Objekt für einen nicht-modalen Dialog
als lokales Objekt sonst stürzt Ihre Anwendung mit sehr großer Wahrscheinlichkeit
ab! |
 |
Fügen Sie zunächst zur Klasse CBaseDlgView den Zeiger
auf das nicht-modale CDialog-Objekt wie nachfolgenden angegeben hinzu:

Anschließend setzen Sie diesen Zeiger im Konstruktor noch auf NULL. Außerdem
ist im Destruktor das CDialog-Objekt, (das später noch erzeugt wird)
zu entfernen falls die Anwendung bei dargestelltem nicht-modalem Dialog beendet
wird.
CBaseDlgView::CBaseDlgView()
{
// ZU ERLEDIGEN: Hier Code zur Konstruktion einfügen,
// Zeiger auf nicht-modalen
Dialog initialisieren
m_pCNonModalDlg = NULL;
}
CBaseDlgView::~CBaseDlgView()
{
delete m_pCNonModalDlg;
} |
|
Hiermit sind alle 'Vorbereitungen' zur Erstellung und Anzeige des nicht-modalen
Dialogs abgeschlossen. Die Erstellung und Anzeige des Dialogs erfolgt dann im Beispiel
in der Methode OnNonmodal(...) des Ansichtsobjektes. Diese Methode haben
Sie bereits vorhin (bei der Erweiterung des Standard-Menüs) zum Ansichtsobjekt hinzugefügt.
Zuerst müssen Sie in dieser Methode, wie beim modalen Dialog, ein entsprechendes
Dialog-Objekt für den nicht-modalen Dialog erstellen. Um dann den mit dem CDialog-Objekt
assoziierten Dialog zur Anzeige zu bringen gibt es aber leider keine Methode DoNonModal(...).
Vielmehr müssen Sie den Dialog mit der CDialog-Methode Create(...)
zuerst erstellen. Diese Methode erhält im ersten Parameter die ID des zu erstellenden
Dialogs. Den zweiten Parameter der Methode können Sie in der Regel auf dem Defaultwert
NULL belassen, er ist der Zeiger auf das dem Dialog übergeordnete Fenster. Nachdem
der Dialog erstellt wurde muss er anschließend noch mit der bereits bekannten
CWnd-Methode ShowWindow(...) angezeigt werden.
 |
All diese Aktionen hat Ihnen vorhin beim modalen Dialog übrigens
die Methode DoModal(...) abgenommen. Sie hat den mit dem CDialog-Objekt
verknüpften Dialog erstellt und anzeigt. Denken Sie auch immer daran, dass
die Klasse CDialog von CWnd abgeleitet ist und damit auch
(fast) alle CWnd-Methoden auf Dialoge angewandt werden können.
|
 |
Erweitern wir nun die Methode OnNonmodal(...) der
Ansichtsklasse um die Erstellung und Anzeige des nicht-modalen Dialogs. Beachten
Sie dabei bitte, dass der Anwender durchaus mehrfach die Anzeige des Dialogs
über das Menü anfordern kann, da ja die Interaktion mit dem Hauptfenster während
der Anzeige des nicht-modalen Dialogs nicht gesperrt ist. Sie müssen also in
der Regel immer abprüfen, ob ein nicht-modaler Dialog bereits angezeigt wird.
Im Beispiel erfolgt dies durch abprüfen des Zeigers auf das nicht-modale Dialog-Objekt.
void CBaseDlgView::OnNomodal()
{
// TODO: Code für Befehlsbehandlungsroutine hier einfügen
// Falls Dialog noch
nicht erstellt ist
if (m_pCNonModalDlg == NULL)
{
// Dialog-Objekt erstellen
m_pCNonModalDlg = new CNonModal(this);
// Dialog erstellen und anzeigen
m_pCNonModalDlg->Create(CNonModal::IDD);
m_pCNonModalDlg->ShowWindow(SW_NORMAL);
}
} |
Sie können das Beispiel jetzt bereits, obwohl es noch einige Ungereimtheiten
besitzt, übersetzen und laufen lassen.
|
Allerdings werden Sie beim Beenden des Programms eine Fehlermeldung erhalten
und den nicht-modalen Dialog auch nur einmal dargestellt bekommen. Der Grund dafür
liegt in der Abfrage des Zeigers auf das nicht-modale Dialog-Objekt in der Methode
OnNonmodal(...). Eigentlich sollte mit dieser Abfrage verhindert werden,
dass der Dialog mehrmals erstellt wird wenn der Anwender immer wieder den Menüpunkt
Dialog-Nicht-modaler Dialog anwählt ohne den Dialog vorher zu schließen.
Der nicht-modalen Dialog wird beim Schließen aber nur versteckt, d.h. nicht mehr
angezeigt. Der Dialog und das mit ihm verbundene CDialog-Objekt bleiben aber
weiterhin bestehen. Damit das Ansichtsobjekt vom Schließen des Dialogs erfährt und
dann das Dialog-Fenster und das CDialog-Objekt zerstören kann, muss der Dialog
beim Schließen dem Ansichtsobjekt eine entsprechende Nachricht zusenden. Dies kann
auf verschiedene Arten erfolgen. Im Beispiel werden wir dazu einmal eine registrierte
WINDOWS-Nachricht verwenden. Registrierte WINDOWS-Nachrichten sind Nachrichten die
im gesamten System nur einmal vorkommen. Um eine solche Nachricht zu erstellen wird
die API-Funktion RegisterWindowMessage(...) aufgerufen. Sie erhält als
Parameter einen char-Zeiger auf den Namen der Nachricht und liefert dann als Returnwert
die ID der neuen Nachricht. Registrieren verschiedene Klassen, oder sogar auch Anwendungen,
Nachrichten mit dem gleichen Namen, so erhalten alle die gleiche Nachrichten-ID
zurückgeliefert. Mittels registrierter Nachrichten könnte damit sogar ein Nachrichtenaustausch
zwischen verschiedenen Anwendungen durchgeführt werden. In unserem Beispiel wird
aber nur der nicht-modale Dialog mit dem Ansichtsobjekt über eine solche Nachricht
kommunizieren. Das einzige Problem das es noch zu lösen gilt ist die Bestimmung
des Nachrichtennamens, da dieser von verschiedenen Klassen aus zugänglich sein muss.
Die Festlegung des Nachrichtennamens kann entweder über eine Define-Anweisung, eine
Konstante oder über eine statische public-Methode des Dialogs erfolgen. Im
Beispiel verwenden wir eine public-Methode der Dialog-Klasse hierzu.
 |
Fügen Sie zuerst der Dialog-Klasse eine entsprechende Methode
zur Bestimmung des Namens der registrierten WINDOWS-Nachricht hinzu. Da diese
Methode auch schon dann aufgerufen werden kann, wenn noch kein entsprechendes
Dialog-Objekt existiert, muss diese Methode als static-Methode definiert
werden. Fügen Sie die Methode GetMsgString(...) wie unten angegeben
zur Klasse CNonModal hinzu. Beachten Sie bitte die Markierung im Feld
Statisch im Dialog!!

Anschließend muss die Methode noch vervollständigt werden:
const char* CNonModal::GetMsgString()
{
// ACHTUNG! Methode
ist static zu deklarieren
return "CloseNonModDlg";
} |
Im Anschluss daran kann die registrierte WINDOWS-Nachricht erzeugt werden.
Da sowohl der Dialog wie auch das Ansichtsobjekt diese Nachricht benötigen,
muss die Nachricht von beiden Objekten registriert werden. Fügen wir dazu zuerst
der Dialogklasse CNonModal eine privat Membervariable vom Typ
UINT mit dem Namen m_wCloseMsg hinzu und anschließend registrieren
wir die neue WINDOWS-Nachricht im Konstruktor der Klasse.
CNonModal::CNonModal(CWnd*
pParent /*=NULL*/)
: CDialog(CNonModal::IDD, pParent)
{
//{{AFX_DATA_INIT(CNonModal)
// HINWEIS: Der Klassen-Assistent fügt
hier Elementinitialisierung ein
//}}AFX_DATA_INIT
m_pParent = pParent;
// WINDOWS-Nachricht
registrieren
m_wCloseMsg = ::RegisterWindowMessage(GetMsgString());
} |
|
Etwas komplizierte sieht die Sache bei der Registrierung der WINDOWS-Nachricht
beim Ansichtsobjekt aus. Da hier der Nachrichtenbearbeiter für die registrierte
WINDOWS-Nachricht definiert wird (der Dialog versendet ja nachher nur die Nachricht),
müssen zwei Dinge beachtet werden. Zum einen muss die Membervariable, die die Nachrichten-ID
aufnimmt, als statische Membervariable definiert werden. Und zum anderen bietet
der Klassen-Assistent nicht die Möglichkeit, registrierte Nachrichten in die Nachrichtentabelle
(Message-Map) einzufügen. Dies müssen Sie von Hand erledigen.
 |
Fangen wir mit der Definition der Membervariablen zur Aufnahme
der Nachrichten-ID der registrierten WINDOWS-Nachricht an. Fügen Sie wie folgt
die statische Membervariable m_wCloseMsg zur Klasse des Ansichtsobjekts
hinzu.

Leider definiert der Klassen-Assistent außerdem nicht automatisch
statische Membervariablen, so dass Sie dies nun manuell durchführen müssen.
Es bietet sich an, die statische Membervariable bei ihrer Definition auch gleichzeitig
zu initialisieren. Fügen Sie folgenden Code am Anfang der Datei BaseDlgView.cpp
ein:
// BaseDlgView.cpp : Implementierung
der Klasse CBaseDlgView
//
....
include "Modal.h"
#ifdef _DEBUG
....
#endif
// Nachricht fuer Datenaustausch mit nicht-modalem
Dialog registrieren
// ACHTUNG! Die Nachrichtenvariablen muß als static deklariert sein
// sonst wird im Makro ON_REGISTERED_MESSAGE(...) ein Fehler gemeldet
UINT CBaseDlgView::m_wCloseMsg =
RegisterWindowMessage(CNonModal::GetMsgString());
/////////////////////////////////////////////////////////////////////////////
// CBaseDlgView
IMPLEMENT_DYNCREATE(CBaseDlgView, CView) |
Danach muss die registrierte Nachricht noch mit einer Methode des Ansichtsobjekts
verbunden werden. Dies erfolgt über das Makro ON_REGISTERED_MESSAGE(...).
Wie bereits erwähnt, bietet der Klassen-Assistent leider keine Möglichkeit dieses
Makro zur Nachrichtentabelle hinzuzufügen. Fügen Sie daher von Hand folgende
Zeile zur Nachrichtentabelle hinzu, die die Methode OnNonModClosed(...)
mit der registrierten Nachricht verbindet:
BEGIN_MESSAGE_MAP(CBaseDlgView,
CView)
//{{AFX_MSG_MAP(CBaseDlgView)
ON_COMMAND(ID_MODAL, OnModal)
ON_COMMAND(ID_NOMODAL, OnNomodal)
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE
(m_wCloseMsg, OnNonModClosed)
END_MESSAGE_MAP() |
Fügen Sie dann noch die Methode OnNonModClosed(...) wie folgt zum
Ansichtsobjekt hinzu:

|
Damit wäre die Nachricht zwischen dem Dialog und dem Ansichtsobjekt 'installiert'.
Wenden wir uns nun wieder dem Dialog zu. Der Dialog muss die registrierte WINDOWS-Nachricht
immer dann an das Hauptfenster senden, wenn er über einen der beiden Buttons geschlossen
wird. Dazu müssen die Nachrichtenbearbeiter für die Buttons OK und
Abbrechen zur Dialogklasse über den Klassen-Assistenten hinzugefügt werden.
 |
Öffnen Sie jetzt den Klassen-Assistenten und wählen den Tabulator
Nachrichtenzuordnungstabellen aus. Selektieren Sie im Feld Klassenname
die Klasse CNonModal. Klicken Sie dann die Objekt-ID IDOK
und anschließend die Nachricht BN_CLICKED doppelt an. Der Klassen-Assistent
schlägt daraufhin als Methodenname zur Behandlung der Buttonnachricht OnOK(...)
vor; übernehmen Sie diesen Vorschlag. Verfahren Sie auf die gleiche Art um den
Nachrichtenbearbeiter OnCancel(...) für den Abbrechen Button
zu erstellen. Haben Sie beide Methoden erstellt, schließen Sie den Klassen-Assistent
mit dem OK-Button.

Nun gilt es noch die Methoden OnOK(...) und OnCancel(...)
zu implementierten. Beide Methoden müssen mittels SendMessage(...)
die vorhin registrierte Nachricht an das Ansichtsobjekt senden. Welcher Button
gedrückt wurde, wird dem Ansichtsobjekt über den zweiten Parameter (WPARAM)
der SendMessage(...) Methode mitgeteilt. Erweitern Sie die beiden Methode
wie folgt:
void CNonModal::OnOK()
{
// TODO: Zusätzliche Prüfung hier einfügen
m_pParent->SendMessage(m_wCloseMsg,IDOK,NULL);
}
void CNonModal::OnCancel()
{
// TODO: Zusätzlichen Bereinigungscode hier einfügen
m_pParent->SendMessage(m_wCloseMsg,
IDCANCEL, NULL);
} |
Damit ist die Dialog-Klasse CNonModal vollständig.
|
Beachten bitte, dass im obigen Listing die Methode SendMessage(...)
des Empfängers aufgerufen wird und nicht die des sendenden Objektes! Außerdem wurde
die Standard-Verarbeitung der Nachrichten entfernt.
Zum Schluss muss noch die Nachricht im Nachrichtenbearbeiter OnNonModClosed(...)
des Ansichtsobjekts entsprechend verarbeitet werden. Die Methode kann durch auswerten
des (nun) ersten Parameters feststellen, über welchen Button der Dialog geschlossen
wurde. Nach dem Auswerten des Parameters kann das Dialogfenster und das CDialog-Objekt
zerstört werden. Damit bei der nächsten Auswahl der Menüpunktes für die Anzeige
des nicht-modalen Dialogs festgestellt werden kann dass dieser zerstört wurde, muss
der Zeiger auf das Dialog-Objekt noch auf den Wert NULL zurückgesetzt werden.
 |
Erweitern Sie die Methode OnNonModClosed(...) wie folgt:
LONG CBaseDlgView::OnNonModClosed(UINT
wReason, LONG lParam)
{
// Dialog-Ergebnis
auswerten
if (wReason == IDOK)
TRACE("Nicht-modaler Dialog mit OK-Button
geschlossen\n");
else
TRACE("Nicht-modaler Dialog mit Abbruch-Button
geschlossen\n");
// Dialog zerstoeren
m_pCNonModalDlg->DestroyWindow();
// und Dialog-Objekt loeschen
delete m_pCNonModalDlg;
m_pCNonModalDlg = NULL;
return 0L;
} |
Wenn Sie das Beispiel jetzt übersetzen, so sollte es die gewünschte Funktionalität
aufweisen, d.h. bei angezeigtem nicht-modalem Dialog können Sie trotzdem mit
dem Hauptfenster arbeiten und sich z.B. den modalen Dialog anzeigen lassen.
|
Damit wollen wir die Grundlagen des Dialogs abschließen. Das fertige Beispiel
finden Sie unter 08Dialoge\BaseDlg. Zusätzlich
wurde das Beispiel noch etwas erweitert; so wird im nicht-modalen Dialog die aktuelle
Position der Maus im Ansichtsobjekt dargestellt. Der Dialog erhält die aktuelle
Mausposition über die gleiche registrierte Nachricht zugesandt, d.h. die Nachricht
läuft nun nicht mehr bloß vom Dialog zum Ansichtsobjekt sondern auch vom Ansichtsobjekt
zum Dialog.
So, in der nächsten Lektion geht's dann richtig mit den Controls los.
|