创建多个动态视图






4.97/5 (22投票s)
2001年10月22日
3分钟阅读

175407

7688
一篇关于动态创建多个视图的文章,无需文档/视图架构
引言
本文介绍了一种创建多个动态视图的方法,无需 MFC 的文档/视图架构。这些视图可以附加到其他框架、控件或停靠栏。
文档/视图架构是一种非常有用的机制,可以创建需要多个视图的健壮应用程序,但有时并不需要这种功能。即使如此,在没有框架的帮助下创建视图既不明显也不容易。问题主要在于以下几个方面:
- 从
CView
派生一个类不允许使用运算符new
创建视图,因为派生类的构造函数和析构函数是受保护的。 - 当包含“手动”创建的视图的窗口或框架被销毁时,
CView
对象会自动销毁,并且会为每个对象调用DestroyWindow()
。这就是为什么创建的视图不能使用类成员变量等对象来引用,而是使用指向对象的指针。因此,需要使用运算符new
来创建它,但不要调用delete
,因为框架会自动销毁这些对象。 - 当动态创建多个视图时,需要分配有效的标识符,这些标识符不能使用 Visual C++ 的资源编辑器定义。为此,必须定义一个通用标识符,它将用作每次创建的每个视图的标识基础(使用类似于
WM_USER + idView0
,WM_USER + idView1
, ...)。
分步指南
- 使用 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() };
- 在希望创建和引用视图的框架/窗口/对话框类中,需要添加一个对象列表/数组 (
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() };
- 当希望创建一个新视图时,只需使用运算符
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); }
- 完成此操作后,就可以使用指向该视图的指针来实现基本的窗口功能,如显示/隐藏、更改大小等。
/* * 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); }
- 记住动态创建的
CView
对象绝对不能使用运算符delete
显式删除,这一点非常重要。 事实上,它们将由框架在作为视图父级的窗口的WM_DESTROY
消息中自动删除。 在示例中,所有视图的父级是一个选项卡控件,当应用程序退出时,所有视图都会被删除,并且没有任何内存泄漏。