曾经,不少程序员对Visual C++中的MFC编程方式抨击很多,其最主要原因可能就是:MFC对应用程序代码封装太多,使得大多数人很难理解其内部的工作方式。现在,我倒留念起这种编程方式,因为在托管C++中,编写一个文档应用程序是非常复杂的,尽管在编写过程中能很好地帮助我们理解其内部机制,但这样的代价似乎有点大。
幸好,我们对MFC的文档、视图和框架类之间的联系的作用有了一定的了解,因此编写托管C++的文档应用程序倒觉得不那么复杂。那么,事情该如何进行呢?
一、.NET框架中文档应用程序的机制
.NET框架只提供多文档应用程序的机制,这是因为在单文档应用程序中,文档窗口可以用相应的控件来代替(如上一篇的综合示例),由于视图也是一个窗口,因此.NET框架没有“视图类”的概念,也没有“单文档”概念,也就是说.NET框架不再使用MFC的“文档/视图”机制。.NET框架使用下列的文档应用程序的创建机制:
1、主框架窗口的创建
MDI(多文档)应用程序的主框架窗口与前面所说的Win表单窗口没有太大的区别,所不同的是:这里的主框架窗口只是将其IsMdiContainer属性设置为true。通常菜单栏、工具栏和状态栏也在此窗口中添加,添加的方法前面已介绍过。
2、子窗口的创建
子窗口也称MDI的文档窗口,它也是从Form类派生而来,它的设计和构造方法同一般Win表单窗口是一样的,可以添加相应的控件。由于MDI的文档窗口是属于主框架窗口的,因此在具体实例化的时候要指定相应的MDIParent。
3、文档窗口的操作
.NET框架对MDI中的文档窗口提供了直接的支持,这包括文档窗口的布局显示、在菜单中显示出文档窗口列表以及当前文档窗口的获得等,下面分别讨论。
(1) 文档窗口的布局显示
在Form类中包含一个LayoutMdi方法,允许用户在主框架窗口调用方法使用所有的文档窗口按一定的方式排列布局,其参数可以是:
MdiLayout::ArrangeIcons —— 在主框架窗口的客户区中显示各个文档窗口的图标
MdiLayout::Cascade —— 层叠显示
MdiLayout::TileHorizontal —— 在主框架窗口的客户区中从上到下显示各个文档窗口
MdiLayout::TileVertical —— 在主框架窗口的客户区中从左到右显示各个文档窗口
(2) 在菜单中显示出文档窗口列表
想要在主框架中的某个菜单项显示出所有文档窗口,以便通过菜单项切换相应的文档窗口,.NET框架提供最直接也是最简单的方法,只要将该菜单项的MdiList属性设为true即可。
(3) 当前文档窗口的获得
当向文档窗口传送数据或进行相关文档操作时,都可能需要获取当前文档窗口的指针以便进行相关操作。当前文档窗口的获得是通过主框架窗口的ActiveMDIChild属性来实现的。例如下面的代码:
Form *curChild = this->ActiveMdiChild;
二、一个简单的MDI综合示例
下面给出一个简单的MDI应用程序代码,运行后结果如图1所示。
#using
using namespace System;
#using "System.dll"
#using "System.Windows.Forms.dll"
#using "System.Drawing.dll"
using namespace System::ComponentModel;
using namespace System::Windows::Forms;
using namespace System::Drawing;
// 这是子窗口
__gc class ChildWnd: public Form
{
private:
TextBox* m_pTextBox;
public:
ChildWnd()
{
InitChildWnd(); // 调用自定义的表单初始化代码
}
void Dispose() // 表单释放,类似于析构函数
{
// 所有的删除代码添加在这里
Form::Dispose(); // 默认的释放操作
}
void InitChildWnd()
{
Text = S"文档窗口——未命名";
m_pTextBox = new TextBox();
m_pTextBox->Dock = DockStyle::Fill;
m_pTextBox->Multiline = true;
m_pTextBox->BackColor = Color::White;
m_pTextBox->BorderStyle = BorderStyle::Fixed3D;
Controls->Add(m_pTextBox);
}
};
// 这是主框架窗口
__gc class MainFrame: public Form
{
private:
MainMenu * m_pMenu;
MenuItem * m_pFileMenu;
MenuItem * m_pFileMenuNew;
MenuItem * m_pFileMenuOpen;
MenuItem * m_pFileMenuExit;
MenuItem * m_pWndMenu;
MenuItem * m_pWndMenuIcons;
MenuItem * m_pWndMenuCascade;
MenuItem * m_pWndMenuTileHori;
MenuItem * m_pWndMenuTileVert;
MenuItem * m_pWndMenuList;
public:
MainFrame()
{
InitMainFrame(); // 调用自定义的表单初始化代码
CreateChildWnd();
}
void Dispose() // 表单释放,类似于析构函数
{
// 所有的删除代码添加在这里
Form::Dispose(); // 默认的释放操作
}
void InitMainFrame()
{
Text = S"Win表单多文档应用程序";
IsMdiContainer = true;
m_pFileMenuNew = new MenuItem( S"新建(&N)",
new EventHandler(this, &MainFrame::OnFileNew));
m_pFileMenuOpen = new MenuItem( S"打开(&O)",
new EventHandler(this, &MainFrame::OnFileOpen));
m_pFileMenuExit = new MenuItem( S"退出(&X)",
new EventHandler(this, &MainFrame::OnFileExit));
MenuItem* rItems[] = new MenuItem *[3];
rItems[0] = m_pFileMenuNew;
rItems[1] = m_pFileMenuOpen;
rItems[2] = m_pFileMenuExit;
m_pFileMenu = new MenuItem( S"文件(&F)", rItems);
m_pWndMenu = new MenuItem( S"窗口(&X)");
m_pWndMenu->MdiList = true;
m_pWndMenuIcons = new MenuItem( S"图标排列");
m_pWndMenuIcons->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuIcons);
m_pWndMenuCascade = new MenuItem( S"窗口层叠");
m_pWndMenuCascade->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuCascade);
m_pWndMenuTileHori = new MenuItem( S"上下平铺");
m_pWndMenuTileHori->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuTileHori);
m_pWndMenuTileVert = new MenuItem( S"左右平铺");
m_pWndMenuTileVert->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuTileVert);
m_pMenu = new MainMenu();
m_pMenu->MenuItems->Add(m_pFileMenu);
m_pMenu->MenuItems->Add(m_pWndMenu);
Menu = m_pMenu;
}
void OnFileNew(Object *sender, EventArgs *e)
{
CreateChildWnd();
}
void OnFileOpen(Object *sender, EventArgs *e)
{
OpenFileDialog *dlg = new OpenFileDialog();
dlg->Filter = S"所有文件|*.*";
dlg->ShowDialog();
}
void OnFileExit(Object *sender, EventArgs *e)
{
Close();
}
void OnWndMenu(Object *sender, EventArgs *e)
{
if(sender->Equals(m_pWndMenuIcons))
{
LayoutMdi(MdiLayout::ArrangeIcons);
}
else if(sender->Equals(m_pWndMenuCascade))
{
LayoutMdi(MdiLayout::Cascade);
}
else if(sender->Equals(m_pWndMenuTileHori))
{
LayoutMdi(MdiLayout::TileHorizontal);
}
else if(sender->Equals(m_pWndMenuTileVert))
{
LayoutMdi(MdiLayout::TileVertical);
}
}
void CreateChildWnd(void)
{
ChildWnd *pWnd = new ChildWnd();
pWnd->MdiParent = this;
pWnd->Show();
}
};
#ifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{
Application::Run(new MainFrame()); // 启用应用程序,运行该类
return 0;
}
![](http://www.yesky.com/20020520/jt-2002-5-20.gif)
从以上可以看出,一般的MDI应用程序是非常简单的。但是若需要子文档窗口之间的数据传送以及增加视图的概念,那么就变得复杂许多,不过我们仍然可以用托管C++来设计这样的机制,下一篇,我们就来讨论这个问题。
幸好,我们对MFC的文档、视图和框架类之间的联系的作用有了一定的了解,因此编写托管C++的文档应用程序倒觉得不那么复杂。那么,事情该如何进行呢?
一、.NET框架中文档应用程序的机制
.NET框架只提供多文档应用程序的机制,这是因为在单文档应用程序中,文档窗口可以用相应的控件来代替(如上一篇的综合示例),由于视图也是一个窗口,因此.NET框架没有“视图类”的概念,也没有“单文档”概念,也就是说.NET框架不再使用MFC的“文档/视图”机制。.NET框架使用下列的文档应用程序的创建机制:
1、主框架窗口的创建
MDI(多文档)应用程序的主框架窗口与前面所说的Win表单窗口没有太大的区别,所不同的是:这里的主框架窗口只是将其IsMdiContainer属性设置为true。通常菜单栏、工具栏和状态栏也在此窗口中添加,添加的方法前面已介绍过。
2、子窗口的创建
子窗口也称MDI的文档窗口,它也是从Form类派生而来,它的设计和构造方法同一般Win表单窗口是一样的,可以添加相应的控件。由于MDI的文档窗口是属于主框架窗口的,因此在具体实例化的时候要指定相应的MDIParent。
3、文档窗口的操作
.NET框架对MDI中的文档窗口提供了直接的支持,这包括文档窗口的布局显示、在菜单中显示出文档窗口列表以及当前文档窗口的获得等,下面分别讨论。
(1) 文档窗口的布局显示
在Form类中包含一个LayoutMdi方法,允许用户在主框架窗口调用方法使用所有的文档窗口按一定的方式排列布局,其参数可以是:
MdiLayout::ArrangeIcons —— 在主框架窗口的客户区中显示各个文档窗口的图标
MdiLayout::Cascade —— 层叠显示
MdiLayout::TileHorizontal —— 在主框架窗口的客户区中从上到下显示各个文档窗口
MdiLayout::TileVertical —— 在主框架窗口的客户区中从左到右显示各个文档窗口
(2) 在菜单中显示出文档窗口列表
想要在主框架中的某个菜单项显示出所有文档窗口,以便通过菜单项切换相应的文档窗口,.NET框架提供最直接也是最简单的方法,只要将该菜单项的MdiList属性设为true即可。
(3) 当前文档窗口的获得
当向文档窗口传送数据或进行相关文档操作时,都可能需要获取当前文档窗口的指针以便进行相关操作。当前文档窗口的获得是通过主框架窗口的ActiveMDIChild属性来实现的。例如下面的代码:
Form *curChild = this->ActiveMdiChild;
二、一个简单的MDI综合示例
下面给出一个简单的MDI应用程序代码,运行后结果如图1所示。
#using
using namespace System;
#using "System.dll"
#using "System.Windows.Forms.dll"
#using "System.Drawing.dll"
using namespace System::ComponentModel;
using namespace System::Windows::Forms;
using namespace System::Drawing;
// 这是子窗口
__gc class ChildWnd: public Form
{
private:
TextBox* m_pTextBox;
public:
ChildWnd()
{
InitChildWnd(); // 调用自定义的表单初始化代码
}
void Dispose() // 表单释放,类似于析构函数
{
// 所有的删除代码添加在这里
Form::Dispose(); // 默认的释放操作
}
void InitChildWnd()
{
Text = S"文档窗口——未命名";
m_pTextBox = new TextBox();
m_pTextBox->Dock = DockStyle::Fill;
m_pTextBox->Multiline = true;
m_pTextBox->BackColor = Color::White;
m_pTextBox->BorderStyle = BorderStyle::Fixed3D;
Controls->Add(m_pTextBox);
}
};
// 这是主框架窗口
__gc class MainFrame: public Form
{
private:
MainMenu * m_pMenu;
MenuItem * m_pFileMenu;
MenuItem * m_pFileMenuNew;
MenuItem * m_pFileMenuOpen;
MenuItem * m_pFileMenuExit;
MenuItem * m_pWndMenu;
MenuItem * m_pWndMenuIcons;
MenuItem * m_pWndMenuCascade;
MenuItem * m_pWndMenuTileHori;
MenuItem * m_pWndMenuTileVert;
MenuItem * m_pWndMenuList;
public:
MainFrame()
{
InitMainFrame(); // 调用自定义的表单初始化代码
CreateChildWnd();
}
void Dispose() // 表单释放,类似于析构函数
{
// 所有的删除代码添加在这里
Form::Dispose(); // 默认的释放操作
}
void InitMainFrame()
{
Text = S"Win表单多文档应用程序";
IsMdiContainer = true;
m_pFileMenuNew = new MenuItem( S"新建(&N)",
new EventHandler(this, &MainFrame::OnFileNew));
m_pFileMenuOpen = new MenuItem( S"打开(&O)",
new EventHandler(this, &MainFrame::OnFileOpen));
m_pFileMenuExit = new MenuItem( S"退出(&X)",
new EventHandler(this, &MainFrame::OnFileExit));
MenuItem* rItems[] = new MenuItem *[3];
rItems[0] = m_pFileMenuNew;
rItems[1] = m_pFileMenuOpen;
rItems[2] = m_pFileMenuExit;
m_pFileMenu = new MenuItem( S"文件(&F)", rItems);
m_pWndMenu = new MenuItem( S"窗口(&X)");
m_pWndMenu->MdiList = true;
m_pWndMenuIcons = new MenuItem( S"图标排列");
m_pWndMenuIcons->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuIcons);
m_pWndMenuCascade = new MenuItem( S"窗口层叠");
m_pWndMenuCascade->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuCascade);
m_pWndMenuTileHori = new MenuItem( S"上下平铺");
m_pWndMenuTileHori->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuTileHori);
m_pWndMenuTileVert = new MenuItem( S"左右平铺");
m_pWndMenuTileVert->Click += new EventHandler(this, &MainFrame::OnWndMenu);
m_pWndMenu->MenuItems->Add(m_pWndMenuTileVert);
m_pMenu = new MainMenu();
m_pMenu->MenuItems->Add(m_pFileMenu);
m_pMenu->MenuItems->Add(m_pWndMenu);
Menu = m_pMenu;
}
void OnFileNew(Object *sender, EventArgs *e)
{
CreateChildWnd();
}
void OnFileOpen(Object *sender, EventArgs *e)
{
OpenFileDialog *dlg = new OpenFileDialog();
dlg->Filter = S"所有文件|*.*";
dlg->ShowDialog();
}
void OnFileExit(Object *sender, EventArgs *e)
{
Close();
}
void OnWndMenu(Object *sender, EventArgs *e)
{
if(sender->Equals(m_pWndMenuIcons))
{
LayoutMdi(MdiLayout::ArrangeIcons);
}
else if(sender->Equals(m_pWndMenuCascade))
{
LayoutMdi(MdiLayout::Cascade);
}
else if(sender->Equals(m_pWndMenuTileHori))
{
LayoutMdi(MdiLayout::TileHorizontal);
}
else if(sender->Equals(m_pWndMenuTileVert))
{
LayoutMdi(MdiLayout::TileVertical);
}
}
void CreateChildWnd(void)
{
ChildWnd *pWnd = new ChildWnd();
pWnd->MdiParent = this;
pWnd->Show();
}
};
#ifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{
Application::Run(new MainFrame()); // 启用应用程序,运行该类
return 0;
}
![](http://www.yesky.com/20020520/jt-2002-5-20.gif)
从以上可以看出,一般的MDI应用程序是非常简单的。但是若需要子文档窗口之间的数据传送以及增加视图的概念,那么就变得复杂许多,不过我们仍然可以用托管C++来设计这样的机制,下一篇,我们就来讨论这个问题。