MDI是多文档界面的简称。在这种界面下,所有子窗口都在父窗口之内,子窗口之间相互独立,可以在父窗口内活动和排列,也可以放大后占据父窗口的所有空间(遮盖其他子窗口)。
MDI界面中,主要的两个类是wxMDIParentFrame和wxMDIChildFrame。wxMDIParentFrame代表MDI父窗口,wxMDIChildFrame代表MDI子窗口。一般MDI程序需要一个继承自wxMDIParentFrame的对象作为主窗口,主窗口可以包含多个继承自wxMDIChildFrame的子窗口。
一个MDI窗口程序例子如下。该程序可以通过单机工具栏按钮创建子窗口,运行效果如下:
(1)主窗口类代码如下:
/// MainFrame.h
#pragma once
#include "wxInc.h"
class MainFrame : public wxMDIParentFrame
{
DECLARE_EVENT_TABLE();
public:
MainFrame(void);
~MainFrame(void);
public:
void CreateBars();
void CreateChild(wxCommandEvent & evt);
};
/// MainFrame.cpp
#include "MainFrame.h"
enum {
ID_ITEM_1 = wxID_HIGHEST + 1,
ID_ITEM_2,
ID_ITEM_3,
ID_ITEM_4,
};
BEGIN_EVENT_TABLE(MainFrame, wxMDIParentFrame)
EVT_TOOL(ID_ITEM_3, CreateChild)
END_EVENT_TABLE()
MainFrame::MainFrame() : wxMDIParentFrame(NULL, wxID_ANY, wxT("main window"))
{
/// 创建菜单栏、工具栏和状态栏.
CreateBars();
}
MainFrame::~MainFrame()
{
}
void MainFrame::CreateBars()
{
/// 1. 创建菜单栏
wxMenuBar * menuBar = new wxMenuBar();
wxMenu * menu1 = new wxMenu();
menu1->Append(ID_ITEM_1, wxT("subitem 1-1"));
menu1->Append(ID_ITEM_2, wxT("subitem 1-2"));
menuBar->Append(menu1, wxT("item 1"));
SetMenuBar(menuBar);
/// 2. 创建工具栏
wxToolBar * toolBar = new wxToolBar(this, wxID_ANY);
toolBar->AddTool(ID_ITEM_3, wxT("button1"), wxArtProvider::GetBitmap(wxART_GO_BACK, wxART_TOOLBAR));
toolBar->AddTool(ID_ITEM_4, wxT("button2"), wxArtProvider::GetBitmap(wxART_TIP, wxART_TOOLBAR));
toolBar->Realize(); /// 在增加按钮后必须调用此函数
SetToolBar(toolBar);
/// 3. 创建状态栏
wxStatusBar * statusBar = CreateStatusBar(2);
}
void MainFrame::CreateChild(wxCommandEvent & evt)
{
/// 创建子窗口(自动显示).
wxMDIChildFrame * childWnd = new wxMDIChildFrame(this, wxID_ANY, wxT("child window"));
}
(2)主程序代码如下:
/// MyApp.h
#pragma once
#include "wxInc.h"
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
DECLARE_APP(MyApp)
/// MyApp.cpp
#include "MyApp.h"
#include "MainFrame.h"
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
MainFrame * mainWnd = new MainFrame();
SetTopWindow(mainWnd);
mainWnd->Show(true);
return true;
}
通过AUI,也可实现Tab风格的MDI,即每个子窗口占据父窗口的全部空间,子窗口通过标签进行选择和显示(类似Visual Studio的风格)。
在AUI框架下,实现Tab风格MDI程序需要两个步骤:
程序的其他部分可以基本保持不变。
只需要改动主窗口代码即可。主窗口代码如下:
/// MainFrame.h
#pragma once
#include "wxInc.h"
class MainFrame : public wxAuiMDIParentFrame
{
DECLARE_EVENT_TABLE();
public:
MainFrame();
~MainFrame();
private:
void CreateBars();
void CreatePanes();
private:
void OnTest1(wxCommandEvent & evt);
void OnClose(wxCloseEvent & evt);
private:
wxAuiManager mAuiMgr;
};
/// MainFrame.cpp
#include "MainFrame.h"
enum {
ID_TEST1 = wxID_HIGHEST + 1,
};
BEGIN_EVENT_TABLE(MainFrame, wxAuiMDIParentFrame)
EVT_MENU(ID_TEST1, OnTest1)
EVT_CLOSE(OnClose) /// 注册窗口关闭消息处理函数
END_EVENT_TABLE()
MainFrame::MainFrame()
: wxAuiMDIParentFrame(NULL, wxID_ANY, wxT("main window"),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE)
{
mAuiMgr.SetManagedWindow(this);
CreateBars();
CreatePanes();
//mAuiMgr.Update();
}
MainFrame::~MainFrame()
{
mAuiMgr.UnInit();
}
void MainFrame::CreateBars()
{
wxMenuBar * menuBar = new wxMenuBar();
wxMenu * menu1 = new wxMenu();
menu1->Append(ID_TEST1, wxT("item1"));
menuBar->Append(menu1, wxT("File"));
SetMenuBar(menuBar);
wxToolBar * toolBar = new wxToolBar(this, wxID_ANY);
toolBar->AddTool(ID_TEST1, wxT("test1"), wxArtProvider::GetBitmap(wxART_NEW, wxART_TOOLBAR));
toolBar->Realize();
SetToolBar(toolBar);
CreateStatusBar(2);
}
void MainFrame::CreatePanes()
{
}
void MainFrame::OnTest1(wxCommandEvent & evt)
{
wxAuiMDIChildFrame * childWnd = new wxAuiMDIChildFrame(this, wxID_ANY, wxT("child"));
}
void MainFrame::OnClose(wxCloseEvent & evt)
{
/// 处理窗口关闭消息
wxAuiNotebook * notebook = GetNotebook();
if (notebook)
{
/// 关闭所有子窗口.
notebook->DeleteAllPages();
}
evt.Skip();
}
需要注意的是,当还有子窗口存在时,退出程序有时会抛出异常,而子窗口全部关闭则无此现象。(Visual Studio 2013有此现象,Visual Studio 2010则无 )
所以,通过模拟正常工作时的退出流程,在程序关闭时,通过处理窗口关闭消息(EVT_CLOSE),主动关闭所有子窗口,可以避免出现异常。
EVT_CLOSE的处理函数OnClose代码如下。
void MainFrame::OnClose(wxCloseEvent & evt)
{
wxAuiNotebook * notebook = GetNotebook();
if (notebook)
{
notebook->DeleteAllPages();
}
evt.Skip();
}