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:

no1.gif (995 Byte) die modale Dialoge, die geschlossen werden müssen bevor weitere Aktionen mit dem übergeordneten Fenster durchgeführt werden können. Beispiel: Datei-Öffnen Dialog
no2.gif (1016 Byte) 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:

Der 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:

Die Eigenschaften eines Dialogs

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:

Dialogsteuerelemente

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:

Textfeld-Element

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.

Eigenschaften des Textfeldes

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

Ihr erster modaler Dialog

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.

CDialog-Klasse mit dem Dialog verknüpfen

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:

Dialog-Klasse definieren

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.

Nachrichtenzuordnung zu einem Dialog

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:

Ihr erster eigener Versuch eines Dialogs

Ü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.

Lösung zur Erstellung des Dialogs

Zuerst fügen Sie dem neuen Dialog ein Textfeld hinzu und stellen dessen Eigenschaften wie folgt ein:

Eigenschaften des Textfeldes

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

Die Dialogklasse des neuen Dialogs

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:

Die Dialogklassen

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>.

Menü für die Anzeige der Dialoge

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.

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.

Der Zeiger auf das übergeordnete Fenster des Dialogs

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:

Zeiger auf die Klasse des nicht-modalen Dialogs

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!!

Static-Methode zur Bestimmung des Nachrichtenstrings

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.

Definition der Nachrichten-ID im Ansichtsobjekt

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:

Der Nachrichtenbearbeiter für die registrierte Nachricht

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.

Nachrichtenbearbeit für OK/Abbruch Button installieren

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.



Copyright © 2004

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