注册 登录
编程论坛 VC++/MFC

[原创]窗口切换分割详解

myajax95 发布于 2006-08-23 10:54, 11471 次点击

这里写一下窗口的切换于分割。一般这里说的是单文档界面或者多文档界面的各种分割与切换。多文档的作法和单文档没有什么区别,这里就以单文档为例。在本文最后我会列一个分割对话框的例子。这部份内容不是很少,在书上查得到的我就不详细说了。

一般常用的MFC视窗结构是文档/视窗结构(document/view architecture)。有很多人说这个结构浪费不少资源,不够节约。但我觉得作到界面这一级浪费点资源没什么太大问题。只要不漏内存,不影响效率就已经足够好了。何况这是微软最推崇的标准界面。
文档/视窗(document/view architecture)结构主要由四个class组成。document类,view类,framework类和app类。app类是程序的引擎,在MFC中是最不不要关心的一个类。framwork是窗口的框架,在程序运行开始的时候先生成框架,然后是document class,这里是用来存储数据的。然后是view类,用来显示数据同时作数据交换的。单文档界面只有一个document class,但可以有多了view class。至少有一个view class是active的。可以用GetActiveView()得到它的指针。没个和document class 关联的view class都有一个control ID,这个ID是一个整数。如果总共只显示一个view class,这个class的control ID是AFX_IDW_PANE_FIRST,如果同时显示好几个view class就需要用分割器(splitter)割开。class 名字叫CSplitterWnd。CSplitterWnd有两种不同的切割framework的方式。一种叫动态的,用Create()来实现,切的很不理想。没见过多少class用这种切法。真正应用广泛的是静态切割,用CReateStatic实现。当然从名字上就可以看出静态切割的缺点,就是不能动态重新切分。在本文中我会介绍一个可以实现静态切割的程序。被分割器隔开的窗口的Control ID可以通过IdFromRowCol(row, col)函数得到,row和col是窗口的行数和列数。其数值也是在AFX_IDW_PANE_FIRST。也是一个比较大的数字。所以隐藏当前不想显示的view时把他的control ID改成一个1,2,3之类的很小的数就可以了。

基本知识就说这些,肯定不够详细,大家可以参照Visual C++的各种教程找到详细资料。下面开始说一些具体问题了。从单窗口开始。
1。在Framework中显示一个View。通过菜单或按钮切换成不同的view。假设有三种view: CViewA, CViewB,CViewC。用三个常数表示他们不显示时的control ID.

enum eView {ViewA, ViewB, ViewC};
在CMainFrame加上下面一个函数就可以实现不同窗口的切换了。很易懂,唯一没有说的就是CCreateContext context,这是每次Create一个view时必须设定的。其实也就是m_pCurrentDoc这个指向当前document class的指针需要设定,其它的取默认值就可以了。

void CMainFrame::SwitchToView(eView nView)
{
CView* pOldActiveView = GetActiveView();
CView* pNewActiveView = (CView*) GetDlgItem(nView);
if (pNewActiveView == NULL)
{
switch (nView)
{
case ViewA:
pNewActiveView = (CView*) new CViewA;
break;
case ViewB:
pNewActiveView = (CView*) new CViewB;
break;
case ViewC:
pNewActiveView = (CView*) new CViewC;
break;
}
CCreateContext context;
context.m_pCurrentDoc = pOldActiveView->GetDocument();
pNewActiveView->Create(NULL, NULL, WS_BORDER|WS_CHILD,
CFrameWnd::rectDefault, this, nView, &context);
pNewActiveView->OnInitialUpdate();
}

SetActiveView(pNewActiveView);
pNewActiveView->ShowWindow(SW_SHOW);
pOldActiveView->ShowWindow(SW_HIDE);
pOldActiveView->SetDlgCtrlID(m_nCurrentView);
pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
m_nCurrentView = nView;
RecalcLayout();
}

下面是这个例子的全程序:

只有本站会员才能查看附件,请 登录


5。多个窗口的分割,不只1X1,1X2,2X1,2X2。可以分得十分复杂,比VC IDE上的窗口还多都可以。这时需要用多个Splitter。
6。对话框的切分,没有标准的MFC class,需要自己写一个。
5和6的例子我回头加上。

[此贴子已经被作者于2006-9-18 0:51:27编辑过]

37 回复
#2
beyoung2006-08-23 11:03
多谢版主
多谢版主!
#3
ligt06102006-08-23 11:16
谢谢楼主
收了
#4
myajax952006-08-23 22:21
三个View的,需要更多的话就在switch里面写就可以了。
只有本站会员才能查看附件,请 登录

#5
beyoung2006-08-23 22:26
谢谢版主。
不知道该如何表答谢意了。嘿嘿。
#6
myajax952006-08-23 22:28
写的太快乐,发现m_nPreviousView这个变量好像没用,可以删掉。
#7
ligt06102006-08-26 22:43
实在是应该向楼主好好学习了
居然写的太快乐
#8
wfpb2006-08-27 13:04
void CMainFrame::SwitchToView(eView nView)
{
    CView* pOldActiveView = GetActiveView();     //获取现在活动窗口view指针
    CView* pNewActiveView = (CView*) GetDlgItem(nView);  //不理解GetDlgItem(nView)是什么意思,参数不是ID吗?
    if (pNewActiveView == NULL)
    {
        switch (nView)
        {
        case ViewA:
            pNewActiveView = (CView*) new CViewA;
            break;
        case ViewB:
            pNewActiveView = (CView*) new CViewB;
            break;
        case ViewC:
            pNewActiveView = (CView*) new CViewC;
            break;
        }
        CCreateContext context;  //声名一个上下文
        context.m_pCurrentDoc = pOldActiveView->GetDocument();  //给这个上下文赋值
        pNewActiveView->Create(NULL, NULL, WS_BORDER|WS_CHILD,  //产生新窗口
            CFrameWnd::rectDefault, this, nView, &context);
        pNewActiveView->OnInitialUpdate();                   //更新
    }

    SetActiveView(pNewActiveView);     设置新的view类为当前view
    pNewActiveView->ShowWindow(SW_SHOW);  显示新窗口模式
    pOldActiveView->ShowWindow(SW_HIDE);  隐藏旧窗口模式
    pOldActiveView->SetDlgCtrlID(m_nCurrentView); 将旧窗口模式设置新的标识(identifier)
    pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST); 设置新窗口模式的标识
    m_nCurrentView = nView;   //改变当前窗口类型
    RecalcLayout();                //这在整个函数中起的作用是什么?请指教
}
#9
wfpb2006-08-27 13:06
enum nView{ViewA,ViewB,ViewC};这个类型(枚举型),为什么可以做窗口的标识的?
#10
myajax952006-08-29 05:18
换窗口时需要把当前的View设成 AFX_IDW_PANE_FIRST,这样CFrameWnd::RecalcLayout 的时候会知道这个View是第一个pane。所以会把他显示出来。不显示这个View的时候把View设成任何其它数值。用eView枚举是为了后来 GetDlgItem的时候方便读取。
RecalcLayout是MainFrame resize的时候调整view, toolbar之类时候用的。这时换了view正好用一下。
另外View在Create的时候会在document class里面注册,CDocument::AddView()。这样窗口关闭的时候document class会知道总共有多少个view,逐一destroy window。也就是说,虽然你的View是new出来的,千万别画蛇添足的自己去delete。否则会意外crash。
#11
wfpb2006-08-29 10:22

哦,我看花眼睛了,你枚举里面似乎一定给3个值定义成了对应的ID了吧

#12
myajax952006-08-29 10:55

就是0,1,2啦。什么值都行的。

#13
myajax952006-09-01 15:32
只有本站会员才能查看附件,请 登录

#14
热情依然2006-09-01 15:40
大感谢,正想学这类的东西,因为我见到的多个View类的都要增加document template,这样一运行的时候就会多出一个对话框来选择,很麻烦
#15
myajax952006-09-01 15:45

13楼的例子是左右两窗口切换右面窗口的。

#16
热情依然2006-09-01 17:01
太好了,这两个程序对我用VC++做ERP有很大帮助,哇哈哈哈哈哈哈哈哈
大感谢
俺刚刚才从C++ BUILDER 6的折磨中回来,没有办法,为了那个破BDE数据库引擎
而且调试的时候断点跟踪居然出现致命错误,幸好是调试线程和API,弄到我要在VC.NET重新写一个,然后调试,调试正确再拷贝到C++ BUILDER 6 感谢微软强大的编译器,那个C++ BUILDER,不知道怎样说才好,智能弹出的速度是VC++的 1/3,对机器的要求高,.........痛苦啊

[此贴子已经被作者于2006-9-1 17:06:24编辑过]

#17
juzi40032006-09-04 11:18

请问楼主:
那么多的类,你是怎么做的呢?
怎么让它们组织在一起,
可以和我讲一下具体的步骤吗?

谢谢啊!

我现在学习VC,但是老是觉得不知道从哪开始,老是没有进步,
能给我点建议吗?
比如:推荐本书、网站等,

谢谢啊 !

#18
juzi40032006-09-04 12:34
我说的是第一个压缩文件,
里面那些类好象没有在一个工作区里吧?
是怎么做的呢?
谢谢
#19
myajax952006-09-05 13:31

基本上是拿来主义,四处找例子抄抄。不过写这几个类的人都没写干净,没一个都费了点劲加工。还缺一个本来是单窗口但可以中途切成多窗口,以后又能换回来的例子。明后天如果试出来的具体的写一下。

#20
myajax952006-09-12 11:50
总结了一下各种切分,切换,还差两个例子,回头补上。
#21
digitalhot2006-11-02 18:18
13楼的例子有问题哦,连续3次点击显示分割窗口时,崩溃
#22
digitalhot2006-11-06 17:44
斑竹大人呢...
切换分割视图时,好像Doc丢失了
#23
jilinyouyou2006-12-25 17:48

正准备学习这方面的知识,很高兴这能加入这个论坛,楼住的程序对我的学习很有帮助,谢谢

[此贴子已经被作者于2006-12-25 17:49:17编辑过]

#24
xfeng2007-01-03 17:21
感谢楼主!!!!
解决了我的一个大问题。
#25
tomax2007-07-07 22:43
今年我刚刚考上研究生,导师让我做一个VC程序。要求程序可以完成以下功能:
首先,要创建一个对话框;
其次,要在对话框中创建一个控件,这个控件可以完成如下功能:能显示给定的函数方程对应的曲线图;
还有,在对话框中有一个专门的按钮控制曲线图的显示与否。
请问老师,我用什么方法创建这个控件呢?VC中有没有现成的控件啊?
#26
wpking2007-07-09 08:50
#27
achord2007-07-16 13:32
品字型大家都会分割,口吕型怎么分割?

[此贴子已经被作者于2007-7-16 13:34:00编辑过]


#28
muxiaoyao2007-07-29 16:00
在论坛中看了版主的“窗口切换分割详解”一文,非常感谢,但是我对第一个程序中的一句话很不理解,如下:

void CMainFrame::SwitchToView(eView nView)
{
CView* pOldActiveView = GetActiveView();


CView* pNewActiveView = (CView*) GetDlgItem(nView);//就是这句,好像用它得到的pNewActiveView一直都是NULL,我把它换成下面一句,程序照样可以运行,给个理由?
// CView* pNewActiveView = NULL;


if (pNewActiveView == NULL)
。。。。。
}

nView 只是一个enum元素,为什么要放在这里?
#29
colama2008-01-14 03:35
楼上几位,都多久以前的帖子了~
#30
greenwings2009-08-10 17:37
新手上路,代码很有用
#31
浩浩19862010-01-06 16:56
楼主真是太有才了~
#32
浩浩19862010-01-06 17:02
感激之情,无以复加~
#33
Alidesheng2010-05-26 19:00
多谢!!
#34
sculfm2010-06-18 19:58
回复 楼主 myajax95
谢谢楼主!
#35
sculfm2010-06-18 20:04
附加怎么不能下载啊??
#36
mkj_mkj2013-01-01 15:42
谢谢,最近在学习
#37
rout20122013-01-15 02:01
学习了
#38
Troy_lee2015-02-11 10:57
多谢版主分享
1