[Erledigt] Wie bemerkt CFrameWnd(Ex) ein neues Dokument?



  • Hallo da!
    In nächster Zeit könnten sich leider meine Fragen bezüglich SDI-Anwendungen häufen 😞
    Und zwar habe ich zuvor immer (und auch das war selten) nur MDI-Anwendungen geschrieben. Seit Anbeginn wollte und hatte ich immer mehrere Views (unterschiedlicher Klassen) auf ein Dokument. In meinen Anwendungen habe ich diese daher in einer CMDIFrameWnd-abgeleiteten Klasse orchestriert (Tab-Controls da drinnen, mehrere Views erstellt und dem Dokument hinzugefügt etc.). Nun hatte ich damals schon das Problem, dass ich nicht herausgefunden habe, wie ein FrameWnd mitbekommt, wann es einem Dokument zugeordnet ist (Obwohl ich weiß, dass während ein neues Dokument erstellt wird, eine Methode in der Basisklasse aufgerufen wird (InitialUpdateFrame), aber da komme ich ja nicht dran, weil sie nicht virtuell definiert ist). Ich hatte mir damals damit beholfen, dass ich in das MultiDocTemplate einen "proxy"-View gegeben habe, der, wenn er OnInitialUpdate bekommt das FrameWnd benachrichtigt, welches darauf den View wieder kaputt macht und dann die neuen Views erstellt usw. (sic!)
    Nun habe ich aber in der SDI-Anwendung das Problem, dass überhaupt nur ein Mal ein neues Dokument erzeugt wird, dass dann ja wiederverwendet wird. Somit wird auch dieser "Proxy"-View nur am Anfang erstellt, hinterher nicht mehr. Jetzt müsste ich also in all meinen Views in der OnInitialUpdate-Methode das FrameWnd benachrichtigen, dass es ein neues Dokument gibt (Der dann die Views wieder in der Ausgangstelleung arrangiert usw.).
    Langer Text, kurzer Sinn, wie findet man als CFrameWnd(Ex)-abgeleitete Klasse in einer SDI-Anwendung am besten heraus, dass ein neues Dokument erstellt wurde oder ein anderes geöffnet?

    Sorry für die Textwand 😞
    Viele Grüße,
    Michael


  • Mod

    1. SDI! SINGLE DOCUMENT!
    2. Ein Frame interessiert ganz und gar nicht, dass es ein Dokument gibt. Ein Frame ist nur an einem View interessiert. Es gibt keinerleit Koppelung von Frame und Doc! Deshalb merkt so etwas ein Frame gar nicht!
    3. Du kannst mehrere Views haben, aber ein View wird beim Start einem Dokument zugeordent. Und ein View wird einem Frame zugeordnet.
    4. Auch das Erzeugen eines Dokumentes bedeutet gar nichts. Das kann ja auch ohne Frame/View existieren.

    Der Trick wie das die MFC macht ist elementar einfach. Die MFC gibt beim erzeugen des CFrameWnd einfach einen CreateParam mit. Die Struktur ist öffentlich und heißt CCreateContext, in der stehen die Infos.

    Nach meinem Dafürhalten hast Du ein Design-Problem.
    Und um das zu lösen müsstest Du mehr sagen über Deine Anwendung.



  • Hrmm, naja, wer soll denn sonst dafür sorgen, dass die einzelnen Views, die mit dem Dokument verbunden sind, in einer bestimmten Art und Weise auf dem Bildschirm angeordnet sind, denn das Fenster in dem sie alle zusammen als "Kinder" geführt werden? Oder sollte ich einen Layout-View haben, der selber weitere Views erstellt und diese alle sind dann Kinder des Hauptviews?
    Im Moment habe ich ja zum Glück noch kein Design-Problem, weil das natürlich so elementar war, dass ich nicht weitermachen konnte (Ich entwickle die Views derzeit nur getrennt).
    Also im Prinzip werde ich 3 unterschiedliche Viewklassen (Ein View für Messkonfiguration und Konfigurationsstatus während der Datenaufnahme und 2 Views für unterschiedliche Datenkurven) für das einzige Dokument haben. Die drei Views sollen per Tab-Control wechselbar sein. Die einzelnen Views sollen dann evtl. noch über DockablePane's unterstützt werden, aber für deren "Ausfahren" oder ähnliches können ja in OnActivate oder so selber sorgen. Da ich aber damit rechne, evtl. doch nochmal Änderungen daran vorzunehmen, wäre mir eine Zentrale Stelle lieb, die sich um die Layout-Geschichten kümmert und die 3 Views sozusagen als Datenmember hat. Dass ich da zuerst an das FrameWnd im SDI-Fall und an das CMDIChildWindow im MDI-Fall gedacht habe, ist ja offensichtlich falsch gewesen.

    Vielen Dank!
    Michael

    PS: Mit "neues Dokument" meinte ich im SDI-Fall natürlich eines, dass per OnNewDocument oder ähnlichem neu initialisiert wurde.


  • Mod

    Ich verstehe nicht was Du willst, bzw. wo Du ein Problem hast!

    Für was musst Du wissen, dass ein neuer View erzeugt wurde, wnen Du jederzeit die View über die Doctemplates des Dokumentes oder über das Dokument selbst enumerieren kannst?
    Ein Frame muss dies doch in keiner Weise wissen, denn es ist doch nur der Container. Schon hgnaz und gar nicht, wenn Dumit Splittern oder anderen Techniken arbeitest.

    Du erzeugst einen View zu einem Dokument und gut ists.
    Was Du wie in dem Frame anzeigst und anordnest ist dem Frame egal...



  • So, um das ganze nochmal ins rechte Licht zu rücken.
    Was ich habe: 4 CView-abgeleitete-Klassen

    • class commandView : public CView;
    • class messageView : public CView;
    • class graphView : public CView;
    • class dataView : public CView;

    Nun möchte ich folgendes Aussehen bzw. Verhalten:
    Am unteren Ende des Clientbereichs soll eine DockablePane (abgeleitet, ich würde die Klasse beispielsweise statusPane nennen), welches einen messageView, einen CommandView und einen graphView enthält.
    Am oberen Ende des Clientbereichs soll ein CMFCTabControl sein, mit dem ich für den Rest des Clientbereichs 3 verschiedene Views mit unterschiedlicher Konfiguration anzeige (Ein "Config" Tab zaubert einen graphView hervor, und 3 andere Tabs zaubern jeweils dataViews mit unterschiedlichen Einstellungen hervor). Nun hat zumindest der graphView einigen Verwaltungsaufwand, das heißt, ich möchte ihn nicht immer dynamisch erzeugen wenn man den Tab auswählt. Bei den anderen Tabs würde ich auch bevorzugen, wenn sie statisch (als Datenmember) vorgehalten würden.
    Nun ist mein Problem: Wo setze ich an, dieses Layout umzusetzen. FrameWnd soll ja, wie ich bereits gelernt habe, nichts damit zu tun haben. Will heißen, die Views einzeln funktionieren super, aber an welcher Stelle füge ich sie zu einem Gesamten zusammen? Könnte ich einen layoutView (von CView abgeleitet) umsetzen, der im Prinzip nichts anderes macht als neue Views und Panes zu erstellen und mit dem Dokument zu verbinden (Können DockablePanes an den Rand von Views gedockt werden, die haben ja eigentlich gar kein Konzept von Non-Clientbereich?)

    Ich hoffe das bringt etwas Licht ins Dunkle.

    Viele Grüße,
    Michael



  • Also ich habe nun weiter das ganze Internet zu dieser Problematik durchforstet. Die Ratschläge gehen von "Benutze MDI und beschneide es um die Fähigkeit, mehr als ein Dokument gleichzeitig zu öffnen" in den VC++-Foren bis hin zu "Benutze OnCreateClient des FrameWnd" (Ganz genau so, wie ich das in meinen MDI-Anwendungen gemacht habe), um die ganzen Views zu erstellen, aus Stanford. Also es kann ja sein, dass ich die MFC derzeit falsch benutze um das Ziel zu erreichen, dass ich erreichen möchte, aber es ist doch bestimmt kein Designfehler, dass ich verschiedene Views auf ein und dasselbe Dokument in einem bestimmten halb-fixen Layout über die Lebenszeit des Dokuments verwalten möchte.
    Wenn das FrameWnd nun die Views erstellen und arrangieren soll (Dazu muss es dem Dokument ja die Views bekanntmachen bzw. je nach Sichtweise andersherum), muss es aber zuerst einmal den genauen Zeitpunkt kennen, wann es mit einem Dokument verbunden ist. Darum werde ich nun einmal den Weg von DocTemplate bis hin zum erstellten FrameWnd durch Debuggen nachvollziehen, vielleicht finde ich da ja etwas.

    Ich hoffe, ich habe das Problem in diesen beiden Posts so gut erklärt, dass es einigermaßen verständlich rüberkommt. Unter allen Umständen möchte es möglichst kompatibel zu dem, was die MFC erwarten, lösen, damit ich nicht am Ende böse überrascht werde, weil irgendwas so einfach nicht hinhaut.
    Und ja, ich bin mir über Splitter-Wnd's im Klaren, aber erstmal finde ich, dass sie standardmäßig irgendwie bekloppt aussehen, nicht direkt zu meinem Problem passen und zweitens müssen auch Splitterwnd's gefüllt werden, und das passiert ja dann auch in der FrameWnd-abgeleiteten-Klasse?

    Viele Grüße,
    Michael



  • Okay, nachdem ich das mit dem Debugger nachvollzogen hat, erstellt CFrameWnd in OnCreateClient per AddView und der Runtime-Klasse des Views aus der CreateContext-Struktur den View. Ich denke, das werde ich einfach unterbinden und an der Stelle die ganze UI zusammenbasteln (Panes erstellen, Tab Control etc.) und dann dem Dokument (aus dem CreateContext) die ganzen Views bekanntmachen.
    Muss ich da irgendwelche Nebenwirkungen befürchten, wenn ich nicht höllisch aufpasse?

    Viele Grüße,
    Michael


  • Mod

    Ich hatte Dir doch geschrieben, dass Dies per CreateParam an das Frame weitergegeben wird.
    Man unterbindet das ganz einfach indem Du nicht die Default-Implementierung von CFrameWnd::OnCreate aufruft, oder einfach OnCreateClient leer überschreibt.

    Das Frame macht nur was man ihm sagt. Zuest wird das Dokument erzeugt. Dann das Frame, dass dann die Views erzeugt.
    Wenn Du eine spzielle Konstruktion möchtest, kann das Frame natürlich mehrere Views erzeugen und diese anbinden an sich. Es ist nicht notwendig, dass ein View sichtbar ist.
    Üblich ist auch, dass man im Frame ein Splitter-Window erzeugt, dass wiederumg mehrere Views hat.

    Aber Achtung! Ein View muss immer ein Frame als Parent haben. Also so wahlfrei mit docken des Views ist nicht ganz... Du musst darauf achten, dass das Parent immer ein CFrameWnd ist, oder zumindest in der Kette der Parents muss ein CFRameWnd sitzen!

    BTW: Bzgl. docken und eweiterten UI Funktionen bietet die MFCNext in VC-2008 SP1 einiges.



  • Ja, aber du hattest ja auch noch einmal ganz besonders betont, dass das FrameWnd von Views gar nichts weiß, aber in meinem Fall weiß es von allen Views (Also natürlich meine eigene Klasse von FrameWndEx abgeleitet) und hat diese auch eigenhändig oder indirekt erstellt und sie sind sogar Datenmember. Das hatte mich etwas verwirrt, so dass ich angenommen hatte, mein Ansatz geht total ins Leere.
    Also wenn ich in meiner OnCreateClient überschriebenen Methode alle weiteren Views und DockablePanes erstelle, dann einfach SetParent(this). In den DockablePanes (deren übergeordnetes Fenster ja dann das FrameWndEx ist) kann ich wiederum SetParent(this) benutzen, weil in der Hierarchie dann ja trotzdem (über eine Ebene) das FrameWnd der "Vater" ist? Dann noch die ganzen Views per Document->AddView und schon sieht das doch nach einer Sache mit Hand und Fuß aus! Wunderbar!

    Zur Entstehungsgeschichte meines proxyViews, den ich, wie weiter oben beschrieben, in meinen MDI-Anwendungen benutzt habe: Damals hatte ich in den OnCreate-Methoden immer versucht GetActiveDocument zu benutzen, um an das Dokument zu kommen, was natürlich konsequenterweise immer einen 0-Zeiger bzw. ein falsches Dokument geliefert hatte. Auf die Idee mit den CreateParam-Membern rumzuhantieren bin ich aber nie gekommen (Ob das zu viel Respekt vor den magischen Innereien der MFC war? 🙂 ) Naja, jetzt kann ich mein damaliges Vorgehen zum Glück belächeln und evtl. irgendwann nochmal diese Schmu bereinigen 🙂

    Danke, dass du noch so ruhig mit mir geblieben bist 😉
    Fröhliche Weihnachten schon einmal!
    Michael

    PS: Danke für den Tip mit dem FeaturePack. Dessen CDockablePane und schönen Toolbars und alles gedenke ich auch zu benutzen 🙂 Wie immer werde ich mir dann wieder einen Abbrechen, einigermaßen hübsche Button-Bitmaps zu zaubern... Künstlerisch begabt müsste man sein 😞


Anmelden zum Antworten