【Windows编程】创建多文档界面

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

多文档界面框架创建过程需要以下几步:

  • 主框架窗口创建

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

  • 客户区窗口创建

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

  • 视图窗口创建

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

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

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

限于内容浏览限制,这里只贴出关键部分代码,查看完整的代码请猛戳左下角的“阅读原文”。

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

ghWndMainFrame = CreateWindow(szMDIMainFrame, gszAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, // allows system choose an x position CW_USEDEFAULT, // allows system choose a y position 400, 300, NULL, ghMainFrameMenu, hInstance, NULL);

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, 0, 0, 0, 0, hWnd, NULL, g_hInst, (void *)&ccs); #ifdef CLIENT_WND_BGROUND pfOrigClientWndProc = (WNDPROC)SetWindowLong(ghWndMDIClient, GWL_WNDPROC, (LONG)ClientWndSubClassProc); #endif } return 0; case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_FILE_NEW: CreateDocView(ghWndMDIClient, g_hInst); return 0; } break; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefFrameProc(hWnd, ghWndMDIClient, message, wParam, lParam); }

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, 0); 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 0 ; } case WM_CLOSE: delete g_pSystemInfo; break; case WM_DESTROY: return 0; } 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 = 0; wcDoc.cbWndExtra = 0; 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 0; } } m_hWndView = CreateWindowEx(WS_EX_MDICHILD, szViewWndName, TEXT("New view"), 0, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, 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一文。

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

原文发布于微信公众号 - 程序员互动联盟(coder_online)

原文发表时间:2015-09-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

React Native基础&入门教程:以一个To Do List小例子,看props和state

在上篇中,我们介绍了什么是Flexbox布局,以及如何使用Flexbox布局。还没有看过的小伙伴欢迎回到文章列表点击查看之前的文章了解。

643
来自专栏梧雨北辰的开发录

DTCoreText的集成与使用目录一、相关资源二、DTCoreText的集成三、DTCoreText的使用四、可能遇到的错误五、参考链接

DTCoreText是可以将HTML字符串转化为富文本使用的工具,既保证原生实现又能适应灵活的样式修改,而且相比于使用WebView显示内容在性能上也有很大优势...

4258
来自专栏hrscy

iOS常见问题总结(二)

在导入框架libxml2.2.dylib后, 最到了XCode仍然找不到<libxml/tree.h>的情况, 最后解决过程如下: 1 )项目 -Targ...

712
来自专栏编程微刊

前端js实现打印(导出)excel表格

1282
来自专栏林冠宏的技术文章

js 调用百度地图,并且定位用户地址,显示省市区街,经纬度

网上的一些百度地图例子,基本上没有连套的 定位 例子。下面我分享一套我自己弄的,废话不多说,看代码,里面有注释! 1 <!DOCTYPE html> 2...

1859
来自专栏贺嘉的专栏

如何用Baas快速在腾讯云上开发小程序之系列4:实现客户侧商品列表、商品详情页程序

本文将分享如何在腾讯云上实现小程序的商品展示和数据库查询操作。通过实现商品列表、商品详情页程序,熟练掌握云端数据表查询操作,包括掌握小程序调试方法、掌握小程序操...

7760
来自专栏何俊林

Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑

前言:关于《TV Metro界面(仿泰捷视频TV版)源码解析》由于都是相关代码,就不发公众号了,有兴趣的可以看链接:http://blog.csdn.net/h...

22110
来自专栏更流畅、简洁的软件开发方式

【开源】QuickPager ASP.NET2.0分页控件V2.0.0.3 【增加了使用说明】

最新版本:V2.0.0.7 。http://www.cnblogs.com/jyk/archive/2008/07/28/1255101.html 下载:ht...

21910
来自专栏木宛城主

ASP.NET MVC使用Bootstrap系列(3)——使用Bootstrap 组件

Bootstrap为我们提供了十几种的可复用组件,包括字体图标、下拉菜单、导航、警告框、弹出框、输入框组等。在你的Web Application中使用这些组件...

24710
来自专栏林德熙的博客

win10 uwp 按下等待按钮

我们需要一个值让我们知道是不是已经完成了后台,按钮可以按下,在按下时,自动让按钮IsEnable为false。

322

扫描关注云+社区