前面我们所举的例子中都是单文档界面框架,也就是说这个窗口里面的客户区就是一个文档界面,可以编写程序在里面输入或者绘制文本图形输出,但是不能有出现多个文档的情况。比如下面的UltraEdit就是一个典型的多文档界面,他可以同时编辑多个文档,每个文档还可以最大化,最小化等等,我们今天就来看看多文档的基本框架是怎么实现的。

多文档界面的框架创建需要几下几步。

  • 主框架窗口创建

主框架窗的创建跟普通的窗口没有什么区别,就是自己注册一个类并用该类创建一个重叠窗口,这个可以用CreateWindow/CreateWindowEx函数完成,主框架窗口可以用自己的菜单和状态栏。

  • 客户区窗口创建

客户区创建的创建同样用你CreateWindow,但需要指定类为“MDICLIENT”,用这个类会创建多文档的客户区窗口。或者采用CreateWindowEx函数,指定扩展风格为WS_EX_MDICHILD。客户区不需要菜单和状态栏。

  • 视图窗口创建

创建工作或者视图窗口作为实际文档窗口,这个也是需要自己注册类并创建自己需要的视图窗口。视图窗口可以有自己的菜单,一般不需要状态栏。当视图窗口激活时,主窗口的菜单需要替换成视图窗口的菜单,因为这个时候往往是对视图窗口进行操作,菜单替换也是理所当然的。菜单设置通过WM_MDISETMENU消息完成。该消息定义如下:

SendMessage(hWndControl, WM_MDISETMENU, wParam, lParam)
wParam表示新框架窗口菜单,
lParam表示要设置的菜单句柄

OK,基本的API就这些,下面我们直接上demo程序来演示,由于视图一般是一个多文档的实例,所以demo代码中用类来存储视图,每一个视图就是一个类的实例:

主文件,该文件包括主框架窗口、客户窗口已经视图窗口调用:

#include <windows.h>

#define CLIENT_WND_BGROUND

#define IDM_FIRSTCHILD 1001
#define IDM_FILE_NEW 1002 HINSTANCE g_hInst;
TCHAR szMDIMainFrame[]= TEXT("MDI Main Frame");
TCHAR gszAppName[] = TEXT("MDI Demo");
HWND ghWndMDIClient;
HWND ghWndMainFrame;
HMENU ghMainFrameMenu; void CreateChildWndMenu(void);
HWND CreateDocView(HWND hParentWnd, HINSTANCE hInstance);
static LRESULT CALLBACK MainFrameProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); #ifdef CLIENT_WND_BGROUND
static WNDPROC pfOrigClientWndProc;
LRESULT CALLBACK ClientWndSubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_ERASEBKGND:
{
RECT rect;
HDC hDC = (HDC)wParam;
GetClientRect(hwnd, &rect);
HBRUSH hbr = CreateSolidBrush(RGB(, , ));
HBRUSH holdbr = (HBRUSH)SelectObject(hDC, hbr);
FillRect(hDC, &rect, hbr);
SelectObject(hDC, holdbr);
DeleteObject(hbr);
}
return TRUE;
default:
break;
} return CallWindowProc(pfOrigClientWndProc, hwnd, uMsg, wParam, lParam);
}
#endif HMENU CreateMainFrameMenu(void)
{
//主框架菜单
HMENU hMenu = CreateMenu(); //文件菜单
HMENU hFileMenu = CreateMenu();
AppendMenu(hFileMenu, MF_STRING, IDM_FILE_NEW, TEXT("&New"));
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, TEXT("&File")); return hMenu;
} int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG msg;
WNDCLASSEX wndclass = {}; wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MainFrameProc;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszClassName = szMDIMainFrame;
if(!RegisterClassEx(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), gszAppName, MB_ICONERROR);
return ;
}
g_hInst = hInstance; ghMainFrameMenu = CreateMainFrameMenu();
CreateChildWndMenu(); //Create the main MDI frame window
ghWndMainFrame = CreateWindow(szMDIMainFrame,
gszAppName,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, // allows system choose an x position
CW_USEDEFAULT, // allows system choose a y position
,
,
NULL, // handle to parent window
ghMainFrameMenu,// handle to menu
hInstance, // handle to the instance of module
NULL); // Long pointer to a value to be passed to the window through the
ShowWindow(ghWndMainFrame, SW_SHOW);
UpdateWindow(ghWndMainFrame); while(GetMessage(&msg, NULL, , ))
{
TranslateMessage(&msg) ;
DispatchMessage(&msg) ;
} return msg.wParam ;
} LRESULT CALLBACK MainFrameProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CREATE:
{
CLIENTCREATESTRUCT ccs;
ccs.hWindowMenu = NULL;
ccs.idFirstChild = IDM_FIRSTCHILD;
ghWndMDIClient = CreateWindow(TEXT("MDICLIENT"), NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
, , , , hWnd, NULL,
g_hInst, (void *)&ccs);
#ifdef CLIENT_WND_BGROUND
pfOrigClientWndProc = (WNDPROC)SetWindowLong(ghWndMDIClient, GWL_WNDPROC, (LONG)ClientWndSubClassProc);
#endif
}
return ; case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_FILE_NEW:
CreateDocView(ghWndMDIClient, g_hInst);
return ;
}
break; case WM_DESTROY:
PostQuitMessage();
return ;
} return DefFrameProc(hWnd, ghWndMDIClient, message, wParam, lParam);
}

下面是视图窗口,每个视图窗口由一个类来描述,本文仅仅是一个demo,只有实例化一个视图窗口作为演示:

#include <windows.h>

#define IDM_EDIT_COPY 1003
#define IDM_EDIT_PASTE 1004 class CViewInfo
{
public:
CViewInfo(HINSTANCE hInst);
~CViewInfo();
HWND CreateViewWindow(HWND hParentWnd);
private:
HINSTANCE m_hInst;
HWND m_hWndView;
}; static TCHAR szViewWndName[] = TEXT("View Window");
static CViewInfo *g_pSystemInfo;
static HMENU ghChildWndMenu;
extern HMENU ghMainFrameMenu; CViewInfo::CViewInfo(HINSTANCE hInst)
{
m_hWndView = NULL;
m_hInst = hInst;
} CViewInfo::~CViewInfo()
{
m_hWndView = NULL;
} LRESULT CALLBACK ViewWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_MDIACTIVATE:
{
HWND hWndClient = GetParent(hWnd);
HWND hWndFrame = GetParent(hWndClient); HMENU hMenu = GetSubMenu(ghChildWndMenu, );
if (lParam == (LPARAM)hWnd)
{
SendMessage(hWndClient, WM_MDISETMENU, (WPARAM)ghChildWndMenu, (LPARAM)hMenu);
EnableMenuItem(ghChildWndMenu, IDM_EDIT_COPY, MF_GRAYED);
} // Set the framewindow menu if losing focus
if (lParam != (LPARAM)hWnd)
{
SendMessage(hWndClient, WM_MDISETMENU, (WPARAM)ghMainFrameMenu, (LPARAM)NULL);
} // call DrawMenuBar after the menu items are set
DrawMenuBar(hWndFrame); return ;
} case WM_CLOSE:
delete g_pSystemInfo;
break; case WM_DESTROY:
return ;
} return DefMDIChildProc(hWnd, message, wParam, lParam); //Frame window calls DefFrameProc rather than DefWindowProc
} HWND CViewInfo::CreateViewWindow(HWND hParentWnd)
{
WNDCLASSEX wcDoc;
wcDoc.cbSize = sizeof(WNDCLASSEX);
wcDoc.style = CS_HREDRAW | CS_VREDRAW;
wcDoc.lpfnWndProc = ViewWindowProc;
wcDoc.cbClsExtra = ;
wcDoc.cbWndExtra = ;
wcDoc.hInstance = m_hInst;
wcDoc.hIcon = NULL;
wcDoc.hCursor = LoadCursor(NULL, IDC_ARROW);
wcDoc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wcDoc.lpszMenuName = NULL;
wcDoc.lpszClassName = szViewWndName;
wcDoc.hIconSm = NULL; if(!RegisterClassEx(&wcDoc))
{
DWORD dw_LastError = GetLastError();
if(ERROR_CLASS_ALREADY_EXISTS != dw_LastError)
{
return ;
}
} m_hWndView = CreateWindowEx(WS_EX_MDICHILD, szViewWndName,
TEXT("New view"), , CW_USEDEFAULT,
CW_USEDEFAULT, , ,
hParentWnd, NULL, m_hInst, NULL); return m_hWndView;
} HWND CreateDocView(HWND hParentWnd, HINSTANCE hInstance)
{
HWND hView; g_pSystemInfo = new CViewInfo(hInstance);
hView = g_pSystemInfo->CreateViewWindow(hParentWnd);
if (hView == NULL)
{
delete g_pSystemInfo;
g_pSystemInfo = NULL;
} return hView;
} void CreateChildWndMenu(void)
{
//View菜单
ghChildWndMenu = CreateMenu(); //编辑菜单
HMENU hEditMenu = CreateMenu();
AppendMenu(hEditMenu, MF_STRING, IDM_EDIT_COPY, TEXT("&Copy"));
AppendMenu(hEditMenu, MF_STRING, IDM_EDIT_PASTE, TEXT("&Paste"));
AppendMenu(ghChildWndMenu, MF_POPUP, (UINT_PTR)hEditMenu, TEXT("&Edit"));
}

本实例运行后,界面如下:

选择File->New新建一个视图后demo程序如下,可以看到菜单编程视图的菜单:

最大化后可以看到视图窗口和填满客户窗口:

虽然本程序只实现了一个视图的实例,但是再增加一个是很容易的,只要想办法在菜单中调用CreateDocView函数,并且正确处理对象指针即可。实例并没有增加状态栏,因为这个对多文档并不是必须的,要增加的读者可以参考前面的创建Toolbar和Statusbar一文。

本实例实现了一个基本的多文档窗口框架,读者朋友可以在此基础上加上工具栏、状态栏、视图窗口创建对类的处理,多实例以及具体的需求,完成实用化的多文档界面。

更多经验交流可以加入Windows编程讨论QQ群454398517

关注微信公众平台:程序员互动联盟(coder_online),你可以第一时间获取原创技术文章,和(Java/C/C++/Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序员互动联盟,开发人员自己的家。

转载请注明出处http://www.coderonline.net/?p=2052,谢谢合作!

【Windows编程】系列第十一篇:多文档界面框架的更多相关文章

  1. Python高级网络编程系列之终极篇---自己实现一个Web框架

    通过前面几个小节的学习,现在我们想要把之前学到的知识点给串联起来,实现一个很小型的Web框架.虽然很小,但是用到的知识点都是比较多的.如Socket编程,装饰器传参在实际项目中如何使用.通过这一节的学 ...

  2. ElasticSearch查询 第二篇:文档更新

    <ElasticSearch查询>目录导航: ElasticSearch查询 第一篇:搜索API ElasticSearch查询 第二篇:文档更新 ElasticSearch查询 第三篇: ...

  3. 基于.NetCore3.1搭建项目系列 —— 使用Swagger做Api文档 (上篇)

    前言 为什么在开发中,接口文档越来越成为前后端开发人员沟通的枢纽呢? 随着业务的发张,项目越来越多,而对于支撑整个项目架构体系而言,我们对系统业务的水平拆分,垂直分层,让业务系统更加清晰,从而产生一系 ...

  4. 第15.37节 PyQt(Python+Qt)入门学习:containers容器类部件QMdiArea多文档界面部件详解及编程开发案例

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 老猿在前期学习PyQt相关知识时,对每个组件的属性及方法都研 ...

  5. 基于WPF系统框架设计(5)-Ribbon整合Avalondock 2.0实现多文档界面设计(二)

    AvalonDock 是一个.NET库,用于在停靠模式布局(docking)中排列一系列WPF/WinForm控件.最新发布的版本原生支持MVVM框架.Aero Snap特效并具有更好的性能. Ava ...

  6. VC基于单文档OpenGL框架

    本文是在VC6.0的环境下,运用MFC实现的OpenGL最基本框架,需要简单了解MFC编程(会在VC6.0里创建MFC单文档应用程序就行),甚至不必了解OpenGL的知识.以下是具体的步骤. 1.创建 ...

  7. 在Winform框架的多文档界面中实现双击子窗口单独弹出或拖出及拽回的处理

    在基于DevExpress的多文档窗口界面中,我们一般使用XtraTabbedMdiManager来管理多文档窗口的一些特性,如顶部菜单,页面的关闭按钮处理,以及一些特殊的设置,本篇随笔介绍这些特点, ...

  8. Qt多文档界面应用设计

    使用Qt编写多文档界面(MDI)应用相当方便,主要会使用到QMdiArea和QMdiSubWindow两个类.可以查看Qt Asistant中这两个类的说明文档,里面介绍的相当详细.另外,可以搜索例程 ...

  9. 多文档界面的实现(DotNetBar的superTabControl)

    private void FormMain_Load(object sender, EventArgs e) { superTabControl2.Tabs.Clear(); timer1.Start ...

随机推荐

  1. Final Cut Pro X效果插件开发总结

    一.介绍 最近公司需要针对Final Cut Pro(FCP)开发一款效果插件,用于对公司自己开发的视频格式进行后期处理.Final Cut Pro是苹果公司推出的一款视频剪辑软件,因此需要在OSX平 ...

  2. QRCode

    这个星期, 领导要我总结项目中用到的一些技术, 然后交付文档. 嘿嘿, 奉命整理. 二维码, 相信很多项目中都会要求生成这个, 然后由手机端去扫描, 或存储一些详情信息, 或存储一条链接, 可以快捷访 ...

  3. java函数

    函数的封装没有定规,只要遵循语法,函数如何封装按照需求来做 函数四要素:函数名,输入,加工,输出(返回). 一.函数调用 1.函数名(变量列表); 没有返回值. 2.数据类型 变量名=函数名(变量列表 ...

  4. html中<radio>单选按钮控件标签用法解析及如何设置默认选中

    <input type="radio" name="radio" value="1">单选1 <input type=&q ...

  5. Python默认版本修改

    Python默认版本修改 当电脑安装了多个版本的Python,而Shell中默认的Python不是你想要的,这个时候就需要对Python的默认版本进行修改. 在Windows中,可以通过修改环境变量的 ...

  6. AJAX与PHP(PHP笔记)--动态验证用户名

    在PHP基础的学习过程中经常会遇到对页面的局部刷新. 比如说,我们在填写用户名的同时,对数据库中的信息进行验证,检查是否充分. 这时就要用到AJAX实现页面的动态加载. 下面例子是简单的PHP与AJA ...

  7. PHP中抽象类,接口定义

    这里先介绍接口,因为在我最近看的好几本php工具书中都没有提到抽象类. 本人也觉得,在理解了接口后抽象类也非常好理解. 例子代码随便写了一下.例子代码是很ok的,测试过了不会报错,懒得看代码的筒靴们看 ...

  8. eclipse js 报错解决办法

    最近项目中的js一直红叉叉,本人本来就是那种看见回收站有东西想清理的那种强迫症患者, 所以对于这种红叉叉更是心烦.这个我已经忍了好久了.今天 见这哥们犀利的解决了这个eclipes的bug.很是激动, ...

  9. js鼠标滚轮滚动图片切换效果

    效果体验网址:http://keleyi.com/keleyi/phtml/image/12.htm HTML文件代码: <!DOCTYPE html PUBLIC "-//W3C// ...

  10. 使用C#类向数据库添加数据的例子源码

    在上一篇中,增加了sql server数据库操作类SqlOperator,用于操作sql server数据库.还有一个SqlStringHelper类,用于处理sql语句的单引号.那么这两个类怎么使用 ...