Hallo,
ich habe in MFC einen nicht modalen Dialog erstellt. Das war auch
ersteinmal kein großes Problem. Es stellt sich jedoch die Frage, wie ich
den Datenaustausch am besten gestalte. Also sobald sich Daten im
Dokument ändern müssen die angezeigten Werte im Dialog aktualisiert
werden.
Alle Daten auf die der Dialog zugreift werden nur von einer Methode im
Dokument verändert. Ich würde es gerne so handhaben, dass bei jedem
Aufruf der Methode auch die Daten im Dialog aktualisiert werden.
Hat jemand einen Vorschlag, wie so etwas am geschicktesten realisiert
werden könnte?
Ich habe mehrere Ideen gehabt, diese sind jedoch alle etwas unschön.
-Zyklische Abfrage alle n ms.
-Aufruf einer statischen Methode des Dialoges vom Dokument aus.
(Schlecht Skalierbar, da ich das Dokument immer anpassen muss)
... und ja, das Dokument müsste, wenn es sich ändern, dem Dialog
Bescheid geben. Dieser fragt dann alle Werte ab und zeigt sie an, wenn
sich ein Wert gar nicht geändert hat, macht man halt etwas zu viel
arbeit. Das regelmäßig Abfragen ist, sagen wir, völlig bescheuert - denn
das Programm weiß ja, wann sich die Daten ändern, und muss sich selbst
dennoch regelmäßig danach fragen?!
A. R. schrieb:> Ich würde es gerne so handhaben, dass bei jedem> Aufruf der Methode auch die Daten im Dialog aktualisiert werden.
Sende eine Nachricht an den Dialog, der sich daraufhin die
aktualisierten Daten aus dem Dokument besorgt. So eine Nachricht kannst
Du durch Nutzen von WM_USER zuzüglich einer von Dir zu verwaltenden
Konstante erzeugen.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931%28v=vs.85%29.aspx
Nachtrag:
Du musst also im Dokument eine Funktion zur Verfügung stellen, die die
aktuellen Daten liefert. Der Dialog kann diese Funktion auch beim
Initialisieren (also als Reaktion auf WM_INITDIALOG bzw. in
OnInitDialog) nutzen, somit ist das die einzige erforderliche
Schnittstelle, um die Daten in den Dialog hineinzubekommen.
>OnChange,OnMouseButton,OnMouseClick,... ruft UpdateData() auf
UpdateData() geht leider nur in einem View nicht in einem Dialog. Bzw.
in der CPropertyPage wo ichs verwenden möchte lässt es sich per
Klassenassistent nicht einfügen.
>MFC? Das verwendet noch jemand? Man gucke sich Gtk oder Qt an...
Was spricht gegen MFC, warum ist Gtk oder Qt besser? Eine kurze
Ausführung währe sehr interessant.
>... und ja, das Dokument müsste, wenn es sich ändern, dem Dialog>Bescheid geben. Dieser fragt dann alle Werte ab und zeigt sie an, wenn>sich ein Wert gar nicht geändert hat, macht man halt etwas zu viel>arbeit. Das regelmäßig Abfragen ist, sagen wir, völlig bescheuert - denn>das Programm weiß ja, wann sich die Daten ändern, und muss sich selbst>dennoch regelmäßig danach fragen?!
Eben genau das wollte ich nach Möglichkeit etwas vermeiden. Der Dialog
darf wissen, dass es ein Dokument gibt. Im Dialog kann ich den Header zu
dem Dokument einfügen. Jedoch wollte ich nicht zusätzlich noch dem
Dokument mitteilen, dass es den Dialog gibt. Dieser wird schließlich von
der VIEW-Klasse aufgerufen. Das wird mir sonst etwas zu verschachtelt.
-VIEW ruft dynamisch den Dialog auf.
-VIEW teilt Dokument mit, dass es den Dialog gibt.
-Dokument teilt dem Dialog mit, dass sich Werte geändert haben.
-Dialog ruft Methode des Dokuments auf und hohlt sich die Daten.
>Sende eine Nachricht an den Dialog, der sich daraufhin die>aktualisierten Daten aus dem Dokument besorgt. So eine Nachricht kannst>Du durch Nutzen von WM_USER zuzüglich einer von Dir zu verwaltenden>Konstante erzeugen.
Das klingt sehr vielversprechend.
Habe mich nun durch verschiedene Lektüren im Internet glesen und ein
bisschen programmiert. Leider ist der Erfolg ausgeblieben. Könnte bitte
mal wer drübergucken.
Im Dokument habe ich folgenden Code erstellt.
1
PostMessage(HWND_BROADCAST,WM_APP+1,0,0);//HWND_BROADCAST sollte heißen, dass die Nachricht an alles gesendet wird. Weiß nicht, wie ich ein HWND bekommen soll
HWND_BROADCAST sendet an alle Applikationshauptfenster. Du aber willst
nur an den Dialog senden. Ist der Dialog (also die Instanz der Klasse
CGraph_Dialog_etc.) Deinem Dokument bekannt?
Dann kannst Du eine Memberfunktion der von CDialog abgeleiteten Klasse
verwenden (CDialog ist wiederum von CWnd abgeleitet, und das wiederum
hat eine Memberfunktion namens PostMessage).
Also:
Angenommen, Dein Dialogobjekt hieße Dlg. Dann musst Du nur schreiben:
Dlg.PostMessage(WM_USER + 1, 0, 0);
Statt PostMessage kannst (und solltest) Du übrigens das synchrone
SendMessage verwenden. Das kehrt erst dann zum Aufrufer zurück, wenn der
Adressat die Nachricht verarbeitet hat.
Du kannst aber auch das Fensterhandle des Dialogs bestimmen, das ist die
Membervariable m_hwnd (definiert in der Basisklasse CWnd).
Es gibt wahrscheinlich viele Möglichkeiten, wie man einen nicht modalen
Dialog ins System einbringen kann.
Ich hab mal ganz schnell ein Beispiel zusammengestellt für eine MDI
Applikation, die einen nicht modalen Einstelldialog für Daten im
Dokument beinhaltet.
Der Dialog gehört dabei zum Mainframe und wird vom View, wenn er
aktiviert wird über das gerade aktive Dokument informiert. Damit können
sich mehrere Dokumente problemlos denselben Dialog teilen. Wird ein View
eines Dokuments aktiviert, dann schaltet der Dialog auf das jeweilige
Dokument um, und holt sich von ihm die anzuzeigenden Daten.
Wird im Dialog ein Wert verändert, dann teilt der Dialog das auch dem
zugehörigen Dokument mit (welches dann seinerseits wieder alle Views von
der Änderung informiert).
Damit gibt es eine 2-Weg Kommunikation
* Aus dem Dialog heraus, können Dokumentdaten verändert werden
(und der Rest des Systems [View, Menues, Toolbars] passt sich an)
* Aus dem Menue/View heraus können die Daten verändert werden und
der Dialog reagiert auf die Änderung
In diesem Beispiel wissen die Klassen voneinander und rufen ihre
Methoden gegenseitig auf. Die allgemeinere Form wäre natürlich, wenn da
Message Passing betrieben werden würde. In der App, aus der ich die
Einzeilteile zusammenge'klaut' habe, passiert das auch so, hauptsächlich
deshalb, weil diese App zur Laufzeit mit zusätzlich ladbaren DLLs
erweiterbar ist, die sich nahtlos während des Betriebs in das Programm
einklinken können müssen. Weder Dokument noch Dialog wissen voneinander
in Form von Member-Funktionen, sondern nur in Form von zu sendenden
Messages bzw Aufrufen von UpdateData.
Der springende Punkt ist aber der, dass hier der Dialog zu keinem
Document gehört und auch nicht zu einem View, sondern die Mainfrm für
die Erzeugung und Verwaltung zuständig ist.
(Die App wurde mit VC++ 6.0 gemacht. Also himmelalt. Mit neueren
Versionen wirst du wahrscheinlich ein paar Fehlermeldungen oder
zumindest Warnings bekommen.
Interessant sind die Menüpunkte:
Ansicht / Eigenschaften -> Dialog geht auf / zu
Form / Rechteck, Form / Kreis -> Stellvertreter für Datenmanipualtion
und natürlich Datei/Neu, Fenster/Neu etc.
> in der CPropertyPage wo ichs verwenden möchte lässt es sich> per Klassenassistent nicht einfügen.
Der Klassenassistent ist nicht alles. Manches muss man einfach von Hand
einfügen :-)
Vielen Dank an euch beide!
Ich werde es vorraussichtlich vorerst mit Membermethoden machen. Werde
mich aber trotzdem in die Thematik mit den Nachrichten etwas
einarbeiten.
Mittlerweile habe ich verstanden, wie das gelöst worden ist. Mein
Problem war ständig, dass ich einen Zirkelinklude programmiert habe weil
2 Klassen gegenseitig aufeinander zugreifen müssen.
Das habe ich jetzt so wie im Beispiel von Karl Heinz Buchegger gelöst.
Im Header des Dokuments und des nicht modalen Dialogs ist lediglich eine
Forward-Declaration zu der jeweils anderen Klasse. Die Funktionen werden
erst in der .cpp verwendet, so das erst dort das Einbinden des Headers
der jeweils anderen Klasse notwendig wird.
Der Datenaustausche zwischen Dokument und Dialog funktioniert
einwandfrei.
Nun würde ich gerne noch einen Datenaustausch zwischen Dialog und einer
Klasse für ein Diagramm realisieren. Dieses befindet sich im Dialog und
gibt die Werte des Dokuments an. Hierbei wäre es sehr schön, wenn ich
eine Standardklasse des Diagramms an mehreren Stellen im Programm
verwenden kann.
Ist es möglich, einer Klasse (Diagramm) einen Funktionpointer bzw. einen
Funktor einer beliebigen Methode einer beliebigen Klasse zu übergeben,
so dass diese aufgerufen wird? Eine Parameterübergabe ist nicht
notwendig und der Rückgabewert kann auch void sein.
Um nun keinen Funktor übermitteln zu müssen habe ich die Methode die
aufgerufen werden soll mit dem Zusatz static versehen.
Trotzdem bekomme ich beim ausführen folgender Methode eine Fehlermeldung
die sich im Anhang befindet.
1
m_grid1.SetMeineFunktion(Diagramm_Wert_geändert);
Ich werde nun noch versuchen, eine Funktion zu übergeben, die keine
Methode ist. Innerhalb dieser Funktion könnte ich dann versuchen die
Methoden aufzurufen.
A. R. schrieb:> Ich werde nun noch versuchen, eine Funktion zu übergeben, die keine> Methode ist
Wenn die Funktion eine Memberfunktion einer Klasse ist, dann muss sie
eine statische Memberfunktion sein, also eine, die auch ohne eine
existierende Objektinstanz der Klasse auskommt (und folglich auch keinen
this-Pointer hat).
Das ist exakt der Code, den Du Deinem Compiler vorwirfst?
Und nicht nur etwas, was Du aus dem Gedächtnis hier so ähnlich abgetippt
hast?
Einerseits: Was Du da oben gepostet hast (als Screenshot) ist keine
Compilerfehlermeldung, sondern nur eine von "Intellisense", was für das
Syntaxhighlighting zuständig ist. Das aber kann durchaus danebenliegen.
Übersetze den Code, und sieh Dir an, was im Output-Window steht.
Andererseits: Erlaubt Dein Compiler wirklich Funktionsnamen, die nicht
mit 7-Bit-ASCII codiert sind?
>Das ist exakt der Code, den Du Deinem Compiler vorwirfst?>Und nicht nur etwas, was Du aus dem Gedächtnis hier so ähnlich abgetippt>hast?
Ja, nur Testweise in einem Probleprogramm um zu testen ob die Syntax
stimmt.
>Einerseits: Was Du da oben gepostet hast (als Screenshot) ist keine>Compilerfehlermeldung, sondern nur eine von "Intellisense", was für das>Syntaxhighlighting zuständig ist. Das aber kann durchaus danebenliegen.
Bis jetzt hat Initellisense immer richtig gelegen oder ich habe es nicht
gemerkt. Es wäre auch äußerst unschöner Code wenn mir Intellisense
etliche Fehlermedlungen ausgibt die nicht existieren.
>Andererseits: Erlaubt Dein Compiler wirklich Funktionsnamen, die nicht>mit 7-Bit-ASCII codiert sind?
Ja
Habe das Problem nun gelöst.
Im Header der Klasse der Tabelle befindet sich folgender Code.
1
typedefvoid(*MyFunctionPointer)(void*);//Legt den Funktionspointer fest
2
3
classMyCug_Standard:publicCUGCtrl
4
{
5
protected:
6
MyFunctionPointerCallbackfunktion;//Deklariert einen Funktionspointer
7
void*Callbackfunktion_Parameter;//Deklariert einen Übergabeparameter des Typs Void
8
public:
9
voidSetCallbackfunktion(MyFunctionPointerFunktionspointer,void*data);//Übergibt einen Pointer auf die Callbackfunktion die bei einer Änderung aufgerufen wird.
10
//Der Void Pointer wird von der Callbackfunktion übergeben und kann für beliebige Date verwendet werden.
11
//Z.B. um von der Callbackfunktion ausgehend eine Methode einer Klasse aufzurufen
12
}
Von der Dialogklasse rufe ich die Settermethode wie folgt auf:
Fertig. Nun bin ich in der Lage der Klasse des Diagramms eine beliebe
Funktion inklusive Objektpointer zu übergeben. Wenn sich ein Wert im
Diagramm ändert ruft die Klasse die Funktion auf und übergibt Ihr den
Objektpointer. Von dieser Funktion aus rufe ich die Methode auf.
Nutzen: Ich kann die Klasse von jeder beliebigen anderne Klasse aus
aufrufen und bekomme eine Methode meienr Klasse aufgerufen, sobald sich
ein Wert ändert.