Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » FAQ VC++ / MFC » Beispiel zum Erzeugen eines nichtmodalen Dialoges

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
17.01.2003, 23:10 Uhr
Uwe
C/C++ Master
(Administrator)


Erstellt von Uwe

Im Gegensatz zum modalen Dialog ist der Anwender beim nichtmodalen Dialog in der Lage zwischen/zu anderen Fenstern zu wechseln. Die nichtmodalen Dialogfelder bleiben meist eine längere Zeit am Bildschirm. Ein typisches Beispiel hierfür ist der Suchen- oder Suchen- und Ersetzen- Dialog von Editoren. Leider finden sich in den meisten Büchern wenige Anhaltspunkte wie nichtmodale Dialoge erzeugt und behandelt werden. Das folgende kleine Beispiel demonstriert das Erzeugen von oben genanntem.
Als erstes erzeugen wir ein neues SDI-Projekt mit dem Namen Dialoge. Im Schritt 1 beim Anwendungsassistenten wählen wir Einzelnes Dokument (SDI) und deaktivieren Unterstützung der Dokument-/Ansichten Architektur. Vom Schritt 2-6 bis 6-6 übernehmen wir alle vorgeschlagenen Einstellungen. Jetzt wechseln wir in den Ressourcen-Editor der VC++ Entwicklungsumgebung wählen Dialoge und nach einem Klick mit der rechten Maustaste Dialog einfügen. Nachdem die neue Ressource also ein neuer Dialog angelegt wurde klicken wir diesen kurz an, so das dieser aktiv wird. Durch einen erneuten klick mit der rechten Maustaste oder durch drücken der Enter-Taste öffnen wir das Menü Eigenschaften für unseren neuen Dialog. Unter dem Register Allgemein geben wir einen Titel - Nichtmodaler Dialog ein. Im Register Weitere Formate müssen wir nun Sichtbar auswählen. Den Rest der Einstellungen belassen wir so wie vorgegeben.
Jetzt legen wir eine neue Klasse für unseren Dialog an. Dies geschieht am schnellsten durch einen Doppelklick in den Innbereich unseres neu erstellten Fensters. Jetzt sollte ein Fenster mit der Beschriftung Hinzufügen einer Klasse erscheinen. Die Voreinstellung wird so belassen und OK bestätigt. Im darauf folgendem Fenster geben wir als Namen z.B. CNichtModalerDlg ein. Die Basisklasse ist CDialog. Mit OK wird nun unsere neue Klasse angelegt.
Im Gegensatz zum modalen Dialog haben wir nun noch einigen programmiertechnischen Aufwand vor uns. Nichtmodale Dialoge werden nicht mit DoModal(), sondern mit Create() erzeugt. Create() kehrt unmittelbar zurück während das Dialogfeld angezeigt bleibt. Das bedeutet auch, das nach Abarbeitung der Nachrichtenbehandlungsroutine des Menübefehls (welchen wir später noch einfügen werden) das Dialogfeld Gültigkeit hat. Aus diesem Grund können wir keine lokale Variable für den Dialog verwenden. Nach Austritt aus der Funktion ist diese Variable nicht mehr gültig. Wir behelfen uns hier mit einer privaten Zeigervariable. Diese müssen wir in der Ansichtsklasse eintragen.
Wir öffnen dazu den ChildView.h und geben folgendes ein:

C++:
...........
..........
// Operationen
public:

// Überladungen
        // Vom Klassen-Assistenten erstellte virtuelle Funktionsüberladungen
        //{{AFX_VIRTUAL(CChildView)
        protected:
        virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
        //}}AFX_VIRTUAL

// Implementierung
public:
        virtual ~CChildView();

        // Generierte Funktionen für die Nachrichtentabellen
protected:
        //{{AFX_MSG(CChildView)
        afx_msg void OnPaint();
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()

        // Unser Eintrag Anfang
private:
        CNichtModalerDlg *m_pNichtModalerDlg;

        // Unser Eintrag Ende
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ fügt unmittelbar vor der vorherigen Zeile zusätzliche Deklarationen ein.

#endif // !defined(AFX_CHILDVIEW_H__053AC393_7FD9_4C4E_BB8A_164602BC88BE__INCLUDED_)


Wir müssen nun den NichtModalerDlg.h inkludieren. Im Konstruktor müssen wir das Dialogobjekt mit new erzeugen. Durch delete im Destuktor wird es gelöscht. Wir wechseln also in die DialogeView.cpp und legen Hand am Konstruktor und Destruktor an.

C++:
CChildView::CChildView()
{
        // Unser Eintrag Anfang

        m_pNichtModalerDlg=new CNichtModalerDlg(this);

        // Unser Eintrag Ende
}

CChildView::~CChildView()
{
        // Unser Eintrag Anfang

        delete m_pNichtModalerDlg;

        // Unser Eintrag Ende


}


Jetzt wird es etwas kompliziert. Unser Dialog muss nun die zugehörige Ansicht kennen um Nachrichten nach dort senden zu können. Wir wechseln einmal in den Konstruktor unseres erzeugten Dialoges also in die NichtModalerDlg.cpp. Dort sehen wir folgendes:

C++:
CNichtModalerDlg::CNichtModalerDlg(CWnd* pParent /*=NULL*/)

{
        //{{AFX_DATA_INIT(CNichtModalerDlg)
                // HINWEIS: Der Klassen-Assistent fügt hier Elementinitialisierung ein
        //}}AFX_DATA_INIT
}


Wir sehen hier, dass der Besitzer dieses neuen Fenster nicht die Ansicht sondern das Hauptrahmenfenster ist. Das müssen wir abändern in:

C++:
CNichtModalerDlg::CNichtModalerDlg(CChildView* pView /*=NULL*/)

{
        //{{AFX_DATA_INIT(CNichtModalerDlg)
                // HINWEIS: Der Klassen-Assistent fügt hier Elementinitialisierung ein
        //}}AFX_DATA_INIT
}


Das gleiche muss im entsprechenden Header erledigt werden.

C++:
class CNichtModalerDlg : public CDialog
{
public:
        CNichtModalerDlg(CChildView* pView = NULL);   // Standardkonstruktor

// Dialogfelddaten
        //{{AFX_DATA(CNichtModalerDlg)
        enum { IDD = IDD_DIALOG1 };
.........
........
}


Nun muss unserer Dialogklasse jedoch die Ansichtsklasse bekannt gegeben werden, sprich der Header müsste inkludiert werden. Diese hängt jedoch von NichtModalerDlg.h bereits ab. Wir würden praktisch die Header gegenseitig inkludieren. Was nun? Es gibt doch die Vorwärtsdeklaration.
In NichtModalerDlg.h fügen wir deshalb ein:

C++:
#if !defined(AFX_NICHTMODALERDLG_H__CA9873AF_ED02_4002_B373_98B64598C52C__INCLUDED_)
#define AFX_NICHTMODALERDLG_H__CA9873AF_ED02_4002_B373_98B64598C52C__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// Unser Eintrag - Anfang

class CChildView;

// Unser Eintrag - Ende

// NichtModalerDlg.h : Header-Datei
//

/////////////////////////////////////////////////////////////////////////////
// Dialogfeld CNichtModalerDlg

class CNichtModalerDlg : public CDialog
........
.......


Als nächstes machen wir uns daran einen neuen Menüeintrag Dialoge in der IDR_MAINFRAME mit dem Untereintrag Nichtmodaler Dialog anzulegen. Danach öffnen wir den Klassenassistenten und wechseln, falls nicht schon geschehen auf das Register Nachrichtenzuordnungstabellen. Schlie&szlig;lich wählen wir als Klassennamen die CChildView-Klasse, da wir ja mit unserer Zeigervariable (des nichtmodalen Dialoges), welche in dieser Klasse angelegt wurde das Objekt anzeigen wollen. Wir suchen unter den Objekt.-IDs unser eben erzeugten Eintrag, in dem Fall ID_DIALOGE_NICHTMODALERDIALOG. Jetzt erzeugen wir für die Nachricht COMMAND eine Funktion, akzeptieren den Vorschlag und wechseln zu Code bearbeiten. Dort schreiben wir nun unsere Create() Funktion und zeigen unser Objekt an.

C++:
void CChildView::OnDialogeNichtmodalerdialog()
{
        // TODO: Code für Befehlsbehandlungsroutine hier einfügen

        // Unser Eintrag Anfang

        if (m_pNichtModalerDlg->GetSafeHwnd()==0)
                m_pNichtModalerDlg->Create(IDD_DIALOG1);

        // Unser Eintrag Ende
}


Da der Dialog nur einmal angezeigt werden darf müssen wir die Zugriffsnummer mit GetSafeHwnd() ermitteln. Wurde der Dialog noch nicht angezeigt ist diese 0. Mit Create() erzeugen wir dann unseren Dialog über die ID des Dialoges.
Wenn wir jetzt unser Projekt ausführen werden wir feststellen, dass sich unser Fenster nur einmal aufrufen lässt. Ist ja eigentlich auch logisch da OnCancel und OnOk die Funktion EndDialog aufruft. Also müssen wir diese beiden Funktionen überschreiben.
Wir öffnen den Klassenassistenten und wählen unsere CNichtModalerDlg Klasse aus. Bei den Objekt-IDs wählen wir IDCANCEL und klicken im Feld Nachrichten auf BN_CLICKED. Danach wird Funktion hinzufügen ausgewählt und bearbeitet.

C++:
void CNichtModalerDlg::OnCancel()
{
        // TODO: Zusätzlichen Bereinigungscode hier einfügen

        // Unser Eintrag Anfang

        DestroyWindow();

        // Unser Eintrag Ende

        //CDialog::OnCancel();
}


Selbiges muss für die OK Schaltfläche erfolgen.

C++:
void CNichtModalerDlg::OnOK()
{
        // TODO: Zusätzliche Prüfung hier einfügen

        // Unser Eintrag Anfang

        UpdateData(TRUE);
        DestroyWindow();

        // Unser Eintrag Ende

        //CDialog::OnOK();
}


Um nun dem Nutzer nicht zu verwirren deaktivieren wir noch den Menübefehl Nichtmodaler Dialog, wenn das Fenster angezeigt ist und schalten ihn wieder zu wenn das Fenster zerstört wurde.
Wir starten den Klassenassistenten und wählen unsere Ansichtsklasse. Danach suchen wir unsere Objekt-ID des Menübefehls und klicken auf UPDATE_COMMAND_UI.

C++:
void CChildView::OnUpdateDialogeNichtmodalerdialog(CCmdUI* pCmdUI)
{
        // TODO: Code für die Befehlsbehandlungsroutine zum Aktualisieren der Benutzeroberfläche hier einfügen

        // Unser Eintrag Anfang

        pCmdUI->Enable(m_pNichtModalerDlg->GetSafeHwnd()==0);

        // Unser Eintrag Ende
}


Gut wir haben nun das Grundgerüst für unseren Dialog fertig. Wie kann ich nun aber den Datenfluss kontrollieren. Hier ein einfaches Beispiel: Wir nehmen an, der Nutzer kann im nichtmodalen Dialog verschiedene Auswahlen über Kontrollkästchen und Optionsfelder tätigen. Die Ansichtsklasse soll über die Veränderung informiert werden. Wir müssen eine benutzerdefinierte Nachricht an unsere Ansichtsklasse schicken. Dazu legt man im Header der Ansichtsklasse als erstes den Wert für die WM_Konstante (WindowsMessage) fest. WM_USER ist die erste für den Benutzer freie ID. Der Wert 5 wird dazu addiert, da das Anwendungsgerüst schon einige für sich beansprucht.

C++:
#if !defined(AFX_CHILDVIEW_H__053AC393_7FD9_4C4E_BB8A_164602BC88BE__INCLUDED_)
#define AFX_CHILDVIEW_H__053AC393_7FD9_4C4E_BB8A_164602BC88BE__INCLUDED_

#include "NichtModalerDlg.h"        // Hinzugefügt von der Klassenansicht
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// Unser Eintrag Anfang

#define WM_UNSERENACHRICHT WM_USER+5

// Unser Eintrag Ende

/////////////////////////////////////////////////////////////////////////////
// CChildView-Fenster

class CChildView : public CWnd
{
  ...........
  ..........
}


Von der API Programmierung her ist bekannt, das es zum Senden von Nachrichten zwei Möglichkeiten gibt. PostMessage() und SendMessage(). Wir werden hier PostMessage() verwenden, da diese die Nachricht in die Warteschlange stellt und sofort zurück kehrt. PostMessage() ist eine Memberfunktion von CWnd. Wir brauchen also einen CWnd Zeiger auf unsere Ansicht, welchen wir im NchtModalenDlg.h eintragen.

C++:
class CNichtModalerDlg : public CDialog
{
// Konstruktion
public:
        CNichtModalerDlg(CChildView* pView = NULL);   // Standardkonstruktor

// Dialogfelddaten
        //{{AFX_DATA(CNichtModalerDlg)[/em
        enum { IDD = IDD_DIALOG1 };
                // HINWEIS: Der Klassen-Assistent fügt hier Datenelemente ein
        //}}AFX_DATA


// Überschreibungen
        // Vom Klassen-Assistenten generierte virtuelle Funktionsüberschreibungen
        //{{AFX_VIRTUAL(CNichtModalerDlg)
        protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV-Unterstützung
        //}}AFX_VIRTUAL

// Implementierung
protected:

        // Generierte Nachrichtenzuordnungsfunktionen
        //{{AFX_MSG(CNichtModalerDlg)
        virtual void OnCancel();
        virtual void OnOK();
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()

        // Unser Eintrag Anfang

private:
        CChildView * pView;

        // Unser Eintrag Ende
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ fügt unmittelbar vor der vorhergehenden Zeile zusätzliche Deklarationen ein.

#endif // AFX_NICHTMODALERDLG_H__3C258F87_81CF_4EF6_891A_87AEBCC9C033__INCLUDED_


Nun müssen wir den Konstruktor des Dialoges ändern.

C++:
NichtModalerDlg::CNichtModalerDlg(CChildView* pView /*=NULL*/)

{
        // Unser Eintrag Anfang

        p_View=pView;

        // Unser Eintrag Ende

        //{{AFX_DATA_INIT(CNichtModalerDlg)
                // HINWEIS: Der Klassen-Assistent fügt hier Elementinitialisierung ein
        //}}AFX_DATA_INIT
}


In der Nachrichtenzuordnungstabelle der Ansichtsklasse müssen wir per Hand die Nachrichtenbehandlungsroutine einfügen. Also auf nach ChildView.cpp

C++:
CChildView::~CChildView()
{
        delete m_pNichtModalerDlg;

}


BEGIN_MESSAGE_MAP(CChildView,CWnd )
        //{{AFX_MSG_MAP(CChildView)
        ON_WM_PAINT()
        ON_COMMAND(ID_DIALOGE_NICHTMODALERDIALOG, OnDialogeNichtmodalerdialog)
        ON_UPDATE_COMMAND_UI(ID_DIALOGE_NICHTMODALERDIALOG, OnUpdateDialogeNichtmodalerdialog)
        //}}AFX_MSG_MAP

        // Unser Eintrag Anfang

        ON_MESSAGE(WM_UNSERENACHRICHT, OnUnsereNachricht)

        // Unser Eintrag Ende

END_MESSAGE_MAP()


In die Ansichtsklasse muss nun eine Memberfunktion eingefügt werden, welche folgende Struktur hat:

C++:
[i]afx_msg LRESULT OnUnsereNachricht(WPARAM wParam, LPARAM lParam);[/i]


Die Funktion füllen wir wie folgt:

C++:
LRESULT CChildView::OnUnsereNachricht(WPARAM wParam, LPARAM lParam)
{
        AfxMessageBox("Nachricht vom nichtmodalen Dialog\
        gesendet"
);
        return 0L;
}


Jetzt brauchen wir noch ein Ereignis, welches die Nachricht absendet. Also legen wir einen Button in unseren Dialog.

C++:
void CNichtModalerDlg::OnMessageZumView()
{
        // TODO: Code für die Behandlungsroutine der Steuerelement-Benachrichtigung hier einfügen

        // Unser Eintrag Anfang

        m_pView->PostMessage(WM_UNSERENACHRICHT,0);

        // Unser Eintrag Ende
}


Vorher ist der ChildView.h zu inkludieren. Der Dialog kann jetzt voll genutzt und andere Steuerelemente darin platziert werden. Das Beispiel steht als Projekt für VC++ im Downloadbereich zur Verfügung.
--
"Es ist schwierig, ein Programm wirklich idiotensicher zu machen, weil Idioten so genial sind."

Bis dann...
Uwe

Dieser Post wurde am 18.01.2003 um 11:08 Uhr von Uwe editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ FAQ VC++ / MFC ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: