MFC视图切换大全总结

MFC视图切换大全总结

2013-08-08 22:47:41cnblogs.com-记忆斑驳的时光-点击数:945
第七城市th7cn

单纯视图之间的切换

单文档多视图切换是我在学习MFC中遇到的一个老大难问题,在今天总算是一一破解了。我觉得视图切换分为三个等级,第一是在未切分窗格的情况下切换视图类;第二是在分割窗格的一个窗格内实行视图切换;第三是在分割窗格和未分割之间的切换和视图切换。

在MFC创建SDI的伊始,MFC默认的视图类是CView,如果CView满足你的需求,可以直接单击finish,如果你不想让CView成为你的默认视图类,你可以在下图这个地方修改。

如果你忘记了修改默认的视图类这也没关系,我们可以在代码中改变:

在App类里面有个函数叫InitInstance(),在这里面有一段代码

CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(Cprogram8Doc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CView1)); //这里更改你的默认视图类,注意不要忘记包含头文件哦
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);

说完了默认视图类的更改,我们现在进入主题。

1、首先创建你需要切换的视图类,AddClass进入或者创建一个控件然后AddClass创建控件关联类,后者适用于Form之类的控件。

2、控制之处,比如菜单项单击、单击鼠标什么的,我是使用菜单项来切换窗口的

void CMainFrame::OnView1()
{
// TODO: Add your command handler code here

SwitchToForm(IDD_FORMVIEW1);

}

void CMainFrame::OnView2()
{
// TODO: Add your command handler code here

SwitchToForm(IDD_FORMVIEW2);

}

3、SwitchToForm这个函数主要用于视图切换

在这个函数中主要做以下几个动作:

1、获得当前视图类指针和需要转换的视图类指针,如果是第一次使用需要New一个,并且与文档类关联;

2、改变活动视图;

3、显示新视图和隐藏旧视图;

4、设置控件ID;

5、调整框架视图。

void CMainFrame::SwitchToForm(int nForm)
{
CView *pOldActiveView=GetActiveView(); //1
CView *pNewActiveView=(CView*)GetDlgItem(nForm);
if(NULL==pNewActiveView)
{
switch(nForm)
{
case IDD_FORMVIEW1:
pNewActiveView=(CView*)new CView1;
break;
case IDD_FORMVIEW2:
pNewActiveView=(CView*)new CView2;
break;
case IDD_FORMVIEW3:
pNewActiveView=(CView*)new CView3;
break;
case IDD_FORMVIEW4:
pNewActiveView=(CView*)new CView4;
break;
default:
break;
}
CCreateContext context;
context.m_pCurrentDoc=pOldActiveView->GetDocument();
pNewActiveView->Create(NULL,NULL,WS_CHILD | WS_BORDER,CFrameWnd::rectDefault,this,nForm,&context);
pNewActiveView->UpdateData();
}
SetActiveView(pNewActiveView); //2
pNewActiveView->ShowWindow(SW_SHOW); //3
pOldActiveView->ShowWindow(SW_HIDE);
if(pOldActiveView->GetRuntimeClass()==RUNTIME_CLASS(CView1)) //4
pOldActiveView->SetDlgCtrlID(IDD_FORMVIEW1);
else if(pOldActiveView->GetRuntimeClass()==RUNTIME_CLASS(CView2))
pOldActiveView->SetDlgCtrlID(IDD_FORMVIEW2);
else if(pOldActiveView->GetRuntimeClass()==RUNTIME_CLASS(CView3))
pOldActiveView->SetDlgCtrlID(IDD_FORMVIEW3);
else if(pOldActiveView->GetRuntimeClass()==RUNTIME_CLASS(CView4))
pOldActiveView->SetDlgCtrlID(IDD_FORMVIEW4);
pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
RecalcLayout(); //5
}
在看网上其他人的帖子的时候发现在RecalcLayout(); 前面还要加一句delete pOldActiveView;我自己觉得这样有些浪费资源,视图类创建完成了隐藏起来,用的时候重新显示。这样的方法应该比每次使用都要创建好一些。小弟不才,有讨论的朋友可以联系我。

带有分割窗格的视图切换

切换视图第二层就是带有分割窗格的视图切换

分割窗格的视图切换,我觉得难点是不容易在有限区域进行视图切换。

1、首先分割窗格,这里我不多解释,详情看下面链接;

2、再给每个视图一个唯一的ID号;

m_splitter.CreateStatic(this,1,2);
m_splitter.CreateView(0,0,RUNTIME_CLASS(CTree1),CSize(100,100),pContext);
m_splitter.CreateView(0,1,RUNTIME_CLASS(CForm1),CSize(100,100),pContext);
CWnd *pWnd=m_splitter.GetPane(0,1);
m_pViews[0]=(CView*)m_splitter.GetPane(0,1);
pWnd->SetDlgCtrlID(IDD_FORM1);
pWnd->ShowWindow(SW_HIDE);
m_splitter.CreateView(0,1,RUNTIME_CLASS(CForm2),CSize(100,100),pContext);
pWnd=m_splitter.GetPane(0,1);
m_pViews[1]=(CView*)m_splitter.GetPane(0,1);
pWnd->SetDlgCtrlID(m_splitter.IdFromRowCol(0,1));
pWnd->ShowWindow(SW_SHOW);
RedrawWindow();
return true;

注:我这里CreateView一个新视图,就覆盖掉前一个视图,最终显示的是最后一个视图,前面的视图只是隐藏起来,等到使用的时候显示出来就好了。返回值要覆盖返回到基类的语句return CFrameWndEx::OnCreateClient(lpcs, pContext);将这句话注解,然后return true;

3、在哪里激活切换功能自己设计,我使用的是菜单;

4、响应主要包括下面几个步骤:

1、首先获得窗格中的当前视图;

2、使用IsKindOf判断这个类是否是将要切换到的类;

3、获得框架长宽和窗格长宽;

CView *pView=(CView*)m_splitter.GetPane(0,1); //1
m_bTest=pView->IsKindOf(RUNTIME_CLASS(CForm2)); //2
CRect rcFrame,rcClient; //3
m_splitter.GetClientRect(&rcClient);
GetClientRect(&rcFrame);

上面的全部是准备工作,下面才是真正的切换;

4、删除原有视图

5、创建当前视图

6、调整框架

if(m_bTest)
{
m_splitter.DeleteView(0,1);
m_splitter.CreateView(0,1,RUNTIME_CLASS(CForm1),CSize(rcClient.Width(),rcFrame.Height()),NULL);
m_splitter.RecalcLayout();
}
else
{
m_splitter.DeleteView(0,1);
m_splitter.CreateView(0,1,RUNTIME_CLASS(CForm2),CSize(rcClient.Width(),rcFrame.Height()),NULL);
m_splitter.RecalcLayout();
}

仔细一看,貌似这个还要比单纯视图切换还要简单一些,这也没办法,CSplitterWnd提供了这么一个便捷的方法。

带分割视图与未分割视图之间的切换

这个是三部曲中我认为最难的,并不是说程序有多么难,只是想到这个切分方式真的不容易,今天就把这三部曲的最后一曲分享给大家,也为互联网做一点贡献。

首先说一下程序的思想,为分割窗口层专门独立创建一个基于CFrameWnd的类,然后在这里面写分割视图的代码,再与其他未分割的视图类进行切换。

下面我们来看一下实现的过程:

1、创建一个基于CFrameWnd的派生类CSplitterFrame;

2、添加要填充分割窗口的视图类和与分割视图切换的视图类;

3、为这个派生类重载OnCreateClient函数,构造分割视图

m_Splitter.CreateStatic(this,1,2);
m_Splitter.CreateView(0,0,RUNTIME_CLASS(CLeftTree),CSize(300,0),pContext);
m_Splitter.CreateView(0,1,RUNTIME_CLASS(CRightList),CSize(300,0),pContext);
return true;

这里我不解释了,想必大家都很熟悉了,这里我要说一点我自己犯过的错误。我本来想在这里GetClientRect获得窗口大小,但失败了,左思右想不明白,后来才发现,这又不是CMainFrame,所以根本不能获得,要是用API函数。

4、在CMainFrame类里面添加框架和视图类指针变量

CSplitterFrame *m_pSplitterFrame; //分割视图框架指针
CView *m_pView; //单独视图类,基于CView类
CView *m_pForm; //单独视图类,基于CFormView类
int m_nCurrentID; //记录当前视图

5、在CMainFrame类里面重载OnCreateClient函数,创建视图及分割框架

CRect rcClient;
GetClientRect(&rcClient);
m_pView=new CViewShow;
m_pView->Create(NULL,NULL,AFX_WS_DEFAULT_VIEW &~WS_BORDER,rcClient,this,NULL,pContext);
m_pView->ShowWindow(SW_SHOW);
m_pView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
pContext->m_pNewViewClass=(CRuntimeClass*)m_pView; //设置默认视图类
m_pForm=new CFormShow;
m_pForm->Create(NULL,NULL,AFX_WS_DEFAULT_VIEW &~WS_BORDER,rcClient,this,IDD_FORMSHOW,pContext);
m_pForm->ShowWindow(SW_HIDE);
m_pSplitterFrame=new CSplitterFrame;
m_pSplitterFrame->Create(NULL,NULL,AFX_WS_DEFAULT_VIEW &~WS_BORDER,rcClient,this,NULL,0,pContext);
m_pSplitterFrame->ShowWindow(SW_HIDE);
return true;

6、创建视图切换函数

bool CMainFrame::Switch(int nID)
{
if(nID==m_nCurrentID)
return false;
switch(nID)
{
case IDD_FORMSHOW:
m_pForm->ShowWindow(SW_SHOW);
m_pForm->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
m_pView->ShowWindow(SW_HIDE);
m_pView->SetDlgCtrlID(AFX_IDW_PANE_FIRST+2);
m_pSplitterFrame->ShowWindow(SW_HIDE);
m_pSplitterFrame->SetDlgCtrlID(AFX_IDW_PANE_FIRST+1);
m_nCurrentID=IDD_FORMSHOW;
break;
case AFX_IDW_PANE_FIRST+1:
m_pSplitterFrame->ShowWindow(SW_SHOW);
m_pSplitterFrame->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
m_pForm->ShowWindow(SW_HIDE);
m_pForm->SetDlgCtrlID(IDD_FORMSHOW);
m_pView->ShowWindow(SW_HIDE);
m_pView->SetDlgCtrlID(AFX_IDW_PANE_FIRST+2);
m_nCurrentID=AFX_IDW_PANE_FIRST+1;
break;
case AFX_IDW_PANE_FIRST+2:
m_pView->ShowWindow(SW_SHOW);
m_pView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
m_pForm->ShowWindow(SW_HIDE);
m_pForm->SetDlgCtrlID(IDD_FORMSHOW);
m_pSplitterFrame->ShowWindow(SW_HIDE);
m_pSplitterFrame->SetDlgCtrlID(AFX_IDW_PANE_FIRST+1);
m_nCurrentID=AFX_IDW_PANE_FIRST+2;
break;
default:
break;
}
RecalcLayout();
return true;
}

7、创建调用方法

void CMainFrame::OnForm()
{
// TODO: Add your command handler code here
Switch(IDD_FORMSHOW);
}
void CMainFrame::OnView()
{
// TODO: Add your command handler code here
Switch(AFX_IDW_PANE_FIRST+2);
}
void CMainFrame::OnSplitter()
{
// TODO: Add your command handler code here
Switch(AFX_IDW_PANE_FIRST+1);
}

摘要:本文通过一个程序实例描述了在VC++6.0下如何在单文档程序中通过菜单动态控制多 个窗体的切换。    一、 引言    我们在编制程序中根据需求的不同会在程序风格上选择多文档、单文档或是对话框模式 ,对于单文档模式可能是我们使用比较多的,但有时我们想采用单文档的形式显示多个不同 的窗体,如作为数据库前台应用程序就会遇到此类问题,数据库由大量的表单组成,而同常 一个窗体内只用来显示一个表单,所以要显示其他的表单时就要用到切换窗体的技术了,下 面就通过一个程序说明该技术的实现方法。    二、 实现技术    新建一个基于CFormView的单文档应用程序,再添加一个窗体和与之对应的基于 CFormView的新视类,然后通过在主框架类里添加控制代码和菜单控制实现这两个窗体的动态 切换,下面就是具体的实现过程:    (一) 用"MFC AppWizard(exe)"建立一个新项目"SwitchForm",并在第二步的创建类型上选 择为"Single documnet"单文档模式,第三、四、五、六步均取确省状态,最后一步选择 "CFormView"作为视类的基类。点按"完成"按钮,生成了初始工程"SwitchForm"。    (二) 点选菜单"Insert"、"Resource…",在弹出的"Insert Resource"对话框中"Dialog"树 里的"IDD_FORMVIEW",点击"New"按钮,生成了一个新的窗体,将其ID号改为"IDD_NEXTFORM"。 在原有的窗体上加一个静态框"这是第一个窗体";在新建的窗体上也添加一个静态框"这是第二 个窗体"。    (三) 在菜单资源的"IDR_MAINFRAME"上添加一级菜单"窗体切换",及其二级菜单"第一个窗 体"、"第二个窗体",其标识号分别为"ID_FIRSTFORM"和"ID_SECONDFORM"。修该"第一个窗体" 的属性为"Checked",表明程序初始时显示的是第一个窗体。    (四) 在"ClassView"属性页里的"SwitchForm classes"上右键,在弹出菜单上选择 "New Class…",弹出"New Class"对话框,选择"Dialog ID:"为我们刚添加的新窗体 "IDD_NEXTFORM",选择"Base class:"为"CFormView",类名取为"CNextFormView",这样就把第 二个窗体对应的视图类添加到了工程。 (五) 在框架类里添加函数SwitchToForm(): void CMainFrame::SwitchToForm(int nForm) { file://获取原来的活动窗体的视图句柄 CView* pOldActiveView = GetActiveView(); file://获取由"nForm"标识的窗体所对应的视图句柄 CView* pNewActiveView = (CView*) GetDlgItem(nForm); file://若视图句柄为空,则创建一新的。 if (pNewActiveView == NULL) { if (nForm == IDD_SW99vCHFORM_FORM) pNewActiveView = (CView*)new CSwitchFormView; if (nForm == IDD_NEXTFORM) pNewActiveView = (CView*)new CNextFormView; CCreateContext context; context.m_pCurrentDoc = pOldActiveView->GetDocument(); pNewActiveView->Create(NULL,NULL,0L, CFrameWnd::rectDefault, this,nForm,&context); pNewActiveView->OnInitialUpdate(); } file://选择pNewActiveView为活动窗体 SetActiveView(pNewActiveView); file://显示活动窗体,隐藏非活动窗体 pNewActiveView->ShowWindow(SW_SHOW); pOldActiveView->ShowWindow(SW_HIDE); int ID; if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CSwitchFormView)) ID=IDD_SW99vCHFORM_FORM; if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CNextFormView)) ID=IDD_NEXTFORM; file://设置窗体的ID号 pOldActiveView->SetDlgCtrlID(ID); pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST); RecalcLayout(); }    (六)添加两个菜单相对应的命令响应函数和更新函数如下: void CMainFrame::OnFirstform() { file://通过IsKindOf函数确定当前活动窗口是否是第一个窗口,如是,则无须切换, file://否则将通过SwitchToForm函数将当前活动窗口切换到"IDD_SW99vCHFORM_FORM" file://标识的第二个窗体。 if (GetActiveView()->IsKindOf(RUNTIME_CLASS(CSwitchFormView))) return; SwitchToForm(IDD_SW99vCHFORM_FORM); } void CMainFrame::OnUpdateFirstform(CCmdUI* pCmdUI) { file://通过IsKindOf函数判断当前活动窗口是否是第一个窗体,如是则将其选中。 pCmdUI->SetCheck(GetActiveView()->IsKindOf(RUNTIME_CLASS(CSwitchFormView))); } void CMainFrame::OnSecondform() { if (GetActiveView()->IsKindOf(RUNTIME_CLASS(CNextFormView))) return; SwitchToForm(IDD_NEXTFORM); } void CMainFrame::OnUpdateSecondform(CCmdUI* pCmdUI) { pCmdUI->SetCheck(GetActiveView()->IsKindOf(RUNTIME_CLASS(CNextFormView))); }    然后再在该文件开始处添加对两个视图类的引用: #include "SwitchFormDoc.h" #include "SwitchFormView.h" #include "NextFormView.h"    在此须注意:应在两个视类的引用之前添加对文档类的引用,否则会引起编译错误。另外,由于视 类的构造函数在声明时都确省的声明为保护型的,在框架类中无法引用,所以还要将两个视类的类 声明改动如下: class CNextFormView : public CFormView { public: file://将protected 改为public. CNextFormView(); …… }; class CSwitchFormView : public CFormView { public: file://将protected 改为public. CSwitchFormView(); …… };    三、 编译运行    编译运行程序,开始时的窗体上有"这是第一个窗体的字样",菜单也只有"第一个窗体"是被选中的, 当前的活动窗体是第一个窗体;点击菜单"第二个窗体",视图中的窗体上的字样变成了"这是第二 个 窗体",同时选中的菜单也由"第一个窗体"变成了"第二个窗体",实现了通过菜单将窗体进行动态切换。    总结:此程序中关键的是SwitchToView函数,在此函数中,程序搜索所有当前文档的显示窗口来查找与CruntimeClass变量匹配的视图类。如果找到,该窗口被激活。通过与之类似的方法,还可以实现在多文档模式下的单档(文档)多视(视图),通过不同的视图以不同的方式显示来自同一份文档的数据,以更好的满足程序的需要。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值