前面我们所举的例子中都是单文档界面框架,也就是说这个窗口里面的客户区就是一个文档界面,可以编写程序在里面输入或者绘制文本图形输出,但是不能有出现多个文档的情况。比如下面的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. 表单多文件上传样式美化 && 支持选中文件后删除相关项

    开发中会经常涉及到文件上传的需求,根据业务不同的需求,有不同的文件上传情况. 有简单的单文件上传,有多文件上传,因浏览器原生的文件上传样式及功能的支持度不算太高,很多时候我们会对样式进行美化,对功能进 ...

  2. CSS基础转载

    css基本知识框架:(一:基本知识缩影.二基本知识框架图) 1.css样式表的基本概念 2.样式表基本类型-----1.内嵌样式 2.内联样式3.链入外部样式表4.导入外部?式 3.样式表配置方法 4 ...

  3. linux使用心得(持续更新)

    ! 查看发行版本信息 lsb_release -a uname -a   以下方法只适合redhat和centos cat /etc/redhat-release rpm -q redhat-rele ...

  4. C++面试题汇集

    1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?答:首先,extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以 ...

  5. [WCF编程]12.事务:服务事务编程(上)

    一.设置环境事务 默认情况下,服务类和操作没有环境事务,即使客户端事务传播到服务端也是如此. 尽管强制事务流从客户端传播过来,但服务端的环境事务依旧为null.为了启用环境事务,每个操作必须告诉WCF ...

  6. C# Socket 模拟http服务器帮助类

    0x01 写在前面 0x02 Http协议 0x03 TCP/IP 0x04 看代码 0x05 总结 0x01 写在前面 由于工作中,经常需要在服务器之间,或者进程之间进行通信,分配任务等.用Sock ...

  7. 背水一战 Windows 10 (19) - 绑定: TemplateBinding 绑定, 与 RelativeSource 绑定, 与 StaticResource 绑定

    [源码下载] 背水一战 Windows 10 (19) - 绑定: TemplateBinding 绑定, 与 RelativeSource 绑定, 与 StaticResource 绑定 作者:we ...

  8. eclipse中egit插件使用

    这篇文章当时制作有点粗糙,建议阅读升级版:eclipse中egit插件使用--升级版 使用git作为项目的代码管理工具现在是越来越火,网上有各种各样的文章.博客.讨论,其中以命令行居多.使用eclip ...

  9. jQuery中的事件处理

    1.页面载入完毕响应事件所谓页面载入完毕是指DOM元素载入就绪了,能够被读取和操作了.①jQuery中的$(doucument).ready()事件ready(fn)是jQuery事件模块中最重要的一 ...

  10. CSS背景图拉伸不变形

    在线效果体验:http://hovertree.com/texiao/mobile/3.htm 请使用手机浏览器查看. css代码: .bg{ background:url(http://hovert ...