我们在编写程序时,为了加强程序的易用性和获得良好的用户界面,很多时候会用到嵌套式窗口(Splitter
window)。由于在含有分栏窗口的程序中,涉及的类将比较多,分栏窗口的各窗格间要进行相互通信,所经的途径也将会比较复杂,稍有不慎,便可能引起程序的错误。本文将介绍一种较安全的实现分栏窗口间相互通信的办法。
一、 程序原理
要实现嵌套式分栏窗口,有两种方法,一种为动态创建,一种为静态创建。由于动态创建的代码相对要复杂,所受限制较多(动态窗格不可以超过两行两列),因而本文只介绍静态创建分栏窗口的方法。
熟悉MFC编程的朋友都知道,MFC程序都有一个主窗口CMainFrame类,这个主窗口可以与程序中的任一类别的窗口类进行通信。于是,我们要在分栏窗口中进行不同类别不同窗格的窗口类通信时,只需要根据当前窗格找到主窗口的指针,再利用该指针便可与其它窗格通信。对于有CDocument类相关联的CView类来说,这种方法尤为重要,因为在有CDocument类相关联的CView类,是不可以安全地与除相关联的CDocument类之外的其它类通信的。所以当要与这样一个类通信时,要先根据主窗口指针得到与之相关联CDocument派生类指针,再用该指针调用其函数进行通信。以上细节,下面程序中均会有体现。
二、 代码简介及函数说明
要实现分栏窗口,我们要用到一个MFC类CSplitterWnd类,这个类是专门用于实现分栏窗口的,这个类中较重要并出现于本程序中的函数说明如下:
1.BOOL CreateStatic(CWnd? pParentWnd, int nRows, int nCol,
DWORD dwStyle=WS_CHILD|WS_VISIBLE,UINT nID=AFX_IDW_PANE_FIRST);
功能描述:以pParentWnd为父窗口指针,创建一个含有nRow行、nCol列的静态分栏窗口,并连接到CSplitterWnd类对象中。
2.virtual BOOL CreateView(int row,int col,CRuntimeClass? pViewClass,SIZE
SizeInit,
CCreateContext? pContext);
功能描述:把一个CView类连接到由row行col列指定的窗格中。
3. CWnd? GetPane(int Row,int Col);
功能描述:返回由Row行、Col列指定窗格的窗口指针。
4. int IdFormRowCol (int row, int col );
功能描述:返回row行col列指定的窗格中的ID号。
三、 程序实现及代码分析
1.用AppWizard生成一个单文档(SDI)应用程序框架(程序中命名为CUseSplitter)。为了在分栏窗口中显示内容,我们还要另外实现两个CView类。
(1)用对话框生成器生成一个对话框,设置其style属性为Chile
。用ClassWizard从CFormView类中派生一个类(CSelectView),与对话框关联。
(2)用ClassWizard从CView类中派生一个新类,命名为CInputView。此时,待连接的View类已准备好。
2.生成静态分栏窗口,并将刚生成的类连同AppWizard
自动生成的CUseSplitterView类总共三个View派生类连接到分栏窗格中。
(1)在主窗口的头文件Mainfrm.h中加入如下代码:
public:
CSplitterWnd m_wndSplitter ;
CSplitterWnd m_wndSplitter2;
(2)启动ClassWizard,选择主窗口CMainFrame类,重载OnCreateClient函数,修改其代码如下:
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /?lpcs?/,
CCreateContext? pContext)
{//创建一个静态分栏窗口,分为一行两列
if(m_wndSplitter.CreateStatic(this,1,2)==NULL) return FALSE;
m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CSelectView),CSize(100,100),
pContext);
//将CSelectView连接到0行0列窗格上
if(m_wndSplitter2.CreateStatic(&&m_wndSplitter,2,1,
WS_CHILD|WS_VISIBLE,m_wndSplitter.IdFromRowCol(0, 1))==NULL)
return FALSE;//将第0行1列再分开2行1列
//将CInputView类连接到第二个分栏对象的0行0列
m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CInputView),CSize(400,300),pContext);
//将CUseSplitterView类连接到第二个分栏对象的1行0列
m_wndSplitter2.CreateView(1,0,pContext->m_pNewViewClass,CSize(400,300),pContext);
return TRUE;
}
3.建立窗格间的通信。
(1)建立CSelectView 和CInputView间的通信。
确定在CSelectView的实现文件SelectView.cpp中包含如下头文件:
#include "MainFrm.h"
#include "CUseSplitterDoc.h"
#include "InputView.h"
在第一步生成的对话框中加入一个按钮(程序中为ID_CHECK),用ClassWizard生成其单击响应函数,修改如下:
void CSelectView::OnCheck()
{
//取得主窗口指针。
CMainFrame? MainFrame=(CMainFrame?)this->GetParent()->GetParent();
//取得窗格row行col列窗口CInputView类的当前指针
CInputView? InputView=(CInputView?)MainFrame->m_wndSplitter2.GetPane(0,0);
InputView->DoSelect(); //实现通信。
}
这样,便可实现CSelectView 和CInputView间的通信。当然,在CInputView类中,你应加入响应函数DoSelect()。
(2)建立CSelectView和CUseSplitterView间的通信。
同样,在第一步生成的对话框中加入另一个按钮(程序中为ID_INFORMATION),用ClassWizard生成其单击响应函数,修改如下:
void CSelectView::OnInformation()
{
CMainFrame? MainFrame=(CMainFrame?)this->GetParent()->GetParent();
CCUseSplitterDoc? Doc=(CCUseSplitterDoc?)MainFrame->GetActiveDocument();
if(Doc!=NULL)
Doc->OnInformation();
}
接着,在CUseSplitterDoc中新建并实现如下函数:
void CCUseSplitterDoc::OnInformation()
{
POSITION pos=GetFirstViewPosition();
CCUseSplitterView? View;
View=(CCUseSplitterView?)GetNextView(pos);
View->DoInformation();
// 程序实现通信
}
同样,只要你在CUseSplitterView中实现一个名为DoInformation()的函数,便可以实现CSelectView和CUseSplitterView之间的通信。
至此,建立并实现静态分栏窗口间通信的程序已全部完成,运行程序,按下第一栏中的按钮,就可以出现在另一个窗口类中定义的响应消息。