Creation of Multiple Dynamic Views






4.97/5 (22 votes)
Oct 22, 2001
3 min read

174953

7688
An article on creation of multiple views dynamically without document/view architecture
Introduction
This article presents a method for multiple dynamic views creation whithout the MFC's document/view architecture. That views could be attached to other frames, controls or dock bars.
The document/view architecture is a very useful mechanism to create robust applications that requires multiple views, but sometimes there is not necessary such funcionality. Even though creating views whithout the help of the framework is neither evident or easy. The problem lies basically in the following aspects:
- Derivating a class from
CView
does not allow creation of the views using the operatornew
because the constructor and destructor of the derivated class are protected. - When the window or frame that contains the views created "by
hand" is destroyed, then automatically the objects
CView
are destroyed and is invokated theDestroyWindow()
for each one. That's why the created views can not be referenced using objects like member variables of a class but using pointers to objects, so it is necessary to use the operatornew
for its creation but not invokingdelete
because the framework will destroy that objects automatically. - When creating multiple views dinamically there is necessary to assign
valid identifiers that can not be defined using the resource editor of Visual
C++. For that there must be defined a generic identifier that will be used as
base for the identification of each view that will be created by the time (using
something like
WM_USER + idView0
,WM_USER + idView1
, ...).
Step by Step
- Derivate the customized view/views using the wizard of Visual C++ from
CView
and make public the constructor and destructor of the class. This will allow the using of the operatornew
in the objects creation.//// This view encapsulates a personalized 2D view class CMyView : public CView { class CMyPoint : public CObject { public: CMyPoint(CPoint location) { m_location = location; } CPoint m_location; }; public: CMyView(); virtual ~CMyView(); public: int m_idView; protected: DECLARE_DYNCREATE(CMyView) // Attributes protected: CObList m_points; // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMyView) protected: virtual void OnDraw(CDC* pDC); // overridden to draw this view //}}AFX_VIRTUAL // Implementation protected: #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif // Generated message map functions protected: //{{AFX_MSG(CMyView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() };
- In the frame/window/dialog class where it is desired to create and
reference the views, it is necessary to add an object list/array (
CObList
) or a set of attributes pointer to the views so in the future could be referenced the views using those variables. In the example it is created an objectCObList
that stores a pointer to each view created and that objectCObList
is added to the dialog which is the main window.///////////////////////////////////////////////////////////////////////////// // CDynViewsDlg dialog enum VIEW_TYPE { VIEW_2D, VIEW_OPENGL }; class CDynViewsDlg : public CDialog { // Construction public: CDynViewsDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CDynViewsDlg) enum { IDD = IDD_DYNVIEWS_DIALOG }; CTabCtrl m_tabCtrl; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDynViewsDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: void AddView(VIEW_TYPE viewType); void SelectTab(int nTab); HICON m_hIcon; // This member allows to reference any // dynamically created view CObList m_listViews; // Generated message map functions //{{AFX_MSG(CDynViewsDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnSelchangeTabctrl(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnAddview(); afx_msg void OnAddopenglview(); //}}AFX_MSG DECLARE_MESSAGE_MAP() };
- When is desired to create a new view, simply must be created an object of
that class
CView
using the operatornew
, store the pointer to that view for lately use, search a new identifier not used for the new view and invoke create of the view using that identifier.// Handle for button click "Add 2D View" void CDynViewsDlg::OnAddview() { AddView(VIEW_2D); } // Handle for button click "Add 3D/OpenGL View" void CDynViewsDlg::OnAddopenglview() { AddView(VIEW_OPENGL); } /* * AddView Method: * * 1. Creates a new tab for the view * 2. Creates a new view dynamically of the desired type * 3. Asociates the id of the view with the id of the tab * 4. Selects the new created view and tab */ void CDynViewsDlg::AddView(VIEW_TYPE viewType) { // ID of the new view int idNewView = m_listViews.GetCount(); // View size CRect clientRect; m_tabCtrl.GetClientRect(&clientRect); clientRect.DeflateRect(10, 30); // Creation 'on the fly' of the new view CMyView* m_pNewView; CString tabCaption; switch(viewType) { case VIEW_2D: // The operator 'new' has not asociated delete. // The view will be deleted automatically by the framework // on the WM_DESTROY handle of the parent (the tab control) m_pNewView = new CMyView(); tabCaption.Format("View %d (2D)", idNewView); break; case VIEW_OPENGL: // The operator 'new' has not asociated delete. // The view will be deleted automatically by the framework // on the WM_DESTROY handle of the parent (the tab control) m_pNewView = new COpenGLView(); tabCaption.Format("View %d (OpenGL)", idNewView); break; }; // The new tab for the view on the tab control m_tabCtrl.InsertItem(idNewView, tabCaption); m_pNewView->m_idView = idNewView; // Creation of the view window if(! m_pNewView->Create(NULL, NULL, WS_VISIBLE | WS_CHILD, clientRect, &m_tabCtrl, ID_VIEW_BASE + idNewView)) { TRACE( "Failed view creation\n" ); } // Using a list is easy to handle all the views created m_listViews.AddTail(m_pNewView); // Select the new created tab/view m_tabCtrl.SetCurSel(idNewView); SelectTab(idNewView); }
- Once this has been made it is possible to use the pointer to the view to
achieve basic window functions like show/hide, change size, etc.
/* * This method switchs the visibility of each * view, making visible only the selected view * asociated with the tab nTab * */ void CDynViewsDlg::SelectTab(int nTab) { CMyView* pViewSelected = NULL; POSITION pos = m_listViews.GetHeadPosition(); // Hide all views under the tabControl while(pos != NULL) { CMyView* pView = (CMyView*) m_listViews.GetNext(pos); pView->ShowWindow(SW_HIDE); if(pView->m_idView == nTab) pViewSelected = pView; }; // Show the view asociated with the tab selected if(pViewSelected != NULL) pViewSelected->ShowWindow(SW_SHOW); }
- It is really important to remember that the objects
CView
created dynamically must not be deleted explicitally using the operatordelete
. In fact they will be automatically deleted by the framework in theWM_DESTROY
message of the window that is parent of the views. In the example the parent of all the views is a tab control and when the application exits all the views are deleted and there are not any memory leaks.