汇编教程多文档界面MDI.docx
- 文档编号:6893555
- 上传时间:2023-01-12
- 格式:DOCX
- 页数:13
- 大小:22.30KB
汇编教程多文档界面MDI.docx
《汇编教程多文档界面MDI.docx》由会员分享,可在线阅读,更多相关《汇编教程多文档界面MDI.docx(13页珍藏版)》请在冰豆网上搜索。
汇编教程多文档界面MDI
本教程告诉你怎样创建MDI应用程序.事实上并不是很困难.
理论:
多文档界面(MDI)是同一时刻处理多个文档的应用程序的一个规范.你很熟悉记事本.它是单文档界面(SDI)的一个例子.记事本在一个时候只能处理一个文档.假如你希望打开另一个文档,你首先必须关闭你前面打开的那一个.你可以想象这有多麻烦.和MicrosoftWord相比:
Word可以随心所欲的在同一时刻打开任意多个文档,而且可以让用户选择使用哪一个文档.MicrosoftWord是多文档界面(MDI)的一个例子.
MDI应用程序有几个显著的特征:
我列举其中的一些:
有主窗口,在客户区可以有多个子窗口.所有的子窗口都位于客户区.
最小化一个子窗口,它最小化到了主窗口的客户区的左下角.
最大化一个子窗口,它的标题和主窗口的标题合并到了一起.
你可以通过按Ctrl+F4键来关闭子窗口,还可以通过按Ctrl+Tab键在子窗口之间来切换.
包含子窗口的主窗口被称为框架窗口.主窗口的客户区是子窗口活动的地方,因此有了'框架'这个名字.主窗口的任务要比普通窗口精细一些,因为它需要为MDI处理一些协调工作.
为了在你的客户区控制任意多个数目的子窗口,你需要一个特殊的窗口:
客户窗口.你可以把客户窗口看成是覆盖框架窗口的整个客户区的一个透明的窗口.客户窗口才是所有MDI子窗口的实际的父亲.客户窗口是MDI子窗口的真实的监督者.
框架窗口|客户窗口|--------------------------------------------------------------------------------|| | | |
MDI自窗口1
MDI自窗口2
MDI自窗口3
MDI自窗口4
MDI自窗口n
图1.一个MDI应用程序的层次结构
创建框架窗口
现在我们将注意力放到细节上来.首先你需要创建框架窗口.创建框架窗口的方法和普通窗口是相同的:
调用CreateWindowEx.和普通窗口相比,有两个主要的不同.
第一个不同是你必须调用DefFrameProc来处理你的窗口不想处理的窗口信息而不是调用DefWindowProc.这是让Windows为你作的保持一个MDI应用程序的垃圾任务的一个方法.假如你忘记使用DefFrameProc,你的应用程序将不具有MDI的功能.DefFrameProc具有下列语法:
DefFrameProcprochwndFrame:
DWORD,
hwndClient:
DWORD,
uMsg:
DWORD,
wParam:
DWORD,
lParam:
DWORD
假如你将DefFrameProc和DefWindowProc作一个对比,你将会注意到它们之间的唯一的不同在于DefFrameProc有5个参数,而DefWindowProc只有4个.这个增加的参数是客户窗口的句柄.这个句柄是必须的,有了它Windows才可以发送MDI-相关的消息给客户窗口.
第二个不同是你必须在你的框架窗口的消息循环中调用TranslateMDISysAccel.假如你希望Windows为你处理MDI相关的加速键,比如Ctrl+F4,Ctrl+Tab,那么这是必须的.它具有下列语法:
TranslateMDISysAccelprochwndClient:
DWORD,
lpMsg:
DWORD
第一个参数是客户窗口的句柄.对此你应该不会觉得惊讶.因为客户窗口是所有MDI子窗口的父亲.第二个参数是你通过调用GetMessage获得的MSG框架的地址.我们的想法是传递MSG结构给客户窗口,这样客户窗口可以检测在MSG结构中所包含的MDI相关的按键是不是按下去了.假如是的话,客户窗口处理这个信息,然后返回一个非零值,否则返回一个假值..
创建框架窗口的步骤总结如下:
像平常一样填写WNDCLASSEX结构.
通过调用RegisterClassEx注册框架窗口类.
通过调用CreateWindowEx创建框架窗口.
在消息循环中调用TranslateMDISysAccel.
在窗口过程中,将未处理的消息传递给DefFrameProc而不是DefWindowProc.
创建客户窗口
现在我们有了框架窗口,我们可以开始创建客户窗口了.客户窗口类是由Windows预先注册的.类的名称为"MDICLIENT".你同样也需要将CLIENTCREATESTRUCT的地址传递给CreateWindowEx.这个结构具有以下定义:
CLIENTCREATESTRUCTstruct
hWindowMenudd?
idFirstChilddd?
CLIENTCREATESTRUCTends
hWindowMenu是子菜单的句柄,这个子菜单显示Windows将要添加的MDI子窗口名称列表.我们需要对这一功能进行一点解释.假如你以前曾经使用过类似MicrosoftWord的MDI应用程序,你将会注意到有一个名称为"窗口"的子菜单.这个菜单一旦激活的话,将会在底部显示出和窗口管理相关的各种各样的菜单项,还有当前打开的MDI子窗口的列表.这个列表是由Windows自己内部保持的.你不需要为它作任何特殊的事情.仅仅只在需要在hWindowMenu中传递你所希望显示列表的子菜单的句柄,Windows将会处理剩下的事情.注意这个子菜单可以是任何的子菜单:
它并不一定要是名称为"窗口"的子菜单.重要的是你应该传递你希望显示窗口列表的子菜单的句柄.假如你不想要这个列表,你就给hWindowMenu赋一个NULL的值就行了.你可以通过调用GetSubMenu来获得子菜单的句柄.
idFirstChild是第一个MDI子窗口的标识号.Windows为应用程序所创建的每一个新的MDI子窗口相应的增加标识号.举个例子,假如你传递100给这个域,第一个MDI子窗口将会有一个值为100的标识符,那么第二个MDI子窗口也就会有一个值为101的标识符,如此这样下去.当从窗口列表中选择MDI子窗口时,被选择的MDI子窗口的标识符通过WM_COMMAND传递给框架窗口.正常情况下,你将传递"未处理"的WM_COMMAND消息给DefFrameProc.我用"未处理"这个词语,是因为窗口列表中的菜单项不是由你的应用程序创建的,这样你的应用程序不知道它们的标识符,而且也没有它们的句柄.这是MDI框架窗口又一个特殊的地方.假如你有窗口列表的话,你必须像这样来修改你的WM_COMMAND句柄:
.elseifuMsg==WM_COMMAND
.iflParam==0;这条消息是由菜单产生的
moveax,wParam
.ifax==IDM_CASCADE
.....
.elseifax==IDM_TILEVERT
.....
.else
invokeDefFrameProc,hwndFrame,hwndClient,uMsg,wParam,lParam
ret
.endif
一般来说,你可以忽略未处理的消息.但是在MDI的情况下,如果你忽略它们,当用户点击窗口列表中的一个MDI子窗口的名称时,,这个窗口不会被激活.你需要将这些消息传递给DefFrameProc这样它们才会得到适当的处理.
idFirstChild赋值的注意之处:
你不能使用0.你窗口列表将会表现的不正常.举个例子,即使某一个MDI子窗口被激活的话,窗口列表中的这个MDI子窗口名字前的复选标记也不会显现.我们可以选择一个安全的值,比如100或是一个比100大的值.
给CLIENTCREATESTRUCT结构赋值后,你可以通过调用CreateWindowEx用预先注册好的类名"MDICLIENT",在lParam中传递CLIENTCREATESTRUCT结构的地址来创建客户窗口.你同样需要在hWndParent参数中指定框架窗口的句柄,这样Windows才可以知道框架窗口和客户窗口之间的父-子关系.你可以使用的窗口风格有:
WS_CHILD,WS_VISIBLEHEWS_CLIPCHILDREN.假如你忘了WS_VISIBLE的话,即使MDI子窗口成功地创建了,你也看不到它们.
以下是创建客户窗口的步骤:
获取你所希望显示窗口列表的子菜单的句柄.
将这个菜单句柄的值和你希望作为第一个MDI子窗口标识符的值一起传送给CLIENTCREATESTRUCT结构.
调用CreateWindowEx用类名"MDICLIENT",lParam参数为CLIENTCREATESTRUCT结构的地址,
创建MDI子窗口
现在我们既有了框架窗口,也有了客户窗口.下一阶段可以开始创建MDI子窗口了.有两种方法:
你可以发送WM_MDICREATE消息给客户窗口,在wParam参数中传递类型MDICREATESTRUCT的结构的地址.这是常用的也是最简单的MDI子窗口的创建方法.
.data?
mdicreateMDICREATESTRUCT<>
....
.code
.....
[fillthemembersofmdicreate]
......
invokeSendMessage,hwndClient,WM_MDICREATE,addrmdicreate,0
假如创建成功的话,SendMessage将会返回新创建的MDI子窗口的句柄.你并不需要保存这个句柄.如果你需要的话,你可以通过其它的方法来获得它.MDICREATESTRUCT有如下定义.
MDICREATESTRUCTSTRUCT
szClassDWORD?
szTitleDWORD?
hOwnerDWORD?
xDWORD?
yDWORD?
lxDWORD?
lyDWORD?
styleDWORD?
lParamDWORD?
MDICREATESTRUCTENDS
szClass你作为MDI自窗口模板的窗口类的地址
szTitle你希望出现在子窗口的标题栏的文本的地址
hOwner应用程序的例程句柄
x,y,lx,ly子窗口的左上角的坐标以及宽度和高度
style子窗口风格.假若你用MDIS_ALLCHILDSTYLES创建子窗口的话,你可以使用任何窗口风格.
lParam一个应用程序定义的32位值.这是在MDI窗口中共享值的一种方法.如果你不需要它,将它设为NULL.
你可以调用CreateMDIWindow.这一个功能具有下列语法:
CreateMDIWindowprotolpClassName:
DWORD
lpWindowName:
DWORD
dwStyle:
DWORD
x:
DWORD
y:
DWORD
nWidth:
DWORD
nHeight:
DWORD
hWndParent:
DWORD
hInstance:
DWORD
lParam:
DWORD
如果你仔细地看一下这些参数,你将会发现它们和MDICREATESTRUCT结构的成员是相同的,除了hWndParent.以外.本质上它和你用WM_MDICREATE传送的参数数目是相同的.MDICREATESTRUCT不需要hWndParent域,因为你必须用Sendmessage传送整个结构给正确的子窗口..
在这里,你也许会有一些问题:
我应该使用哪一种方法?
在这两者之间有什么区别?
答案如下:
WM_MDICREATE方法创建的MDI子窗口作为调用代码是同一个线程.这意味这假如这个应用程序只有一个主线程的话,所有的MDI子窗口都在这个主线程中运行.这并不是一个大的问题.但是如果一个或是多个你的MDI子窗口执行一些较长的操作的话,问题就来了.想象一下你的整个的应用程序突然之间停止了,对任何事情都没有反应,一直持续到MDI子窗口的操作结束.
这个问题正是CreateMDIWindow设计了所要解决的.CreateMDIWindow为每一个MDI子窗口创建了一个单独的线程.这样假如一个MDI子窗口忙的话,它不会拖累整个应用程序..
有关MDI子窗口的窗口过程还有一点需要说明的地方.对于框架窗口,你不能调用DefWindowProc来处理未处理的消息.与之相反你必须在自窗口的窗口过程中使用DefMDIChildProc.这个函数具有和DefWindowProc相同的参数.
除了WM_MDICREATE以外,还有其它的MDI相关的窗口消息.列表如下:
WM_MDIACTIVATE这条消息由应用程序发送给客户窗口,告诉客户窗口激活所选择的MDI子窗口.当客户窗口受到消息后,它将激活所选择的MDI子窗口和发送WM_MDIACTIVATE消息给将被激活的子窗口和将变为非活动窗口的子窗口.这条消息的用途是双方面的:
应用程序可以用它来激活所希望的子窗口.同时它又可以被MDI子窗口本身用作活动/非活动窗口的指示器.举个例子,假如每一个MDI子窗口都有不同的菜单,那么当它变为活动或是非活动窗口的时候,它可以利用这个机会来改变框架窗口的菜单
WM_MDICASCADE
WM_MDITILE
WM_MDIICONARRANGE这些消息处理如何排列MDI子窗口.举个例子,假如你希望MDI子窗口排列成层叠的样式,发送WM_MDICASCADE消息给客户窗口.本文来自编程入门网---
WM_MDIDESTROY发送这条消息给客户窗口来关闭一个MDI子窗口.你应该使用这条消息而不是调用DestroyWindow因为假如这个MDI子窗口最大化的话,th这条消息将会恢复框架窗口的标题.假如你使用DestroyWindow,框架窗口的标题将不会被恢复.
WM_MDIGETACTIVE发送这条消息检索当前活动MDI子窗口的句柄.
WM_MDIMAXIMIZE
WM_MDIRESTORE发送WM_MDIMAXIMIZE来最大化MDI子窗口和WM_MDIRESTORE来将它恢复成以前的状态.对于这些操作总是使用这些消息.假如你使用参数为SW_MAXIMIZE来调用ShowWindow时,MDI子窗口最大化并没有问题,但是当你试图将它恢复成以前的状态时,问题就来了.但是你可以用调用ShowWindow来最小化MDI子窗口.
WM_MDINEXT发送这条消息给客户窗口,根据wParam和lParam里相应的值来激活下一个或是前一个MDI子窗口.
WM_MDIREFRESHMENU发送这条消息给客户窗口来刷新框架窗口的菜单.注意在发送了这条消息之后,你必须调用DrawMenuBar来更新菜单条.
WM_MDISETMENU发送这条消息给客户窗口来取代框架窗口的整个菜单或是窗口子菜单.你必须使用这条消息而不是用SetMenu.在发送了这条消息之后,你必须调用DrawMenuBar来更新菜单条.正常情况下当活动的MDI子窗口有它自己的菜单而且你希望用这个活动的子窗口自身的菜单来取代框架窗口的菜单时,你将使用这条消息.
我们将创建一个MDI应用程序的步骤回顾一遍.
注册窗口类,既有框架窗口类也有MDI子窗口类.
调用CreateWindowEx创建框架窗口.
在消息循环中调用TranslateMDISysAccel来处理MDI相关的加速键.
在框架窗口的窗口过程中,调用DefFrameProc处理所有你的代码没有处理的消息.
用预选定义好的窗口类名"MDICLIENT"调用CreateWindowEx来创建客户窗口,在lParam参数中传递CLIENTCREATESTRUCT结构的地址.正常情况下,你可以用框架窗口过程中的WM_CREATE句柄来创建一个客户窗口.
相应的要创建MDI子窗口,你可以通过调用CreateMDIWindow来发送WM_MDICREATE消息给客户窗口.
在MDI子窗口的窗口过程中,我们把所有未处理的消息发送给传递给DefMDIChildProc.
假如某一条消息有它的MDI的版本,那我们就使用它的MDI版本.举个例子,我们使用WM_MDIDESTORY消息,而不是使用DestroyWindow来关闭一个MDI子窗口.
例子:
.386
.modelflat,stdcall
optioncasemap:
none
include\masm32\include\windows.inc
include\masm32\include\user32.inc
include\masm32\include\kernel32.inc
includelib\masm32\lib\user32.lib
includelib\masm32\lib\kernel32.lib
WinMainproto:
DWORD,:
DWORD,:
DWORD,:
DWORD
.const
IDR_MAINMENUequ101
IDR_CHILDMENUequ102
IDM_EXITequ40001
IDM_TILEHORZequ40002
IDM_TILEVERTequ40003
IDM_CASCADEequ40004
IDM_NEWequ40005
IDM_CLOSEequ40006
.data
ClassNamedb"MDIASMClass",0
MDIClientNamedb"MDICLIENT",0
MDIChildClassNamedb"Win32asmMDIChild",0
MDIChildTitledb"MDIChild",0
AppNamedb"Win32asmMDIDemo",0
ClosePromptMessagedb"Areyousureyouwanttoclosethiswindow?
",0
.data?
hInstancedd?
hMainMenudd?
hwndClientdd?
hChildMenudd?
mdicreateMDICREATESTRUCT<>
hwndFramedd?
.code
start:
invokeGetModuleHandle,NULL
movhInstance,eax
invokeWinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT
invokeExitProcess,eax
WinMainprochInst:
HINSTANCE,hPrevInst:
HINSTANCE,CmdLine:
LPSTR,CmdShow:
DWORD
LOCALwc:
WNDCLASSEX
LOCALmsg:
MSG
;=============================================
;注册框架窗口类
;=============================================
movwc.cbSize,SIZEOFWNDCLASSEX
movwc.style,CS_HREDRAWorCS_VREDRAW
movwc.lpfnWndProc,OFFSETWndProc
movwc.cbClsExtra,NULL
movwc.cbWndExtra,NULL
pushhInstance
popwc.hInstance
movwc.hbrBackground,COLOR_APPWORKSPACE
movwc.lpszMenuName,IDR_MAINMENU
movwc.lpszClassName,OFFSETClassName
invokeLoadIcon,NULL,IDI_APPLICATION
movwc.hIcon,eax
movwc.hIconSm,eax
invokeLoadCursor,NULL,IDC_ARROW
movwc.hCursor,eax
invokeRegisterClassEx,addrwc
;================================================
;注册MDI子窗口类
;================================================
movwc.lpfnWndProc,offsetChildProc
movwc.hbrBackground,COLOR_WINDOW+1
movwc.lpszClassName,offsetMDIChildClassName
invokeRegisterClassEx,addrwc
invokeCreateWindowEx,NULL,ADDRClassName,ADDRAppName,\
WS_OVERLAPPEDWINDOWorWS_CLIPCHILDREN,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,0,\
hInst,NULL
movhwndFrame,eax
invokeLoadMenu,hInstance,IDR_CHILDMENU
movhChildMenu,eax
invokeShowWindow,hwndFrame,SW_SHOWNORMAL
invokeUpdateWindow,hwndFrame
.whileTRUE
invokeGetMessage,ADDRmsg,NULL,0,0
.break.if(!
eax)
invokeTranslateMDISysAccel,hwndClient,addrmsg
.if!
eax
invokeTranslateMessage,ADDRmsg
invokeDispatchMessage,ADDRmsg
.endif
.endw
invokeDestroyMenu,hChildMenu
moveax,msg.wParam
ret
WinMainendp
WndProcprochWnd:
HWND,uMsg:
UINT,wParam:
WPARAM,lParam:
LPARAM
LOCALClientStruct:
CLIENTCREATESTRUCT
.ifuMsg==WM_CREATE
invokeGetMenu,hWnd
movhMainMenu,eax
invokeGetSubMenu,hMainMenu,1
movClientStruct.hWindowMenu,eax
movClientStruct.idFirstChild,100
INVOKECreateWindowEx,NULL,ADDRMDIClientName,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 汇编 教程 文档 界面 MDI