窗口分割的必要性:有时候我们需要在一个窗口内显示不同的信息,类似于下图所示,每个窗口直接交互并不大,或者我们需要将一个exe嵌入到一个窗口中,这样就用到了窗口分割。窗口分割技术在MFC的单文档文件中比较容易实现,其他编程语言或者对话框可能也可以实现,但是实现起来比较复杂,与此同时没有现成的例子或者函数。
实现窗口分割的步骤:
(1)建立一个单文档程序:新建—项目—Visual C++—MFC应用程序,选择单文档,不需要拆分窗口,因为如果选择了拆分窗口,系统会给你定义一个窗口分割变量,不如自己直接定义可操作性强。这里我建立的程序名称叫DisplayCenter。
(2)在在MainFrm.h中添加分割窗口的变量声明。
//分割窗口变量
CSplitterWnd VSplitter; //用于纵向切割
CSplitterWnd HSplitter1; //用于横向切割
CSplitterWnd HSplitter2; //用于横向切割
//标识窗口分割是否完成
BOOL IsSplitDone;
为什么要定义三个分割窗口的变量呢?因为后面我们肯定要调整分割窗口的大小,如果你要设置的N个窗口一样大,那就完全没必要用多个分割窗口变量,但是往往我们分隔的窗口如图6所示,并非一样大,那么就需要定义几个不同的分割变量,来对应控制相应的大小,这个在后面会讲到。最后一个布尔型变量用于标志窗口分割是否已经完成,为什么需要这个变量呢?因为后面我们可能要调整每个窗口的大小,但是调整和创建是有先后顺序的,也就是要先对大窗口进行分割,分割完了才能调整每个小窗口的大小,有了这个变量,就方便我们对窗口分割是否完成做好判断。
(3)并在CMainFrame的构造函数中,初始化IsSplitDone为FALSE。
CMainFrame::CMainFrame()
{
// TODO: 在此添加成员初始化代码
IsSplitDone = FALSE;
}
(4)重载OnCreateClient()函数,进行窗口的分割
在类视图中点击CmainFrame,然后点击属性中的重写栏,找到OnCreateClient并添加,如图7所示:
在重载函数中添加窗口分割代码:
//获取客户区大小
CRect cr;
GetClientRect(&cr);
// 如果纵向分割(分成*2的)不成功,返回
if(!VSplitter.CreateStatic(this,1,2))
{
return FALSE;
}
//纵向分割完成后对第一行一列横向分割,分成2*1的
if(HSplitter1.CreateStatic(&VSplitter,2,1,WS_CHILD|WS_VISIBLE,
VSplitter.IdFromRowCol(0, 0))==NULL)
{
return FALSE; //将第行列再分开行列
}
//绑定相应的视类
if (!HSplitter1.CreateView(0,0,RUNTIME_CLASS(PPIshow),CSize(100,200),pContext) )
{
return FALSE;
}
if (!HSplitter1.CreateView(1,0,RUNTIME_CLASS(CDisplayCenterView),CSize(100,200),pContext) )
{
return FALSE;
}
//纵向分割完成后对第一行二列横向分割,分成2*1的
if(HSplitter2.CreateStatic(&VSplitter,2,1,WS_CHILD|WS_VISIBLE,
VSplitter.IdFromRowCol(0, 1))==NULL)
{
return FALSE; //将第行列再分开行列
}
//绑定相应的视类
if (!HSplitter2.CreateView(0,0,RUNTIME_CLASS(CDisplayCenterView),CSize(100,200),pContext) )
{
return FALSE;
}
if (!HSplitter2.CreateView(1,0,RUNTIME_CLASS(CDisplayCenterView),CSize(100,200),pContext) )
{
return FALSE;
}
//标志位置为True,标志窗口分割完成,用于后面onsize判断
IsSplitDone = TRUE;
return TRUE;
对于其中CreateStatic、CreateView函数每个参数的含义,具体用法这里不再赘述,具体可查询MSDN,这里简单的说明一下,CreateStatic是创建静态窗口分割,就是把窗口分割开来,分割完了之后,每个窗口肯定要显示一些东西,那么就需要与视类进行绑定,CreateView完成的就是与视类进行绑定的作用。
(5)头文件声明
在(4)中进行视类绑定的时候,调用了一些视类,要把视类想对应的头文件引用声明在MainFrm.cpp里面,也就是:
//调用了其他的视类和文档类,进行声明
#include "DisplayCenterView.h"
#include "DisplayCenterDoc.h"
#include "PPIshow.h"
//调用了打开exe的Windows API,进行声明
#include "shellapi.h"
(6)创建一个自己的视类
从(4)中绑定视类中可以看到,我们的第一个窗口绑定的是一个名叫PPIshow的类,其他三个是自动生成的View类,这个PPIshow类是我自己创建的一个视类,这也是非常正常的,一般我们都要自己写视类,一般的视类都基类都是Cview,同时我重载了Ondraw函数;激活了WM_CREATE消息,也就是OnCeate函数,用于视类的初始化;激活了WM_SIZE消息,也就是OnSize函数,用于控制窗口大小变化时候的窗口处理。此外,这样的视类是没法直接绑定的,会提示说创建空白文档失败,此时你要在这个视类的源文件(cpp文件)和头文件(h文件)中声明,声明的位置如图8、图9所示,声明之后,即可正常绑定视类。
IMPLEMENT_DYNCREATE(PPIshow,CView)
DECLARE_DYNCREATE(PPIshow)
(7)在分割窗口中嵌入EXE
在分割窗口中绑定视类之后,有时候我们要将这个视类里面嵌入一个我们自己写的EXE,这个我仅以一段代码例子来进行简单介绍,需要调用的头文件为#include"shellapi.h"。
//查看P显和B显是否已经打开,默认开P显
CWnd* pWnd = CWnd::FindWindowA(NULL,"PPI显示器");
CWnd* BWnd = CWnd::FindWindowA(NULL,"B显");
//如果没有打开,那么把P显最大化打开,B显最小化打开(即不显示)
if (!pWnd)
{
HINSTANCE m=ShellExecute(m_hWnd,"open","ppiView.exe",NULL,NULL,SW_SHOWMAXIMIZED);
}
if (!BWnd)
{
HINSTANCE m=ShellExecute(m_hWnd,"open","BView.exe",NULL,NULL,SW_SHOWMINIMIZED);
}
//缓冲毫秒,防止程序卡死
Sleep(500);
//查找P显的窗口,并将P显嵌入到相应的窗口里面
m_hFig = ::FindWindow(NULL,"PPI显示器");
if(::IsWindow(m_hFig))
{
//通过SetParent设置父窗口将P显嵌入
::SetParent(m_hFig,this->m_hWnd);
CWnd * pWnd = NULL;
//找到相应的CWnd
pWnd = FromHandle(m_hFig);
//获取客户区大小,方便填满
CRect rect;
this->GetClientRect(&rect);
pWnd->MoveWindow(&rect,false);
//去掉边框什么的
pWnd->ModifyStyle(WS_CAPTION|WS_BORDER|WS_THICKFRAME,0);
//将EXE显示出来
pWnd->ShowWindow(SW_SHOW);
}
m1_hFig = ::FindWindow(NULL,"B显");
if(::IsWindow(m1_hFig))
{
//::SetParent(m_hFig,this->GetSafeHwnd());
::SetParent(m1_hFig,this->m_hWnd);
CWnd * pWnd = NULL;
pWnd = FromHandle(m1_hFig);
CRect rect;
this->GetClientRect(&rect);
pWnd->MoveWindow(&rect,true);
//pWnd->ModifyStyle(WS_CAPTION|WS_BORDER|WS_THICKFRAME,0);
pWnd->ShowWindow(SW_HIDE);
}
UpdateData(FALSE);