单文档多视图的实现
一个文档可能对应多个视图,最典型的如股市行情数据有图形界面和表格界面两种,实现这种程序使用MDI是最直接也是最让人接受的。这里只对可能出现的问题做一下简要分析。
1,如何显示两个视图
首先需要定义要使用的视图类,以股市软件为例(下同),定义一个图形View和一个表格View,这是必须要做的。MDI使用文档模板来加载文档、框架窗口和视图窗口,默认的创建函数在App类的InitInstance中:
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_MY1TYPE,
RUNTIME_CLASS(CMy1Doc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMy1View));
AddDocTemplate(pDocTemplate);
这不是我们需要的,所以我们要自己写一个CMultiDocTemplate,当然不是完全重写。例如写一个CMVDocTemplate,继承自CMultiDocTemplate。把它的构造函数写成这样:
CMVDocTemplate::CMVDocTemplate(UINT _nIDResource,
CRuntimeClass* _pDocClass)
: CMultiDocTemplate( _nIDResource, _pDocClass, NULL, NULL )
{
}
也就是说不要CChildFrame和CView,只要CDoc,这就是单文档。
然后在App类的InitInstance中将CMultiDocTemplate替换成CMVDocTemplate:
CMVDocTemplate* pDocTemplate;
pDocTemplate = new CMVDocTemplate(
IDR_MY1TYPE,
RUNTIME_CLASS(CMy1Doc));
//RUNTIME_CLASS(CChildFrame), // custom MDI child frame
//RUNTIME_CLASS(CListViewView));
AddDocTemplate(pDocTemplate);
不要CFrame和CView了,当然不能生成窗口,肯定要加到文档模板里面去。加到哪里面?什么时候加?
原来,CMultiDocTemplate有两个成员,一个是m_pFrameClass,一个是m_pViewClass,也就是文档模板构造函数中的另两个参数,加入窗口和视图类的时候改变这两个成员就行了。
再有是什么时候加,需要在打开文档或新建文档的时候加,CMultiDocTemplate有一个OpenDocumentFile函数,在这个函数里调用了CreateNewFrame,正是这个过程生成了子窗口,所以我们要重写OnOpenDocumentFile函数,以下这个过程上半部分我是抄加人书上来的,和问题关系不大。
CDocument* CMVDocTemplate::OpenDocumentFile(
LPCTSTR _lpszPathName,
BOOL _bMakeVisible )
{
// Create the document instance and validate it.
CDocument* pDocument = CreateNewDocument();
if ( !pDocument )
{
// The document is invalid.
// Inform the user of the error, and exit the method.
AfxMessageBox( AFX_IDP_FAILED_TO_CREATE_DOC );
TRACE( "Unable of creating the document in OpenDocumentFile /
in %s at %d./n", THIS_FILE, __LINE__ );
return NULL;
}
// Validate the document.
ASSERT_VALID( pDocument );
// The document is valid. Set the autodelete flag.
pDocument->m_bAutoDelete = m_bAutoDelete;
// Verify if we are creating a new document, or opening an exesting one.
if ( _lpszPathName == NULL )
{
// Xreate a new document - with default document name
SetDefaultTitle( pDocument );
// Avoid creating temporary compound file when starting up invisible
if ( !_bMakeVisible )
{
pDocument->m_bEmbedded = TRUE;
}
// Initialize the document.
if ( !pDocument->OnNewDocument() )
{
// Unable to initialize the document.
TRACE( "Unable to initialize the new document in OpenDocumentFile /
in %s at %d./n", THIS_FILE, __LINE__ );
return NULL;
}
// Increment the document counter.
m_nUntitledCount++;
}
else
{
// Open an existing document
CWaitCursor wait;
if ( !pDocument->OnOpenDocument( _lpszPathName ) )
{
// Unable to open the document.
TRACE( "Unable to open the document in OpenDocumentFile /
in %s at %d./n", THIS_FILE, __LINE__ );
// We must delete the document instance.
RemoveDocument( pDocument );
delete pDocument;
CloseAllDocuments( FALSE );
return NULL;
}
// Set the document file path and name.
pDocument->SetPathName( _lpszPathName );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//这里是比关键的部分
CFrameWnd* pFrameWnd = NULL;
m_pFrameClass = RUNTIME_CLASS(CChildFrame);
m_pViewClass = RUNTIME_CLASS(CMy1View);
pFrameWnd = (CMDIChildWnd*)(CreateNewFrame(pDocument, NULL));
pFrameWnd->InitialUpdateFrame(pDocument,TRUE);
pFrameWnd->ActivateFrame(-1);
CString str = MAKEINTRESOURCE(m_nIDResource);
CFrameWnd* pFrameWnd2 = NULL;
m_pFrameClass = RUNTIME_CLASS(CChildFrame);
m_pViewClass = RUNTIME_CLASS(CMy1NextView);
pFrameWnd2 = (CMDIChildWnd*)(CreateNewFrame(pDocument, NULL));
pFrameWnd2->InitialUpdateFrame(pDocument,TRUE);
pFrameWnd2->ActivateFrame(-1);
return pDocument;
}
关键在最后这里,先使用CChildFrame和CMy1View填充那两个成员,调用CreateNewFrame,再使用CChidlFrame(也可以是别的Frame类)和CMy1NextView填充两个成员,再调用CreateNewFrame。
到这里,大功告成!我们运行一个试试,果然。
2,既然生成了多个子窗口,需要加上不同的标题,要么怎么区分?
在CView的OnInitialUpdate中写:
GetParent()->SetWindowText(“aaa”);
单这样做还不行,这里需要用一个小技巧,需要在子框架类的PreCreateWindow中添加:
cs.style &=~ (LONG) FWS_ADDTOTITLE;
一看就明白了,将窗口设置成标题自定义的。
同样改图标的话就是:
GetParent()->SetIcon(------);
3,还有一个小问题,如何在这个窗口切换到另一个窗口?
一般做法是需要查找一下带有某标题的子窗口,然后使用主框架的MDIActivate()函数。
POSITION pos = GetDocument()->GetFirstViewPosition();
while(pos)
{
CWnd* pWnd = GetDocument()->GetNextView(pos)->GetParent();
CString str;
pWnd->GetWindowText(str);
if(str == "aaa")
{
CMDIFrameWnd *pMF=(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
pMF->MDIActivate(pWnd);
}
}