65.9K
CodeProject 正在变化。 阅读更多。
Home

创建多个动态视图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (22投票s)

2001年10月22日

3分钟阅读

viewsIcon

175407

downloadIcon

7688

一篇关于动态创建多个视图的文章,无需文档/视图架构

Sample Image - dynviews.jpg

引言

本文介绍了一种创建多个动态视图的方法,无需 MFC 的文档/视图架构。这些视图可以附加到其他框架、控件或停靠栏。

文档/视图架构是一种非常有用的机制,可以创建需要多个视图的健壮应用程序,但有时并不需要这种功能。即使如此,在没有框架的帮助下创建视图既不明显也不容易。问题主要在于以下几个方面:

  1. CView 派生一个类不允许使用运算符 new 创建视图,因为派生类的构造函数和析构函数是受保护的。

  2. 当包含“手动”创建的视图的窗口或框架被销毁时,CView 对象会自动销毁,并且会为每个对象调用 DestroyWindow()。这就是为什么创建的视图不能使用类成员变量等对象来引用,而是使用指向对象的指针。因此,需要使用运算符 new 来创建它,但不要调用 delete,因为框架会自动销毁这些对象。

  3. 当动态创建多个视图时,需要分配有效的标识符,这些标识符不能使用 Visual C++ 的资源编辑器定义。为此,必须定义一个通用标识符,它将用作每次创建的每个视图的标识基础(使用类似于 WM_USER + idView0, WM_USER + idView1, ...)。

分步指南

  1. 使用 Visual C++ 的向导从 CView 派生自定义视图,并将类的构造函数和析构函数设置为 public。 这将允许在对象创建中使用运算符 new

    //// 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. 在希望创建和引用视图的框架/窗口/对话框类中,需要添加一个对象列表/数组 (CObList) 或一组指向视图的属性指针,以便将来可以使用这些变量引用视图。 在示例中,创建了一个对象 CObList,它存储指向每个创建的视图的指针,并且该对象 CObList 被添加到对话框,该对话框是主窗口。

    /////////////////////////////////////////////////////////////////////////////
    // 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. 当希望创建一个新视图时,只需使用运算符 new 创建该类的对象 CView,存储指向该视图的指针以供以后使用,搜索一个未用于新视图的新标识符,然后使用该标识符调用视图的创建。

    // 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. 完成此操作后,就可以使用指向该视图的指针来实现基本的窗口功能,如显示/隐藏、更改大小等。

    /*
     * 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. 记住动态创建的 CView 对象绝对不能使用运算符 delete 显式删除,这一点非常重要。 事实上,它们将由框架在作为视图父级的窗口的 WM_DESTROY 消息中自动删除。 在示例中,所有视图的父级是一个选项卡控件,当应用程序退出时,所有视图都会被删除,并且没有任何内存泄漏。

© . All rights reserved.