Document/View的初始化过程

本文详细解析了MFC框架中的Document/View模式,从CTest_DrawRectApp::InitInstance()开始,通过CSingleDocTemplate类管理CDocument、CFrameWnd、CView对象的创建过程。解释了如何在CFrameWnd里生成CView对象,以及整个模式的工作流程。

最近正在看《mfc深入浅出》,看到第8章Document/View深入探讨时候,对Document/View的建立过程不是很清楚,于是下大力气猛啃书和各种百度,明白了一些,做个小结:

先从CTest_DrawRectApp::InitInstance()开始

Test_DrawRect.cpp(这个文件是我建立名为Test_DrawRect工程自动生成的文件)

CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CTest_DrawRectDoc),
		RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
		RUNTIME_CLASS(CTest_DrawRectView));
	if (!pDocTemplate)
		return FALSE;
	AddDocTemplate(pDocTemplate);

afxwin.h

class CSingleDocTemplate : public CDocTemplate
class AFX_NOVTABLE CDocTemplate : public CCmdTarget
{
	DECLARE_DYNAMIC(CDocTemplate)		
	......
virtual CDocument* CreateNewDocument();
virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther);
	......
}


doctempl.cpp

CDocument* CDocTemplate::CreateNewDocument()
{
	// default implementation constructs one from CRuntimeClass
	if (m_pDocClass == NULL)
	{
		TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewDocument.\n");
		ASSERT(FALSE);
		return NULL;
	}
	CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
	if (pDocument == NULL)
	{
		TRACE(traceAppMsg, 0, "Warning: Dynamic create of document type %hs failed.\n",
			m_pDocClass->m_lpszClassName);
		return NULL;
	}
	ASSERT_KINDOF(CDocument, pDocument);
	AddDocument(pDocument);
	return pDocument;
}

/////////////////////////////////////////////////////////////////////////////
// Default frame creation

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{
	if (pDoc != NULL)
		ASSERT_VALID(pDoc);
	// create a frame wired to the specified document

	ASSERT(m_nIDResource != 0); // must have a resource ID to load from
	CCreateContext context;
	context.m_pCurrentFrame = pOther;
	context.m_pCurrentDoc = pDoc;
	context.m_pNewViewClass = m_pViewClass;
	context.m_pNewDocTemplate = this;

	if (m_pFrameClass == NULL)
	{
		TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewFrame.\n");
		ASSERT(FALSE);
		return NULL;
	}
	CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
	if (pFrame == NULL)
	{
		TRACE(traceAppMsg, 0, "Warning: Dynamic create of frame %hs failed.\n",
			m_pFrameClass->m_lpszClassName);
		return NULL;
	}
	ASSERT_KINDOF(CFrameWnd, pFrame);

	if (context.m_pNewViewClass == NULL)
		TRACE(traceAppMsg, 0, "Warning: creating frame with no default view.\n");

	// create new from resource
	if (!pFrame->LoadFrame(m_nIDResource,
			WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles
			NULL, &context))
	{
		TRACE(traceAppMsg, 0, "Warning: CDocTemplate couldn't create a frame.\n");
		// frame will be deleted in PostNcDestroy cleanup
		return NULL;
	}

	// it worked !
	return pFrame;
}


上面的意思就是DocTemplate这个类管理CDocument、CFrameWnd、CView这三个对象,在CDocTemplate类里有创建CFrameWnd、CDocument这两个对象的函数。那么CView对象在哪里生成呢?

在CFrameWnd里生成。

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
	LPCTSTR lpszWindowName,
	DWORD dwStyle,
	const RECT& rect,
	CWnd* pParentWnd,
	LPCTSTR lpszMenuName,
	DWORD dwExStyle,
	CCreateContext* pContext)
{
	HMENU hMenu = NULL;
	if (lpszMenuName != NULL)
	{
		// load in a menu that will get destroyed when window gets destroyed
		HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);
		if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
		{
			TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");
			PostNcDestroy();            // perhaps delete the C++ object
			return FALSE;
		}
	}

	m_strTitle = lpszWindowName;    // save title for later

	if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
		rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
		pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
	{
		TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");
		if (hMenu != NULL)
			DestroyMenu(hMenu);
		return FALSE;
	}

	return TRUE;
}

CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
{
	ASSERT(m_hWnd != NULL);
	ASSERT(::IsWindow(m_hWnd));
	ENSURE_ARG(pContext != NULL);
	ENSURE_ARG(pContext->m_pNewViewClass != NULL);

	// Note: can be a CWnd with PostNcDestroy self cleanup
	CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
	if (pView == NULL)
	{
		TRACE(traceAppMsg, 0, "Warning: Dynamic create of view type %hs failed.\n",
			pContext->m_pNewViewClass->m_lpszClassName);
		return NULL;
	}
	ASSERT_KINDOF(CWnd, pView);

	// views are always created with a border!
	if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
		CRect(0,0,0,0), this, nID, pContext))
	{
		TRACE(traceAppMsg, 0, "Warning: could not create view for frame.\n");
		return NULL;        // can't continue without a view
	}

	if (pView->GetExStyle() & WS_EX_CLIENTEDGE)
	{
		// remove the 3d style from the frame, since the view is
		//  providing it.
		// make sure to recalc the non-client area
		ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
	}
	return pView;
}

BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
{
	// default create client will create a view if asked for it
	if (pContext != NULL && pContext->m_pNewViewClass != NULL)
	{
		if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
			return FALSE;
	}
	return TRUE;
}

int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
{
	ENSURE_ARG(lpcs != NULL);
	CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;
	return OnCreateHelper(lpcs, pContext);
}

int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
	if (CWnd::OnCreate(lpcs) == -1)
		return -1;

	// create special children first
	if (!OnCreateClient(lpcs, pContext))
	{
		TRACE(traceAppMsg, 0, "Failed to create client pane/view for frame.\n");
		return -1;
	}

	// post message for initial message string
	PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);

	// make sure the child windows have been properly sized
	RecalcLayout();

	return 0;   // create ok
}


这段代码的意思是,在CSingleDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)中,使得CFrameWnd::Create()得到调用,CFrameWnd::Create(...)调用CFrameWnd::CreateEx(...),此函数会使得系统在还未创建窗口前发送一个WM_CREATE消息,此消息得到CFrameWnd::OnCreate(...)响应,CFrameWnd::OnCreate(...)调用CFrameWnd::OnCreateHelper(...)函数,CFrameWnd::OnCreateHelper(...)函数会调用CFrameWnd::OnCreateClient(...)函数,而CFrameWnd::OnCreateClient(...)函数会最终调用CFrameWnd::OnCreateView(...)完成CView对象的生成。

总结过程就是:

CXXXApp::InitInstance()->CSingleTemplate::CreateNewDocument(),CSingleTemplate::CreateNewFrame()->

CFrameWnd::Create()->CFrameWnd::CreateEx()->发送WM_CREATE消息->CFrameWnd::OnCreate()->

CFrameWnd::OnCreateHelper()->CFrameWnd::OnCreateClient()->CFrameWnd::CreateView()(可算到了!)。


 

### 在 Vue3 中使用 photo-sphere-viewer/core 初始化全景图位置的设置方法 在 Vue3 项目中使用 `photo-sphere-viewer/core` 初始化全景图时,可以通过设置初始化参数中的 `defaultPosition` 来指定初始视角的位置[^2]。以下是详细的实现方法和代码示例: #### 初始化参数说明 `photo-sphere-viewer/core` 提供了多种配置项来定义全景图的行为和外观。其中,`defaultPosition` 参数用于设置全景图加载时的默认视角位置。该参数是一个对象,包含以下属性: - `yaw`: 水平旋转角度(范围为 -180° 到 180°)。 - `pitch`: 垂直旋转角度(范围为 -90° 到 90°)。 - `fov`: 字段视图(Field of View),即视野范围(单位为度,默认值为 75°)。 #### 安装依赖 确保已安装 `photo-sphere-viewer/core` 和相关样式文件。推荐通过 npm 进行安装: ```bash npm install photo-sphere-viewer/core ``` #### 示例代码 以下是一个完整的 Vue3 组件代码示例,展示如何初始化全景图并设置默认视角位置: ```vue <template> <div id="viewer" style="width: 100%; height: 100vh;"></div> </template> <script> import { Viewer } from 'photo-sphere-viewer/core'; import 'photo-sphere-viewer/dist/photo-sphere-viewer.css'; export default { name: 'PanoramaViewer', data() { return { panoramaViewer: null, img: 'path/to/your/panorama-image.jpg', // 替换为实际的全景图片路径 }; }, mounted() { this.initViewer(); }, methods: { initViewer() { this.panoramaViewer = new Viewer({ container: document.querySelector('#viewer'), // 容器选择器 panorama: this.img, // 全景图片地址 defaultPosition: { yaw: -45, // 初始水平旋转角度 pitch: 10, // 初始垂直旋转角度 fov: 75, // 初始视野范围 }, navbar: ['autorotate', 'zoom', 'caption', 'fullscreen'], // 导航栏按钮 size: { width: '100%', height: '100%', }, }); }, }, beforeUnmount() { if (this.panoramaViewer) { this.panoramaViewer.destroy(); // 销毁实例以释放资源 } }, }; </script> <style scoped> #viewer { width: 100%; height: 100vh; } </style> ``` #### 注意事项 - 确保 `panorama` 属性指向有效的全景图片路径[^2]。 - 如果需要动态调整视角位置,可以调用 `viewer.setValues({ yaw, pitch, fov })` 方法[^3]。 - 在组件卸载前调用 `destroy` 方法释放资源,避免内存泄漏。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值