Windows编程之MDI

MDI 创建

1 CilentWindow

1.1 CLIENTCREATESTRUCT

typedefstruct tagCLIENTCREATESTRUCT {

    HANDLE hWindowMenu;

    UINT  idFirstChild;

} CLIENTCREATESTRUCT, *LPCLIENTCREATESTRUCT;

<1>hWindowMenu  多文档应用窗口程序的菜单

<2>idFirstChild  指定第一个创建的MDI子窗口的ID,其后创建的MDI子窗口ID会在此基础上增加,并且随着窗口的销毁会重新分配ID。它们主要用在MDI子窗口向MDIFrame窗口中发送消息。

1.2 ClientWindow的创建

case  WM_CREATE:        // Create the client window

         clientcreate.hWindowMenu      = hMenuInitWindow ;

         clientcreate.idFirstChild         = IDM_FIRSTCHILD ;

         hwndClient = CreateWindow( TEXT("MDICLIENT"), NULL, WS_CHILD |WS_CLIPCHILDREN |                                                     WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1,hInst, (PSTR)&clientcreate) ;

         return 0 ;

 

2 MDI Window

2.1 MDICREATESTRUCT

typedef struct tagMDICREATESTRUCT {

    LPCTSTR szClass;

    LPCTSTR szTitle;

    HANDLE  hOwner;

    int     x;

    int     y;

    int     cx;

    int     cy;

    DWORD   style;

    LPARAM  lParam;

} MDICREATESTRUCT,*LPMDICREATESTRUCT;

<1> hOwner 指创建MDI Client 窗口的hInstance

2.2 MDI child 窗口的创建

创建一个MDI child 窗口有三种方式

<1>通过向Client窗口发送WM_MDICREATE

case  IDM_FILE_NEWHELLO:  // Create a Hello child window

         mdicreate.szClass     = szHelloClass;

         mdicreate.szTitle       = TEXT ("Hello");

         mdicreate.hOwner    = hInst ;

         mdicreate.x         = CW_USEDEFAULT ;

         mdicreate.y         = CW_USEDEFAULT ;

         mdicreate.cx        = CW_USEDEFAULT ;

         mdicreate.cy        = CW_USEDEFAULT ;

         mdicreate.style     = 0 ;

         mdicreate.lParam    = 0 ;

         hwndChild =(HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM)                                                                       (LPMDICREATESTRUCT) &mdicreate) ;

         return0 ;

 

<2>通过函数CreateMDIWindow

hwndChild=CreateMDIWindow(szHelloClass,TEXT("Hello"),0,CW_USEDEFAULT,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hwndClient,hInst,NULL);

 

<3>通过函数CreateWindowEx

hwndChild =CreateWindowEx(WS_EX_MDICHILD,szHelloClass,TEXT ("Hello"),0,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,         ,hwndClient,hMenuInitWindow,hInst,NULL);

3 窗口消息处理

4 WM_MDIACTIVATE

发送方:

wParam

A handle to the MDI child window to beactivated.

lParam

This parameter is not used.

接受方:

wParam

A handle to the MDI child window beingdeactivated.

lParam

A handle to the MDI child window beingactivated.

通过跟踪消息可以发现在MDI child 窗口获得焦点以及失去焦点的时候,都会受到WM_MDIACTIVATE的消息。此时的lParam = NULL。

 

当子窗口获取焦点的时候,Client  Window 会受到WM_GETACTIVE的消息。

 

5 WM_QUERYENDSESSION

当用户选择结束会话(session)的或者应用程序调用系统关闭函数的时候,就会发送WM_QUERYSESSION消息。当应用程序返回0 ,会话不会被终止。

wParam

This parameter is reserved for futureuse.

lParam

This parameter can be one or more of thefollowing values. If this parameter is 0, the system is shutting down orrestarting (it is not possible to determine which event is occurring).

<1>用户注销的方法

ExitWindows(0, 0);

或者

ExitWindowsEx(EWX_LOGOFF, 0);

这个时候一般会出现提示对话框

case  WM_QUERYENDSESSION:

         nOff =      MessageBox(NULL,(LPCWSTR)L"Endsession?",(LPCWSTR)L"WM_QUERYENDSESSION",

         MB_YESNO);

         // ReturnTRUE to continue, FALSE to stop.

         returnnOff == IDYES;

         break;

<2> 用户重启

ExitWindowsEx(EWX_REBOOT,0);

<3> 用户关机

ExitWindowsEx(EWX_SHUTDOWN,0); 

 

6 MDI窗口类别

一个MDI窗口应用程序包含三种窗口,Frame Window,Client Window ,Child Window。

Frame Window: 类似主窗口,它包含标题栏,菜单栏,边框,最小最大化按钮。

MDI Client Window: 属于已经注册好的窗口类,MDICLIENT,它是FrameWindow的子窗口,也是Child Window 的背景,对子窗口的操作也是通过Client Window 实现的。

Child Window :它是Client Window的子窗口。每个子窗口都有一个边框,标题栏,系统菜单,最小最大化按钮。Child Window 不能被拖到Client Window的外面。

Windows编程之MDI_第1张图片


7 MDI APP的菜单

 

MDI应用程序的菜单是属于FrameWindow的。即便通过

SendMessage(hwndClient, WM_MDISETMENU,(WPARAM)hMenuHello, (LPARAM) hMenuHelloWindow) ;

这样的Client Window 设置的菜单,它就是属于Frame Window的,所以在设计MDI的菜单的时候要注意:

<1> 应该设计多个菜单,Frame Window初始化菜单,各个子窗口打开时的专属菜单。

<2> 各个子窗口都要用到的菜单命令处理应该放在FrameWindow的窗口过程函数中,分属于子窗口独自的就放在子窗口对应的窗口过程函数中。

<3> Frame Window的菜单项中应该只包含新文档窗口的创建,以及整个APP 的关闭的菜单项。

7.2 关于Window Menu:

MSDN上的解释是:

The frame window of an MDI applicationshould include a menu bar with a window menu. The window menu should includeitems that arrange the child windows within the client window or that close allchild windows.

翻译:多文档应用程序的框架窗口应该包含一个带有窗口菜单的菜单栏。窗口才到那应该包含组织客户区窗口内子窗口的菜单项,或者关闭所有的子窗口。

可见:Window Menu指的是主要处理MDI多个子窗口之间排列组合以及关闭的菜单,它并不是什么特别的专业术语。

那么Window Menu 起初是怎么指定的呢?

我们要重新回到CLIENTCREATESTRUCT结构体中,

typedef struct tagCLIENTCREATESTRUCT {
    HANDLE hWindowMenu; 
    UINT   idFirstChild; 

} CLIENTCREATESTRUCT,*LPCLIENTCREATESTRUCT;

其中 hWindowMenu的解释是:

Handle to the MDI application's windowmenu. An MDI application can retrieve this handle from the menu of the MDIframe window by using the GetSubMenufunction.

讲到这里,一切都清晰明了很多了。

7.3 关于 WM_MDISETMENU

这个子窗口发给Client窗口让其修改Frame Window的整个菜单或者 Window Menu的消息。

SendMessage (hwndClient,WM_MDISETMENU,(WPARAM)hMenuHello, (LPARAM)hMenuHelloWindow) ;

其中的wParam参数代表修改FrameWindow的 整个菜单,lParam参数代表修改Frame Window的WindowMenu。

hMenuHello菜单如下所示:

Windows编程之MDI_第2张图片

现在我们进行测试,观察不同参数下的不同效果:

<1> lParam 参数为NULL,wParam 不为NULL的时候:

我们观察到的3个弹出菜单:

Windows编程之MDI_第3张图片 Windows编程之MDI_第4张图片 Windows编程之MDI_第5张图片

<2> 两个参数都不为空的时候

并且令hMenuHelloWindow    = GetSubMenu(hMenuHello,  2) ;

得到的结果如下所示:

Windows编程之MDI_第6张图片

令hMenuHelloWindow   = GetSubMenu(hMenuHello,  1) ;

Windows编程之MDI_第7张图片

<3> 当wParam为空,lParam不为空的时候

没有打开新的文档的时候,Frame Window的界面如下所示:

Windows编程之MDI_第8张图片Windows编程之MDI_第9张图片

SendMessage(hwndClient,WM_MDISETMENU,NULL, (LPARAM) hMenuHelloWindow) ;

利用上面语句改变Frame Window的菜单后,如下所示:

Windows编程之MDI_第10张图片


Windows编程之MDI_第11张图片

当我们改变初始菜单中 Help 和File的顺序的时候:

Windows编程之MDI_第12张图片 Windows编程之MDI_第13张图片

<4> wParam ,lParam参数都为NULL的时候:

Window Menu 会被变成NULL,即便此后重新设置也无法改变。

 

总结:

1 创建MDI窗口程序,首先要创建一个FrameWindow,然后在WM_CREATE消息中创建Client Window。

2 在WM_COMMAND命令中创建 MDI Child Window。

3 在Frame Window的窗口过程函数中,处理各个子窗口的共同命令消息,对于某个窗口的个例消息,交给此窗口的过程函数进行处理。

4 在产生子窗口的时候,通常要改变主窗口的菜单。

程序下载地址: MDI源码


你可能感兴趣的:(windows编程)