关于多文档界面

多文档界面 (MDI) 应用程序中的每个文档都显示在应用程序main窗口的工作区内的单独子窗口中。 典型的 MDI 应用程序包括允许用户处理多个文本文档的字处理应用程序,以及允许用户使用多个图表和电子表格的电子表格应用程序。 有关详细信息,请参阅以下主题。

框架、客户端和子窗口

MDI 应用程序有三种类型的窗口:框架窗口、MDI 客户端窗口以及许多子窗口。 框架窗口类似于应用程序的main窗口:它具有调整大小边框、标题栏、窗口菜单、最小化按钮和最大化按钮。 应用程序必须为框架窗口注册一个窗口类,并提供一个窗口过程来支持它。

MDI 应用程序不会在框架窗口的工作区中显示输出。 而是显示 MDI 客户端窗口。 MDI 客户端窗口是属于预注册窗口类 MDICLIENT 的特殊类型的子窗口。 客户端窗口是框架窗口的子窗口;它用作子窗口的背景。 它还支持创建和操作子窗口。 例如,MDI 应用程序可以通过向 MDI 客户端窗口发送消息来创建、激活或最大化子窗口。

当用户打开或创建文档时,客户端窗口将创建文档的子窗口。 客户端窗口是应用程序中所有 MDI 子窗口的父窗口。 每个子窗口都有一个大小调整边框、一个标题栏、一个窗口菜单、一个最小化按钮和一个最大化按钮。 由于子窗口已剪裁,因此它仅限于客户端窗口,并且不能显示在其外部。

MDI 应用程序可以支持多种类型的文档。 例如,典型的电子表格应用程序使用户能够同时处理图表和电子表格。 对于它支持的每种文档类型,MDI 应用程序必须注册一个子窗口类,并提供一个窗口过程来支持属于该类的窗口。 有关窗口类的详细信息,请参阅 窗口类。 有关窗口过程的详细信息,请参阅 窗口过程

下面是典型的 MDI 应用程序。 它名为 Multipad。

多板 mdi 应用程序框架窗口和客户端窗口

子窗口创建

若要创建子窗口,MDI 应用程序调用 CreateMDIWindow 函数或将 WM_MDICREATE 消息发送到 MDI 客户端窗口。 创建 MDI 子窗口的更有效方法是调用 CreateWindowEx 函数,指定 WS_EX_MDICHILD 扩展样式。

为了销毁子窗口,MDI 应用程序会将 WM_MDIDESTROY 消息发送到 MDI 客户端窗口。

子窗口激活

客户端窗口中一次可以显示任意数量的子窗口,但只能有一个子窗口处于活动状态。 活动子窗口位于所有其他子窗口的前面,并突出显示其边框。

用户可以通过单击非活动子窗口来激活该窗口。 MDI 应用程序通过向 MDI 客户端窗口发送 WM_MDIACTIVATE 消息来激活子窗口。 当客户端窗口处理此消息时,它会将 WM_MDIACTIVATE 消息发送到要激活的子窗口的窗口过程,以及正在停用的子窗口的窗口过程。

若要防止子窗口激活,请返回 FALSE 来处理子窗口WM_NCACTIVATE消息。

系统跟踪每个子窗口在重叠窗口堆栈中的位置。 这种堆叠称为 Z 顺序。 用户可以通过单击活动窗口中的窗口菜单中的“ 下一步 ”来激活 Z 顺序中的下一个子窗口。 应用程序通过向客户端窗口发送WM_MDINEXT消息,激活 Z 顺序中的下一个 (或上一 个) 子窗口。

为了检索活动子窗口的句柄,MDI 应用程序会将 WM_MDIGETACTIVE 消息发送到客户端窗口。

多个文档菜单

MDI 应用程序的框架窗口应包含带有窗口菜单的菜单栏。 窗口菜单应包括在客户端窗口中排列子窗口或关闭所有子窗口的项。 典型 MDI 应用程序的窗口菜单可能包含下表中的项。

Menu item 目的
磁贴 以磁贴格式排列子窗口,以便每个子窗口全部显示在客户端窗口中。
Cascade 以级联格式排列子窗口。 子窗口相互重叠,但每个窗口的标题栏是可见的。
排列图标 沿客户端窗口底部排列最小化子窗口的图标。
全部关闭 关闭所有子窗口。

 

每当创建子窗口时,系统都会自动将新的菜单项追加到该窗口菜单中。 菜单项的文本与新子窗口的菜单栏上的文本相同。 通过单击菜单项,用户可以激活相应的子窗口。 销毁子窗口时,系统会自动从窗口菜单中删除相应的菜单项。

系统最多可向窗口菜单添加十个菜单项。 创建第十个子窗口时,系统会将 “更多窗口 ”项添加到窗口菜单。 单击此项将显示 “选择窗口 ”对话框。 该对话框包含一个列表框,其中包含当前可用的所有 MDI 子窗口的标题。 用户可以通过单击列表框中的标题来激活子窗口。

如果 MDI 应用程序支持多种类型的子窗口,请定制菜单栏以反映与活动窗口关联的操作。 为此,请为应用程序支持的每种子窗口类型提供单独的菜单资源。 激活新类型的子窗口时,应用程序应向客户端窗口发送 WM_MDISETMENU 消息,并将句柄传递给相应菜单。

如果不存在子窗口,菜单栏应仅包含用于创建或打开文档的项。

当用户使用游标键在 MDI 应用程序的菜单中导航时,键的行为与用户浏览典型应用程序菜单时的行为不同。 在 MDI 应用程序中,控件从应用程序的窗口菜单传递到活动子窗口的窗口菜单,然后传递到菜单栏上的第一项。

多个文档加速器

若要接收和处理其子窗口的快捷键,MDI 应用程序必须在其消息循环中包含 TranslateMDISysAccel 函数。 在调用 TranslateAccelerator 或 DispatchMessage 函数之前,循环必须调用 TranslateMDISysAccel

MDI 子窗口窗口菜单上的快捷键与非 MDI 子窗口的快捷键不同。 在 MDI 子窗口中,ALT+ – (减去) 组合键将打开窗口菜单,CTRL+F4 组合键关闭活动子窗口,CTRL+F6 组合键激活下一个子窗口。

子窗口大小和排列方式

MDI 应用程序通过将消息发送到 MDI 客户端窗口来控制其子窗口的大小和位置。 为了最大化活动子窗口,应用程序会将 WM_MDIMAXIMIZE 消息发送到客户端窗口。 当子窗口最大化时,其工作区将完全填充 MDI 客户端窗口。 此外,系统会自动隐藏子窗口的标题栏,并将子窗口的窗口菜单图标和还原按钮添加到 MDI 应用程序的菜单栏。 应用程序可以通过向客户端窗口发送WM_MDIRESTORE消息,将客户端窗口还原到其原始 ( 预先) 大小和位置。

MDI 应用程序可以级联或平铺格式排列其子窗口。 级联子窗口时,窗口将显示在堆栈中。 堆栈底部的窗口占据屏幕左上角,其余窗口垂直和水平偏移,以便每个子窗口的左边框和标题栏可见。 若要以级联格式排列子窗口,MDI 应用程序会发送 WM_MDICASCADE 消息。 通常,当用户单击窗口菜单上的 Cascade 时,应用程序会发送此消息。

平铺子窗口时,系统会显示整个子窗口,不重叠任何窗口。 根据需要调整所有窗口的大小,以适应客户端窗口。 若要以磁贴格式排列子窗口,MDI 应用程序会将 WM_MDITILE 消息发送到客户端窗口。 通常,当用户单击窗口菜单上的 “磁贴 ”时,应用程序会发送此消息。

MDI 应用程序应为它支持的每种子窗口类型提供不同的图标。 应用程序在注册子窗口类时指定图标。 最小化子窗口时,系统会自动在客户端窗口的下半部分显示子窗口的图标。 MDI 应用程序通过向客户端窗口发送 WM_MDIICONARRANGE 消息来指示系统排列子窗口图标。 通常,当用户单击窗口菜单上的“ 排列图标” 时,应用程序会发送此消息。

图标标题窗口

由于 MDI 子窗口可能最小化,因此 MDI 应用程序必须避免像操作普通 MDI 子窗口一样操作图标标题窗口。 当应用程序枚举 MDI 客户端窗口的子窗口时,将显示图标标题窗口。 但是,图标标题窗口与其他子窗口不同,因为它们归 MDI 子窗口所有。

若要确定子窗口是否为图标标题窗口,请将 GetWindow 函数与GW_OWNER索引一起使用。 非游戏窗口返回 NULL。 请注意,此测试不足以用于顶级窗口,因为菜单和对话框是拥有的窗口。

子窗口数据

由于子窗口的数量因用户打开的文档数而异,因此 MDI 应用程序必须能够将数据 (关联,例如,当前文件的名称) 每个子窗口。 可通过两种方式实现此目的:

  • 在窗口结构中存储子窗口数据。
  • 使用窗口属性。

窗口结构

当 MDI 应用程序注册窗口类时,它可能会在窗口结构中为特定于此特定窗口类的应用程序数据保留额外的空间。 为了在此额外空间中存储和检索数据,应用程序使用 GetWindowLongSetWindowLong 函数。

若要为子窗口维护大量数据,应用程序可以为数据结构分配内存,然后将包含该结构的内存的句柄存储在与子窗口关联的额外空间中。

窗口属性

MDI 应用程序还可以使用窗口属性存储每个文档的数据。 每文档数据 是特定于特定子窗口中包含的文档类型的数据。 属性与窗口结构中的额外空间不同,在注册窗口类时无需分配额外的空间。 一个窗口可以具有任意数量的属性。 此外,如果偏移量用于访问窗口结构中的额外空间,则属性由字符串名称引用。 有关窗口属性的详细信息,请参阅 窗口属性