Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creation of Multiple Dynamic Views

0.00/5 (No votes)
15 Nov 2001 1  
An article on creation of multiple views dynamically without document/view architecture

Sample Image - dynviews.jpg

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:

  1. Derivating a class from CView does not allow creation of the views using the operator new because the constructor and destructor of the derivated class are protected.

  2. When the window or frame that contains the views created "by hand" is destroyed, then automatically the objects CView are destroyed and is invokated the DestroyWindow() 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 operator new for its creation but not invoking delete because the framework will destroy that objects automatically.

  3. 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

  1. 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 operator new 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()
    };
    
  2. 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 object CObList that stores a pointer to each view created and that object CObList 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()
    };
    
  3. When is desired to create a new view, simply must be created an object of that class CView using the operator new, 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);
    }
    
  4. 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);
    }
    
  5. It is really important to remember that the objects CView created dynamically must not be deleted explicitally using the operator delete. In fact they will be automatically deleted by the framework in the WM_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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here