SDI - Mehrere Formviews in einer SDI



  • Da wohl öfters jemand vor dem Problem steht nehme ich mir mal die Zeit. 🙂

    Also: Du hast eine SDI und zwei (oder mehr) FormViews (mit jeweils einem Doc dazu)? Du möchtest dazwischen wechseln können - dann lies weiter.

    Ich beschreibe nur die groben Schritte, gewisse Grundkenntnisse setze ich einfach mal voraus - dafür gibt es ja das Forum.

    CMainFrame.h

    void SwitchView(unsigned int f_nId);
    	unsigned int	m_nCurID; // Initialisieren nicht vergessen!
    

    CMainFrame.cpp

    // Die #includes für die Klassen nicht vergessen!
    // ...
    
    template<class View, class Doc> View* create_view(CWnd* f_pParent) 
    {
        View* view = new View();
        Doc* doc   = new Doc();
    
        CCreateContext createContext;
    	createContext.m_pNewViewClass = 0;
    	createContext.m_pNewDocTemplate = 0;
    	createContext.m_pLastView = 0;
    	createContext.m_pCurrentFrame = 0;
    	createContext.m_pCurrentDoc = doc;
    
    	view->CreateWnd(f_pParent, createContext);
    
    	return view;
    }
    
    void CMainFrame::SwitchView(unsigned int f_nId)
    {
    	// Ist es nicht der aktuelle View?
    	if (f_nId != m_nCurID) 
    	{
    		// aktuellen view holen
    		CView* pActiveView = GetActiveView();
    		ASSERT(pActiveView);
    
    		// den nächsten view holen
    		CView* pNextView;
    		switch (f_nId)
    		{
    		case ID_VIEW_1:
    			pNextView = create_view<CEinView, CEinDoc>(this);
    			break;
    		case ID_VIEW_2:
    			pNextView = create_view<CZweiView, CZweiDoc>(this);
    			break;
    		default:
    			ASSERT(0);
    			return;
    			break;
    		}
    
    		// die interne id austauschen
    		UINT nTemp = ::GetWindowLong(pActiveView->m_hWnd, GWL_ID);
    		::SetWindowLong(pActiveView->m_hWnd, GWL_ID, ::GetWindowLong(pNextView->m_hWnd, GWL_ID));
    		::SetWindowLong(pNextView->m_hWnd, GWL_ID, nTemp);
    		pActiveView->ShowWindow(SW_HIDE);
    		pNextView->ShowWindow(SW_SHOW);
    		// neuen View setzen
    		SetActiveView(pNextView);
    		// layout updaten
    		RecalcLayout();
    		// View neuzeichnen
    		pNextView->Invalidate();
    
    		// altes document löschen    
    		CDocument* pOldDoc = pActiveView->GetDocument();
    		delete pOldDoc;
    		// alten view löschen
    		pActiveView->DestroyWindow();
    		m_nCurID = f_nId;
    	}
    }
    

    In den Viewklassen bzw. in der (selbst erstellten) Basisklasse.
    C...View.h

    virtual void CreateWnd(CWnd* f_pParent, CCreateContext& f_rCreateContext);
    

    Im cpp dazu

    #include "afxpriv.h"
    
    // ...
    
    void C...View::CreateWnd(CWnd* f_pParent, CCreateContext& f_rCreateContext) 
    {
        VERIFY(Create(0, 0, WS_CHILD | WS_BORDER, CRect(0, 0, 0, 0), f_pParent, AFX_IDW_PANE_FIRST, &f_rCreateContext));
    	SendMessage(WM_INITIALUPDATE, 0, 0);
    }
    

    Die Konstruktoren der Doc- und Viewklassen müssen public sein.

    Da es scheinbar auch beim Erstellen der Docs und Views Probleme gibt, erkläre ich das noch kurz:
    1. Erstelle eine Resource vom Typ Dialog.
    2. Ordne der Resource eine neue Klasse zu, die CFormView als Basisklasse hat. (Strg+W drücken...)
    3. Über rechte Maustaste auf dem Projekt und dann "Neue Klasse..." kannst du die Doc-Klasse erstellen. Basisklasse CDocument.
    4. Wenn ihr eine eigene Basisklasse verwenden wollt, müsst ihr noch etwas anpassen.

    // ------------------------------------------------------------------------------------------------
    // Zusatz:
    // ------------------------------------------------------------------------------------------------
    

    Und da der SwitchView Funktion eine Zahl übergeben wird, bietet sich gleich noch etwas an: CommandRanges 🙂

    Also:
    Im MainFrame.h

    // Generierte Message-Map-Funktionen
    protected:
    	//{{AFX_MSG(CMainFrame)
    // Assistentenzeugs, NICHTS REINSCHREIBEN!
    	//}}AFX_MSG
    	afx_msg void OnViewAufruf(UINT f_nID);
    	afx_msg void OnUpdateViewAufruf(CCmdUI* pCmdUI);
    	DECLARE_MESSAGE_MAP()
    

    Im MainFrame.cpp

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    	//{{AFX_MSG_MAP(CMainFrame)
    // hier steht der kram vom assistenten - da dürft ihr AUF GAR KEINEN FALL etwas dazuschreiben, der löscht das.
    	//}}AFX_MSG_MAP
    	ON_COMMAND_RANGE(ID_VIEW_1, ID_VIEW_2, OnViewAufruf)
    	ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_1, ID_VIEW_2, OnUpdateViewAufruf)
    END_MESSAGE_MAP()
    
    // ...
    
    // Da immer das selbe gemacht werden soll, kann ein Commandrange genutzt werden.
    // Die neuen Befehle müssen nur die richtigen Werte in der Resource.h bekommen.
    void CMainFrame::OnViewAufruf(UINT f_nID) 
    {
    	SwitchView(f_nID);
    }
    void CMainFrame::OnUpdateViewAufruf(CCmdUI* pCmdUI) 
    {
    // Das hier verhindert, dass derselbe View nochmal aufgerufen wird. Ist nur Schönheit...
    	pCmdUI->Enable(m_nCurID != pCmdUI->m_nID);
    }
    

    ⚠ Dabei muss eine Sache beachtet werden:
    Die IDs der betroffenen Menüpunkte müssen direkt hintereinander liegen. Das könnt ihr von Hand in der resource.h ändern. Achtet nur darauf, dass keine ID doppelt ist. Nehmt am besten einen anderen Zahlenbereich.

    Für die beiden Zeilen in der MessageMap gilt:
    Nachrichtenname(kleinste ID, größte ID, Funktionsname)

    Wenn man die Resource für den View erstellt, muss man eine Sache beachten, sonst geht es schief:
    Man darf keinen Dialog erstellen, sondern das muss ein Formview sein.

    Das versteckt sich ein wenig:
    Rechte Maustaste auf den Resourceordner "Dialog" -> Einfügen...
    In dem Fenster auf das kleine Plus vor Dialog klicken und dann IDD_FORMVIEW wählen.

    So, ich hoffe das hilft Euch. 🙂



  • Hi Leute,
    Interessiert euch das nciht? Ihr sagt garnix dazu ... Mag das niemand ausprobieren?



  • doch, nur benötige ich soetwas derzeit nicht.
    fände es gut wenn es in die FAQ kommt.



  • hatte es mit hilfe von estartu_de zum laufen gekriegt, funzt einwandfrei 🙂


Anmelden zum Antworten