单文档与多视的实现方法
发表于:2007-07-01来源:作者:点击数:
标签:
一、单文档与多视 Windows程序一般分为以下几种风格:多文档、单文档、基于对话框的。 Word一类多文档程序和计算器一类基于对话框的程序不在本文介绍之列。单文 档又分为单视的和多视的。一般情况下,单文档仅需要单视就够了,如画笔等。 但也有一些情况下,
一、单文档与多视
Windows程序一般分为以下几种风格:多文档、单文档、基于对话框的。
Word一类多文档程序和计算器一类基于对话框的程序不在本文介绍之列。单文
档又分为单视的和多视的。一般情况下,单文档仅需要单视就够了,如画笔等。
但也有一些情况下,单文档需要多视支持,比如同时观察文档的不同部分,同时
从不同的角度观察同一文档等。
在MFC的框架下,文档对象(CDocument)维持了一个保存其所有视的列表,
并提供了增加与删除视的函数,以及当文档内容改变时通知其所有视的方法。
通过多文档框架的窗口复制机制和单文档框架的分割窗口机制是实现单文档多
视的主要方法。但这些标准方法在有的情况下并不够用,为此笔者摸索出另
外两种实用的方法,在实践中使用效果良好。
二、三种标准的单文档与多视情况
1.视对象基于同一视类,每个视位于MDI的一个独立子文档框架中。
用户可以通过“窗口\新窗口”菜单,为同一文档的视再创建一个窗口,通过
新创建的窗口,可以编辑和观察文档的另一部分,同一文档各个视图之间自动实
现同步,用户修改一个视的内容,在另外的视中也自动更新。
MFC框架通过复制原来的子框架窗口和其中的视来实现上面的功能,并且是
完全自动的。
2.视对象基于同一视类,所有视位于同一文档框架中。
分割窗口将单文档窗口的视区分割成几个独立的视,框架从同一视类创建多
个视对象。Word的子窗口即属于这种类型。
3.视对象基于不同的视类,所有的视位于同一文档框架中。
多个视共享同一文档框架,但从不同的视类创建,每个视可以为文档提供不
同的观察和编辑方法。比如一个视用图形观察文档,而另一个视用文本编辑文档
中对象的属性。这种情况也适应于用不同的视来观察文档的不同部分。这种类
型的实现方法是通过重载OnCreateClient函数实现。下面的例子实现了图中的分
割窗口。
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT,CCreateContext* pContext)
{
RECT rect;
// 先分割为 1X2的形式
m_wndSplitter1.CreateStatic(this, 1, 2, WS_CHILD | WS_VISIBLE );
m_wndSplitter1.SetColumnInfo(0,300,0); // 设置第一列的宽度
// 将第一列进一步分割为 2X1的形式
m_wndSplitter2.CreateStatic( &m_wndSplitter1,2, 1, WS_CHILD | WS_VISIBLE,
m_wndSplitter1.IdFromRowCol(0, 0));
// 创建各个视
m_wndSplitter1.CreateView(0, 1,
RUNTIME_CLASS(CviewView1), CSize(0, 0), pContext);
m_wndSplitter2.CreateView(0, 0,
RUNTIME_CLASS(CviewView2), CSize(0, 0), pContext);
m_wndSplitter2.CreateView(1, 0,
RUNTIME_CLASS(CviewView3), CSize(0, 0), pContext);
......
}
在实践中,笔者发现这三种方式有时都不适用。比如以下的两种情况:需
要在不同的时间分别观察同一文档的不同部分,而文档分割成不同部分的数量较
多,这样就不宜用分割窗口来实现,而需要在同一框架中切换不同的视;另一种
情况可以说是上面提到的第三种情况和此处第一种情况的组合,即在单文档框
架中包含有多个由分割窗口分割的区域,而某(或某些)分割区域中包含有多个
不同的视,仅有其中的一个视显示出来。与第三种情况类似,其它分割区域中具
有不同的视,但它们同时显示出来。因为在实现方法上有所不同,所以这里分为
两种情况来讨论,这也是这篇文章要介绍的重点。
三、单文档单框架窗口中的多视
下面的例子演示了第一种情况下,具有两个视的简单实现方法。必需考虑如
下的问题:非活动的视是否继续连接到文档。当视连接到文档时,它从文档接收
OnUpdate通知消息,使其保持与文档内容同步。但这样做将增加计算机的开销,
同时也会增加程序
开发的任务。另一方法是当非活动的视重新变成活动时,再进
行与文档的同步工作。例子中采用后一种方法。
BOOL CMainFrame::OnViewChange(UINT nCmdID)
{
CView* pViewAdd;
CView* pViewRemove;
CDocument* pDoc = GetActiveDocument();
UINT nCmdID;
nCmdID = LOWORD(GetCurrentMessage()-〉wParam);
if((nCmdID == ID_VIEW_VIEW1) && (m_currentView == 1))
return;
if((nCmdID == ID_VIEW_VIEW2) && (m_currentView == 2))
return;
if (nCmdID == ID_VIEW_VIEW2)
{
if (m_pView2 == NULL)
{
m_pView1 = GetActiveView();
m_pView2 = new CMyView2;
m_pView2-〉Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
rectDefault, this, AFX_IDW_PANE_FIRST + 1, NULL);
}
pViewAdd = m_pView2;
pViewRemove = m_pView1;
m_currentView= 2;
}
else
{
pViewAdd = m_pView1;
pViewRemove = m_pView2;
m_currentView= 1;
}
// 将活动视的child id设置为AFX_IDW_PANE_FIRST
// 将其它视设置为AFX_IDW_PANE_FIRST以外的值,
// 这样当调用 CFrameWnd::RecalcLayout重新布局窗口时,
// 才会得到正确的视
int nSwitchChildID = pViewAdd-〉GetDlgCtrlID();
pViewAdd-〉SetDlgCtrlID(AFX_IDW_PANE_FIRST);
pViewRemove-〉SetDlgCtrlID(nSwitchChildID);
// 显示活动视而隐藏非活动视
pViewAdd-〉ShowWindow(SW_SHOW);
pViewRemove-〉ShowWindow(SW_HIDE);
// 将新的活动视连接到文档,并断开原来的视与文档的连接
pDoc-〉AddView(pViewAdd);
pDoc-〉RemoveView(pViewRemove);
SetActiveView(pViewAdd);
RecalcLayout();
}
四、单文档分割窗口中,某分割区的多视的实现
第二种情况下,窗口的分割与标准方法的第3种类似,这里不再重复。下面
的函数用于切换某分割(第3种标准界面中,左上区域)区域中的视。
这里的实现方法是:先删除原来的视,然后在原分割区域创建一个新的视。
void CMainFrame::OnViewView1()
{
ChangeView(RUNTIME_CLASS(CviewView1));
ViewingView = ID_VIEW_VIEW1;
}
void CMainFrame::OnViewView2()
{
ChangeView(RUNTIME_CLASS(CviewView2));
ViewingView = ID_VIEW_VIEW2;
}
void CMainFrame::OnUpdateViewView1(CCmdUI* pCmdUI)
{
pCmdUI-〉SetCheck(ViewingView == ID_VIEW_VIEW1);
}
void CMainFrame::OnUpdateViewView2(CCmdUI* pCmdUI)
{
pCmdUI-〉SetCheck(ViewingView == ID_VIEW_VIEW2);
}
void CMainFrame::ChangeView(CRuntimeClass * pRTClass)
{
// 先删除原来的视
m_wndSplitter2.DeleteView(0,0);
// 创建新的视
CCreateContext Context;
Context.m_pNewViewClass = pRTClass; // 视类
Context.m_pCurrentDoc = GetActiveDocument(); // 与文档连接
m_wndSplitter2.CreateView(0,0, pRTClass,CSize(0,0),&Context);
CView * pView = (CView *)m_wndSplitter2.GetPane(0,0); // 获取分割区域
pView-〉ShowWindow(SW_SHOW);
pView-〉OnInitialUpdate();
SetActiveView(pView);
m_wndSplitter2.SetRowInfo(0, 300, 20); // 设置宽度
::SetWindowLong(pView-〉m_hWnd, GWL_ID, AFX_IDW_PANE_FIRST);
m_wndSplitter2.RecalcLayout();
}
原文转自:http://www.ltesting.net