Wie schließe ich ein Dokument? (SDI)



  • achso, du willst eigentlich, daß immer nur eine View offen ist (und nicht etwa Views versteckt hinter der aktuellen View, die das Frame-Window momentan ausfüllt, liegen). Dann hoffe ich einmal, daß du zum Umschalten der Views den Code aus diesem MSDN-Artikel:

    http://support.microsoft.com/default.aspx?scid=kb;EN-US;99562

    benutzt, denn der schaltet zuverlässig die Views um, und mit Hilfe der dort vorgestellten Methode SwitchView kann man auch eine View wieder zerstören, denn sie liefert praktischerweise einen Zeiger auf die vorher aktive View zurück, beachte dort den Hinweis unter "Note". Dann mußt du auch nicht befürchten, eine schon offene View noch mal zu öffnen, da du durch die Zerstörung der Views dafür sorgen kannst, daß immer nur eine existiert. Ich hoffe mal, du kannst diesen Code bei dir irgendwie unterbringen...

    MfG



  • Oh, dankeschön. 🙂

    Wird gleich gelesen. Hoffentlich isses nicht zu kompliziert.

    Du musst echt viel wissen, hast mir schon wieder geholfen. 🙂



  • So, ich hab es versucht, bin aber leider gescheitert.

    Wo ist in dem Beispiel denn das Dokument zu dem View berücksichtigt? Meine Views haben alle ein eigenes Dokument.

    Also habe ich versucht, meine alte Version umzubauen aber das geht auch nicht.

    bool CDocViewManager::ShowDocTemplate(int f_nIndex)
    {
    	CWaitCursor wait;
    
    	bool fSuccess = true;
    
    	CView* pView = NULL;
    	if (m_pCurrent)
    	{
    		pView = ((CFrameWnd*)AfxGetMainWnd())->GetActiveView();
    		ASSERT(pView); // <--- Stelle merken
    	}
    
    	if (f_nIndex >= m_arrTemplates.GetSize())
    	{
    		fSuccess = false;
    	}
    	else
    	{
    		// Anderen View anzeigen
    		m_pCurrent = (CDocTemplate*)m_arrTemplates.GetAt(f_nIndex);
    		fSuccess = (NULL != m_pCurrent->OpenDocumentFile(NULL));
    
    		// Den anderen View löschen
    		if (pView)
    		{
    			pView->DestroyWindow();
    		}
    	}
    
    	return fSuccess;
    }
    

    Einmal von View 1 auf 2 umschalten geht - aber wieder zurück geht nicht, weil der ASSERT oben anschlägt. Ich habe also keinen aktuellen View. Wie behebe ich das? Was hab ich falsch gemacht?

    Oder kann ich dem Beispiel aus dem Artikel irgendwie ein Doc "unterschummeln"?

    Sind denn solche Anwendungen wie ich sie hier habe sooo selten? 😞



  • also dein jetziger Code hat nicht mehr viele Gemeinsamkeiten mit dem aus dem Link, den ich oben angegeben habe. Allerdings hat auch die Überschrift zu deiner ersten Frage (SDI) auf eine andere Struktur deiner Anwendung schließen lassen, wenn ich es jetzt noch einmal lese, steht dann dort aber auch "mehrere Doc-View-Kombinationen".., na gut, dann könntest du zwei Dinge probieren, die mir zunächst einfallen: du versuchst, den pView-Zeiger dir auf andere Weise zu holen bzw. zu merken (irgendwo muß der Zeiger ja gültig sein), oder, da es offensichtlich keine aktive View gibt, rufe doch einfach selber SetActiveView auf, dann sollte es auch eine solche geben, und der Zeiger gültig werden

    MfG



  • Wenn ich deinen Text so lese habe ich das Gefühl, dass ich gewaltig in die falsche Richtung marschiert bin. 😞

    Hätte ich denn eine MDI Anwendung machen müssen?
    Ich dachte eine SDI wäre die richtige weil doch immer nur eines zur gleichen Zeit da sein soll.
    Ich habe ziemlich viel gesucht und gelesen aber ich habe nichts gefunden, was meinem Projekt so weit ähnelte, dass ich davon lernen konnte. 😞

    Ich habe das mit SetActiveView mal versucht und das klappt besser als die Version vorher. Allerdings habe ich Memoryleaks und bekomme diesen Assert:

    void CFrameWnd::SetActiveView(CView* pViewNew, BOOL bNotify)
    {
    #ifdef _DEBUG
    	if (pViewNew != NULL)
    	{
    		ASSERT(IsChild(pViewNew)); // <------- Den hier.
    		ASSERT_KINDOF(CView, pViewNew);
    	}
    #endif //_DEBUG
    

    Was sagt mir das? Auf jeden Fall ist das immernoch nicht okay. 😞

    Wie ich auf andere Weise an den Viewzeiger komme, weiß ich nicht. Ich versuche jetzt nochmal, den this Zeiger in OnInitialUpdate des Views an die App zu geben. Hoffentlich funktioniert das.

    Sag mir bitte, wenn ich in die falsche Richtung arbeite, ich sollte so langsam mal mein Gerüst lauffähig bekommen. 😞
    Sollte ein Umstieg auf .net nötig sein, damit ich sowas machen kann, dann könnte ich das Ruder auch noch irgendwie rumreißen.

    Danke 🙂



  • also dafür mußt du nicht auf .NET umsteigen...da geht zwar vieles einfacher, aber die .NET-Framework-Klassen haben nichts mehr mit MFC zu tun (solltest du den Umstieg auf die neue Version der MFC mittels VS-C++.NET meinen, da gibt es keine wesentlichen Änderungen im Document/View-Modell, so daß du das nicht deswegen machen mußt)

    Also der Unterschied MDI/SDI ist im wesentlichen der optische (aus dem dann natürlich die unterschiedlichen Implementierungen der Modelle folgen): bei SDI werden alle Ansichten innerhalb eines (Haupt-)Rahmenfensters dargestellt, bei MDI werden im Hauptrahmenfenster mehrere Ansichten, die in eigenen Rahmenfenstern dargestellt werden, verwaltet. Am besten stellst du dir, wenn du entscheiden müßtest zwischen MDI/SDI, vor, brauche ich so eine Funktionalität wie in WORD (viele Fenster gleichzeitig offen, Fenster können getrennt minimiert, maximiert werden usw...), dann nehme ich MDI, reicht mir das Verhalten von Notepad, dem Editor (wo man nur eine Schreib-Ansicht hat, und auch nur eine zu einem Zeitpunkt offen haben kann), dann nehme ich SDI.

    Da du geschrieben hast, es reicht, daß immer nur eine View offen ist, wäre SDI also prinzipiell geeignet. Ok, zum Problem, der ASSERT ist doch immer wieder nützlich, in diesem Falle meckert er, daß deine View, die du aktivieren willst, kein Child des Rahmenfensters ist, was aber eine Eigenschaft einer korrekten SDI-Anwendung ist. Entweder hast du das beim Create vergessen, oder du hast die Child-Eigenschaft irgendwie entzogen. Dein Create sollte so aussehen:

    m_pView->Create(NULL, "AnyWindowName", WS_CHILD, rect,
                                  m_pMainWnd, viewID, &newContext);
    

    wichtig ist hier der Stil WS_CHILD, mit dem die View zum Child des Hauptrahmenfensters (angegeben im Argument m_pMainWnd) wird. Schau doch noch mal noch, ob es das war...

    zu den Memory-Leaks kann ich so nichts sagen, da mußt du einmal überprüfen, ob irgendwo ein delete nicht stattfindet (beim Zerstören der alten Views z.B.)

    MfG



  • So einen Create-Aufruf habe ich gar nicht. 😕
    Ich habe den Stil mal im PreCreateWindow festgelegt:

    BOOL CBasisView::PreCreateWindow(CREATESTRUCT& cs) 
    {
    	cs.style &= WS_CHILD;
    	return CFormView::PreCreateWindow(cs);
    }
    

    Aber das tuts nicht, der ASSERT kommt immer noch. 😞

    Ich glaube ich muss meinen TemplateManager wegschmeißen. Momentan arbeite ich ja mit CSingleDocTemplates, um den View, das Doc und den Mainframe (das ist immer CMainFrame) irgendwie zusammenhängen zu lassen. Eine Abwandlung von dem automatisch erstellten Standard eben.

    Ich habe nämlich keine Ahnung, welche Mechanismen da drin versteckt sind, die den Zusammenhand zwischen Doc-View-Frame betreffen.

    Nun die Frage: Kann ich auch ohne diese Templates arbeiten? Wie mache ich dem Dokument klar, wer sein View ist und umgekehrt? Wo kommt CMainFrame ins Spiel? Wie funktioniert das mit dem Menü und der Toolbar?

    Momentan sieht das für eine Kombination ja so aus:

    void CAudiodatic_IIIApp::RegisterDocView(CRuntimeClass* f_pDocClass, CRuntimeClass* f_pViewClass)
    {
    	// Neues Template erstellen
    	CSingleDocTemplate* pDocTemplate = new CSingleDocTemplate(
    		// Welches Menü (immer das normale)
    		IDR_MAINFRAME,
    		// Welche Dokumentenklasse
    		f_pDocClass,
    		// Welches Rahmenfenster (immer das normale)
    		RUNTIME_CLASS(CMainFrame),
    		// Welche Viewklasse
    		f_pViewClass);
    	// Hinzufügen (Standard)
    	AddDocTemplate(pDocTemplate);
    
    	// In meinem Manager hinzufügen
    	m_DocViewManager.AddDocTemplate(pDocTemplate);
    }
    

    Den Quellcode zum Anzeigen eines Views hast du ja schon gesehen.

    So, wäre es besser, die Klassen selbst "miteinander bekannt zu machen" und zu erstellen? Oder doch besser den bisherigen Weg?

    Zu den Speicherlecks habe ich noch nichts gefunden. Sie treten allerdings nur auf, denn ich mindestens einmal umgeschaltet habe.
    Ich zerstöre das Fenster mit dem Aufruf hier:

    pView->DestroyWindow();
    

    RemoveView rufe ich nicht auf, weil ich ja das Dokument auch nicht mehr brauche. Ich das vielleicht der Fehler mit dem Speicher? Ich guck nochmal.

    Hier ist mal die Auflistung:

    Detected memory leaks!
    Dumping objects ->
    strcore.cpp(118) : {1533} normal block at 0x004330A0, 90 bytes long.
     Data: <    %   M   Dr c> 01 00 00 00 25 00 00 00 4D 00 00 00 44 72 FC 63 
    array_p.cpp(110) : {1199} normal block at 0x00433E20, 20 bytes long.
     Data: <     -C         > 00 00 00 00 E0 2D 43 00 00 00 00 00 CD CD CD CD 
    array_p.cpp(71) : {1198} normal block at 0x00432060, 4 bytes long.
     Data: <    > 00 00 00 00 
    winfrm2.cpp(66) : {1197} client block at 0x00433E60, subtype 0, 168 bytes long.
    a CDockBar object at $00433E60, 168 bytes long
    array_p.cpp(71) : {1196} normal block at 0x00432090, 4 bytes long.
     Data: <    > 00 00 00 00 
    winfrm2.cpp(66) : {1195} client block at 0x00433F40, subtype 0, 168 bytes long.
    a CDockBar object at $00433F40, 168 bytes long
    array_p.cpp(71) : {1194} normal block at 0x00432350, 4 bytes long.
     Data: <    > 00 00 00 00 
    winfrm2.cpp(66) : {1193} client block at 0x004320C0, subtype 0, 168 bytes long.
    a CDockBar object at $004320C0, 168 bytes long
    winfrm2.cpp(66) : {1191} client block at 0x004321A0, subtype 0, 168 bytes long.
    a CDockBar object at $004321A0, 168 bytes long
    bardock.cpp(735) : {1190} normal block at 0x00432380, 176 bytes long.
     Data: <  K_            > 84 01 4B 5F CD CD CD CD CD CD CD CD CD CD CD CD 
    strcore.cpp(118) : {1189} normal block at 0x00432280, 15 bytes long.
     Data: <            RF > 01 00 00 00 02 00 00 00 02 00 00 00 52 46 00 
    strcore.cpp(118) : {1188} normal block at 0x004322C0, 16 bytes long.
     Data: <            NUM > 01 00 00 00 03 00 00 00 03 00 00 00 4E 55 4D 00 
    strcore.cpp(118) : {1187} normal block at 0x00432300, 15 bytes long.
     Data: <            UF > 01 00 00 00 02 00 00 00 02 00 00 00 55 46 00 
    {1184} normal block at 0x00432460, 80 bytes long.
     Data: <    @           > 00 00 00 00 40 01 00 00 00 01 00 08 00 00 00 00 
    plex.cpp(31) : {1181} normal block at 0x004325A0, 124 bytes long.
     Data: <     %C      -C > 00 00 00 00 B0 25 43 00 00 00 00 00 E0 2D 43 00 
    strcore.cpp(118) : {1120} normal block at 0x00432BD0, 27 bytes long.
     Data: <            Audi> 01 00 00 00 0E 00 00 00 0E 00 00 00 41 75 64 69 
    D:\Realisierung\Audiodatic_III\MainFrm.cpp(20) : {1118} client block at 0x00432CA0, subtype 0, 480 bytes long.
    a CMainFrame object at $00432CA0, 480 bytes long
    Object dump complete.
    

    Das kommt bei einmal Umschalten heraus.

    Die einzige Stelle wo was erstellt wird die er findet ist diese hier:

    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
    

    Bei allen anderen gibt es die Meldung, dass die Datei nicht gefunden wurde. 😞

    Dankeschön 🙂



  • jetzt erkenne ich deinen Aufbau etwas genauer...

    also um alle von dir gestellten Fragen,die ja in die gleiche Richtung gingen, einzugehen:

    ich würde schon mit diesen Templates arbeiten (in diesem Falle CSingleDocTemplate), gerade weil man, wie du schon erwähnst, sonst die Mechanismen und Zusammenhänge alle erst einmal selbst implementieren und nachstellen muß (die durch die Verwendung des CSingleDocTemplate-"Verfahrens" einem erspart werden), wenn man das nicht tut (selbst ich habe noch nicht an diesen Stellen im MFC-Quellcode herumgewühlt, um zu schauen, wie das implementiert ist). Also sich bloß nicht zu weit wegbewegen vom "Standard".

    Vielleicht sollte ich noch etwas mehr zu SDI sagen: eine SDI-Anwendung sollte zu jeder Zeit nur ein gültiges CSingleDocTemplate-Objekt haben, ich weiß jetzt nicht, was dein Doc-Manager mit dem "alten Objekt" macht nach dem new-Aufruf. Auch sollten SDI-Anwendungen nur ein aktuelles CDocument-Objekt haben. Das Memory-Leak könnte dadurch kommen, daß du vielleicht mehrere Objekte schaffen willst, wobei ein anderes (durch den SDI-Automatismus) nicht korrekt zerstört wird.

    Man kann wohl auch mit mehreren CDocument-Objekten, die man dann dynamisch den anzuzeigenden Views zuordnet (über CSingleDocTemplate, oder CCreateContext, wenn man es dynamisch macht, wie in meinem Link angegeben), arbeiten, muß aber dann auch aufpassen

    Vielleicht hilft noch ein Link zum besseren Verständnis, und es ist eine Anwendung dabei, die ungefähr das tut, was du vorhast, so daß du noch besser erkennst, was du anders machen könntest, oder woran es hakt:

    ein Beispiel:
    http://www.codeguru.com/Cpp/W-D/doc_view/viewmanagement/article.php/c6121/

    und die Übersicht hierzu:
    http://www.codeguru.com/Cpp/W-D/doc_view/viewmanagement/

    und hier das, was Codeproject.com zu bieten hat:
    http://www.codeproject.com/docview/

    MfG



  • Dankeschön 🙂

    Das bei Codeguru sieht wirklich passend aus.

    So eine Lösung hatte ich irgendwann zu Anfang mal versucht und bin gescheitert, vielleicht klappt es jetzt ja. Ich werde meinen Stand mal wegkopieren und dann den Teil mit dem Umschalten nochmal komplett neu machen.

    Die Sache, dass ich immer nur ein Template haben darf, könnte die Fehlerquelle sein.

    Ich versuch das heute mal und dann habe ich allerdings erstmal Urlaub. Ich gebe auf jeden Fall eine Erfolgsmeldung - wenn es denn klappt. Das hoffe ich jedenfalls sehr.

    Du hast bei mir wirklich was gut. 🙂



  • Momentan kämpfe ich mit einem ASSERT. Was sollte es auch sonst sein?

    BOOL CWnd::ShowWindow(int nCmdShow)
    {
    	ASSERT(::IsWindow(m_hWnd)); // <-----
    
    	if (m_pCtrlSite == NULL)
    		return ::ShowWindow(m_hWnd, nCmdShow);
    	else
    		return m_pCtrlSite->ShowWindow(nCmdShow);
    }
    

    Meine InitInstance ist aber von der Reihenfolge der Befehle an die des Beispiels angepasst nur dass ist die Templateerstellung in eine Funktion ausgelagert habe.

    Also:
    Template0 erstellen
    ShowWindow
    UpdateWindow
    Template1 bis n erstellen

    Laut den Kommentaren auf der Seite gibt es aber Probleme mit FormViews - die habe ich leider komplett.

    Ich werde noch ne Runde forschen und auch noch andere Lösungen angucken - mal sehen ob das wirklich so schwer ist. 🙂

    Bis dann in drei Wochen 🙂



  • ..hmmm...dieser ASSERT hat zugeschlagen, weil ein Fenster angezeigt werden soll, das (noch) gar nicht existiert (oder zumindest noch nicht vollständig konstruiert ist, oder schon wieder zerstört wurde), mehr kann ich jetzt auch nicht dazu sagen...aber gut, erst mal wünsche ich einen angenehmen Urlaub...

    MfG


Anmelden zum Antworten