Hallo,
ich habe eine Frage zu Nachrichten zwischen Dialogen in MFC-Anwendungen
(MS VC++ 6).
Wenn ich von einem Dialog aus einen Handler in einem anderen Dialog oder
dem Hauptfenster aufrufen möchte (oder Daten übergeben), mache ich
bisher folgendes:
In der Klasse CMainFrame (MainFrm.cpp) implementiere ich für jede
Nachricht einen Messagehandler, beispielsweise so:
ON_MESSAGE(WM_CLOSEPROJDLG, OnForwardMsg_ProjDlgCl)
In CMainFrame schreibe ich dann eine Umleitung, Beispiel:
void CMainFrame::OnForwardMsg_ProjDlgCl(){
{Der_Empfaenger}.SendMessage(WM_CLOSEPROJDLG);
CWnd *pwnd = GetWindow(GW_CHILD);
pwnd -> SendMessage(WM_CLOSEPROJDLG);
}
In diesem Beispiel ist {Der_Empfaenger} meine Zielklasse.
Die Umleitung spreche ich aus dem Dialog an, beispielsweise so:
CWnd* pwnd = GetWindow(GW_OWNER);
pwnd -> SendMessage(WM_CLOSEPROJDLG);
Dieses Hin- und hergerufe kommt mir sehr umständlich vor, so dass ich
denke dass ich ungeschickterweise Konzepte aus anderen Sprachen schlecht
in VC++ 6 übertragen habe.
Meine Frage: Gibt es eine bessere/elegantere Methode?
Gruß & danke,
Nils
Du musst dich der Grundsatzfrage stellen:
Will ich über Messages gehen bzw. was verspreche ich mir von den
Messages?
Wenn dir dafür kein wirklich vernünftiger Grund einfällt, dann musst du
nicht über Messages laufen, sondern kannst das alles auch mit normalen
Funktionsaufrufen machen.
Der Mainframe muss ja auch so alle Dialoge kennen (sind offenbar nicht
modale Dialoge) um die Message weiterrouten zu können. Ob der Mainframe
dem jetzt eine Message schickt, oder ob der Dialog eine ganz spezielle
Funktion dafür zur Verfügung stellt, ist gehupft wie gesprungen.
Auf der anderen Seite ist es in einem MFC Programm von überall leicht ,
an den Mainframe rannzukommen, so dass ein direkter Aufruf der
Verteilfunktion in CMainFrame nichts im Wege steht.
Wenn es dir aber tatsächlich wichtig ist, die einzelnen Klassen
vollständig voneinander zu entkoppeln, dann ist dein Message-Ansatz
schon ok.
Hallo Karl Heinz,
danke für die ausführliche Antwort.
> ... (sind offenbar nicht modale Dialoge) ...
Doch, auch für modale Dialoge wähle ich diesen Weg. Ergeben sich da aus
Deiner Sicht Schwierigkeiten?
> Wenn es dir aber tatsächlich wichtig ist, die einzelnen Klassen> vollständig voneinander zu entkoppeln, dann ist dein Message-Ansatz> schon ok.
Ja, das versuche ich tatsächlich durchzuhalten.
Aber wie ist das mit den Dialoginstanzen, die Mainframe bekannt sind?
Wie kann ich Dialoginstanzen dieses Wissen bekannt geben, wenn ich eine
bestimmte Methode in einem bestimmten Dialog ansprechen möchte?
Sollte ich dann die entsprechende Dialoginstanz globalisieren und das
den miteinander kommunizierenden Dialogen per 'extern' bekannt geben?
Das erschien mir ein bisschen windig - ich denke ich habe hier irgendwie
Scheuklappen vor den Augen und sehe die simple Lösung nicht.
Danke schon mal für die Info, dass mein bisheriger Lösungsansatz adäquat
ist - das beruhigt mich etwas.
Gruß,
Nils
Hallo Rufus,
> Warum müssen Dialoge miteinander kommunizieren?
Mal ein konkretes Beispiel:
In einem modalen Dialog habe ich eine Eingabemaske. Beim Schließen des
Dialogs soll der Wert in der Eingabemaske einen Handler im Hauptfenster
aufrufen, beispielsweise, um eine ComboBox um diesen Wert zu erweitern.
Wie kann man das ohne Nachrichten lösen?
Der Dialog ist modal, also gibt es eine Stelle, an der er aufgerufen
wurde. Das wird irgendwo in einem Ereignishandler eines Views oder eines
Frame Window geschehen.
An eben dieser Stelle kann der Rückgabewert von CDialog::DoModal()
untersucht werden. Z.B. bei IDOK werden dann die im Dialog manipulierten
Dinge ausgewertet und dann kann der View oder wer auch immer etwas damit
anfangen.
Rufus t. Firefly schrieb:
> An eben dieser Stelle kann der Rückgabewert von CDialog::DoModal()> untersucht werden. Z.B. bei IDOK werden dann die im Dialog manipulierten> Dinge ausgewertet und dann kann der View oder wer auch immer etwas damit> anfangen.
Oder um das einmal etwas allgemeiner zu formulieren.
Ein modaler Dialog ist selten in der Verlegenheit, dass andere
Programmteile sofort von seinen Änderungen in Kentnis gesetzt werden
müssen (Ausnahme: Man baut einen Apply-Button ein. Daher sieht man den
auch eher selten :-)
Die übliche Vorgehensweise ist:
Der Dialog geht davon aus, dass seine Vorgabe-Werte beim Betreten der
OnInitDialog richtig eingestellt wurden. Zuständig dafür ist derjenige,
der den Dialog erzeugt und das DoModal() aufgerufen hat. Seine Aufgabe
ist es, den Dialog mit den Ausgangwerten zu versorgen.
Dann macht der Dialog sein Ding
Prüft im OnOK Handler meinetwegen noch, ob irgendwelche Randbedingungen
erfüllt sind und beendet sich
Dann kommt wieder der Aufrufer des Dialogs zum Zug.
Seine Aufgabe ist es jetzt, im OK-Fall sich vom Dialog die Werte
abzuholen und weiter zu verteilen / weiter zu verarbeiten.
Was ein Dialog nicht tun sollte: Direkt auf der Datenstruktur des
Dokuments arbeiten. Das macht die Behandlung des Cancel Buttons
schwierig. Auch ist es dann oft sehr schwer sicherzustellen, dass die
vom Benutzer veränderten Daten in sich konsistent sind, wenn im Dialog
bei jeder Änderung gleich die Dokument-Datenstruktur verändert wird.
Was ein Dialog auch nicht tun sollte: Sich die Daten selbst
zusammensuchen. Es ist der Programmteil, der den Dialog öffnet, der
dafür zuständig ist.
Aus Sicht des Dialogs sollte die Sache so sein:
* Ich kriege Daten vorgesetzt. Und zwar in Form von Kopien. Verändere
ich die Daten, dann passiert erst mal nichts.
* der Benutzer bearbeitet sie
* ich hinterlasse die Daten so, dass mein Vorgesetzter sie sich
abholen kann. Jetzt ist es gut, dass ich immer nur auf Kopien
gearbeitet habe, denn meinem Aufrufer steht es damit frei die
Änderungen ganz einfach zu ignorieren (zb weil ich ihm mitteile
dass mein Benutzer auf Cancel gedrückt hat)
Mit einem derartigen Vorgehen (und einer zentralen UpdateControls
Funktion im Dialog, in der Dialogelemente inaktiv/aktiv gesetzt werden)
waren zumindest bei mir die meisten Dialoge bessere Fingerübungen ohne
gröbere Komplikationen.
Bei nicht-modalen Dialogen ist die Situation häufig unangenehmer. Aber
die sind ja nicht betroffen.
Hallo Rufus, hallo Karl Heinz,
> An eben dieser Stelle kann der Rückgabewert von CDialog::DoModal()...
Das war das Wissen, das mir fehlte. (Hatte ich mir zwar schon angesehen,
hatte aber offenbar Tomaten auf den Augen).
> Was ein Dialog nicht tun sollte: Direkt auf der Datenstruktur des> Dokuments arbeiten.
Ja, und genau das hatte ich vor und es kam mir selber nicht ganz koscher
vor.
Vielen dank für Eure ausführlichen Erklärungen - hat mir sehr
weitergeholfen.
Gruß,
Nils