重叠窗口 是一个顶级窗口, (具有标题栏、边框和工作区的非子窗口) ;它旨在充当应用程序的主窗口。 它还可以具有窗口菜单、最小化和最大化按钮以及滚动条。 用作主窗口的重叠窗口通常包括所有这些组件。
通过在 CreateWindowEx 函数中指定 WS_OVERLAPPED 或 WS_OVERLAPPEDWINDOW 样式,应用程序将创建一个重叠的窗口。 如果使用 WS_OVERLAPPED 样式,则窗口具有标题栏和边框。 如果使用 WS_OVERLAPPEDWINDOW 样式,则窗口具有标题栏、调整边框大小、窗口菜单以及最小化和最大化按钮。
弹出窗口 是一种特殊类型的重叠窗口,用于对话框、消息框和显示在应用程序主窗口外部的其他临时窗口。 标题栏对于弹出窗口是可选的;否则,弹出窗口与 WS_OVERLAPPED 样式的重叠窗口相同。
通过在 CreateWindowEx 中指定 WS_POPUP 样式来创建弹出窗口。 若要包含标题栏,请指定 WS_CAPTION 样式。 使用 WS_POPUPWINDOW 样式创建具有边框和窗口菜单的弹出窗口。 WS_CAPTION 样式必须与 WS_POPUPWINDOW 样式结合使用,使窗口菜单可见。
子窗口 具有 WS_CHILD 样式,并且仅限于其父窗口的工作区。 应用程序通常使用子窗口将父窗口的工作区划分为功能区域。 通过在 CreateWindowEx 函数中指定 WS_CHILD 样式来创建子窗口。
子窗口必须具有父窗口。 父窗口可以是重叠窗口、弹出窗口,甚至是另一个子窗口。 调用 CreateWindowEx 时指定父窗口。 如果在 CreateWindowEx 中指定 WS_CHILD 样式,但未指定父窗口,则系统不会创建该窗口。
子窗口具有工作区,但没有其他功能,除非明确请求它们。 应用程序可以请求标题栏、窗口菜单、最小化和最大化子窗口的按钮、边框和滚动条,但子窗口不能有菜单。 如果应用程序在注册子窗口类或创建子窗口时指定了菜单句柄,则忽略菜单句柄。 如果未指定边框样式,系统将创建一个无边框窗口。 应用程序可以使用无边框子窗口划分父窗口的工作区,同时使分区对用户不可见。
本部分讨论子窗口的以下方面:
系统始终相对于其父窗口工作区的左上角放置子窗口。 子窗口的任何部分都不会出现在其父窗口的边框之外。 如果应用程序创建一个大于父窗口的子窗口或放置子窗口,以便部分或所有子窗口超出父窗口的边框,则系统会剪裁子窗口;也就是说,不显示父窗口工作区外部的部分。 影响父窗口的操作也会影响子窗口,如下所示。
系统不会从父窗口的工作区自动剪裁子窗口。 这意味着,如果父窗口在与子窗口相同的位置执行任何绘制,则父窗口在子窗口上绘制。 但是,如果父窗口具有 WS_CLIPCHILDREN 样式,系统会从父窗口的工作区中剪裁子窗口。 如果剪裁了子窗口,父窗口将无法在它上绘制。
子窗口可以与同一工作区中的其他子窗口重叠。 与一个或多个其他子窗口共享同一父窗口的子 窗口称为同级窗口 。 同级窗口可以在彼此的工作区中绘制,除非其中一个子窗口具有 WS_CLIPSIBLINGS 样式。 如果子窗口确实具有此样式,则其同级窗口位于子窗口内的任意部分将剪裁。
如果窗口具有 WS_CLIPCHILDREN 或 WS_CLIPSIBLINGS 样式,则会略微降低性能。 每个窗口占用系统资源,因此应用程序不应不分青红皂白地使用子窗口。 为了获得最佳性能,需要以逻辑方式划分主窗口的应用程序应在主窗口的窗口过程中执行此操作,而不是通过使用子窗口。
应用程序可以通过调用 SetParent 函数更改现有子窗口的父窗口。 在这种情况下,系统会从旧父窗口的工作区中删除子窗口,并将其移动到新父窗口的工作区。 如果 SetParent 指定 NULL 句柄,桌面窗口将成为新的父窗口。 在这种情况下,子窗口绘制在桌面上,超出任何其他窗口的边框。 GetParent 函数检索子窗口的父窗口的句柄。
父窗口将其工作区的一部分放弃给子窗口,子窗口接收来自该区域的所有输入。 对于父窗口的每个子窗口,窗口类不需要相同。 这意味着应用程序可以使用看起来不同的子窗口填充父窗口,并执行不同的任务。 例如,对话框可以包含多种类型的控件,每种类型的控件都是接受来自用户的不同类型的数据的子窗口。
子窗口只有一个父窗口,但父窗口可以有任意数量的子窗口。 反过来,每个子窗口都可以有子窗口。 在此窗口链中,每个子窗口称为原始父窗口的后代窗口。 应用程序使用 IsChild 函数来发现给定窗口是给定父窗口的子窗口还是子窗口。
EnumChildWindows 函数枚举父窗口的子窗口。 然后, EnumChildWindows 将每个子窗口的句柄传递给应用程序定义的回调函数。 同时枚举给定父窗口的后代窗口。
系统将子窗口的输入消息直接传递给子窗口;消息不会通过父窗口传递。 唯一的例外是 EnableWindow 函数禁用了子窗口。 在这种情况下,系统会将本应转到子窗口的任何输入消息传递到父窗口。 这允许父窗口检查输入消息并启用子窗口(如有必要)。
子窗口可以具有唯一的整数标识符。 使用控件窗口时,子窗口标识符非常重要。 应用程序通过向控件发送消息来定向控件的活动。 应用程序使用控件的子窗口标识符将消息定向到控件。 此外,控件将通知消息发送到其父窗口。 通知消息包括控件的子窗口标识符,父级使用该标识符来标识发送消息的控件。 应用程序通过将 CreateWindowEx 函数的 hMenu 参数设置为值而不是菜单句柄,为其他类型的子窗口指定子窗口标识符。
使用分层窗口可以显著提高具有复杂形状的窗口的性能和视觉效果,使其形状具有动画效果,或者希望使用 alpha 混合效果。 系统会自动撰写并重新绘制分层窗口和基础应用程序的窗口。 因此,分层窗口可以顺利呈现,而不会像复杂窗口区域那样闪烁。 此外,分层窗口可以是部分半透明窗口,即 alpha 混合窗口。
若要创建分层窗口,请在调用 CreateWindowEx 函数时指定 WS_EX_LAYERED 扩展窗口样式,或调用 SetWindowLong 函数以在创建窗口后设置 WS_EX_LAYERED 。 在 CreateWindowEx 调用后,分层窗口将不可见,直到为此窗口调用 SetLayeredWindowAttributes 或 UpdateLayeredWindow 函数。
从 Windows 8 开始, WS_EX_LAYERED 可用于子窗口和顶级窗口。 以前的 Windows 版本仅对顶级窗口支持 WS_EX_LAYERED 。
若要设置给定分层窗口的不透明度级别或透明度颜色键,请调用 SetLayeredWindowAttributes 。 调用后,当窗口显示或调整大小时,系统仍可能要求窗口进行绘制。 但是,由于系统存储分层窗口的图像,因此如果由于桌面上的相对窗口移动而显示其部分,系统不会要求窗口进行绘制。 如果旧版应用程序想要为窗口添加半透明或透明度效果,则不需要重构其绘制代码,因为系统会将调用 SetLayeredWindowAttributes 的窗口的绘制重定向到屏幕外内存中,并对其进行重新编译以实现所需的效果。
若要获得更快、更高效的动画,或者如果需要每像素 alpha,请调用 UpdateLayeredWindow 。 UpdateLayeredWindow 应主要用于应用程序必须直接提供分层窗口的形状和内容,而不使用系统通过 SetLayeredWindowAttributes 提供的重定向机制。 此外,使用 UpdateLayeredWindow 直接更有效地使用内存,因为系统不需要存储重定向窗口的映像所需的额外内存。 为了最大程度地提高动画窗口的效率,请调用 UpdateLayeredWindow 来更改分层窗口的位置和大小。 请注意,在调用 SetLayeredWindowAttributes 后,后续 的 UpdateLayeredWindow 调用将失败,直到清除并再次设置分层样式位。
分层窗口的命中测试基于窗口的形状和透明度。 这意味着窗口中颜色键或 alpha 值为零的区域将允许鼠标消息通过。 但是,如果分层窗口具有 WS_EX_TRANSPARENT 扩展窗口样式,则将忽略分层窗口的形状,并将鼠标事件传递到分层窗口下的其他窗口。
使用仅消息窗口 可以发送和接收消息。 它不可见、没有 z 顺序、无法枚举且不接收广播消息。 该窗口只是调度消息。
若要创建仅消息窗口,请在 CreateWindowEx 函数的 hWndParent 参数中指定 HWND_MESSAGE 常量或现有仅消息窗口的句柄。 还可以通过在 SetParent 函数的 hWndNewParent 参数中指定HWND_MESSAGE,将现有窗口更改为仅消息窗口。
若要查找仅消息窗口,请在 FindWindowEx 函数的 hwndParent 参数中指定 HWND_MESSAGE 。 此外,如果 hwndParent 和 hwndChildAfter 参数均为 NULL , FindWindowEx 将搜索仅消息 窗口和顶级窗口。
一个窗口可以通过多种方式与用户或其他窗口相关联。 窗口可以是拥有的窗口、前景窗口或背景窗口。 窗口也具有相对于其他窗口的 z 顺序。 有关详细信息,请参阅下列主题:
每个进程可以有多个执行线程,每个线程可以创建窗口。 创建用户当前正在使用的窗口的线程称为前台线程,窗口称为 前台窗口 。 所有其他线程都是后台线程,由后台线程创建的窗口称为 后台窗口 。
每个线程都有一个优先级,用于确定线程接收的 CPU 时间量。 尽管应用程序可以设置其线程的优先级,但通常前台线程的优先级略高于后台线程。 由于其优先级较高,前台线程接收的 CPU 时间比后台线程多。 前台线程的正常基优先级为 9;后台线程的正常基优先级为 7。
用户通过单击窗口或使用 Alt+TAB 或 ALT+ESC 组合键来设置前景窗口。 若要检索前台窗口的句柄,请使用 GetForegroundWindow 函数。 若要检查应用程序窗口是否为前台窗口,请将 GetForegroundWindow 返回的句柄与应用程序窗口的句柄进行比较。
应用程序使用 SetForegroundWindow 函数设置前台窗口。
系统限制哪些进程可以设置前台窗口。 仅当以下项时,进程才能设置前台窗口:
即使进程满足这些条件,也有可能被剥夺设置前台窗口的权利。
可以设置前台窗口的进程可以通过调用 AllowSetForegroundWindow 函数或使用 BSF_ALLOWSFW 标志调用 BroadcastSystemMessage 函数,使另一个进程能够设置 前台 窗口。 前台进程可以通过调用 LockSetForegroundWindow 函数来禁用对 SetForegroundWindow 的 调用。
重叠或弹出窗口可由另一个重叠或弹出窗口拥有。 拥有对窗口存在多个约束。
只有重叠或弹出窗口才能成为所有者窗口;子窗口不能是所有者窗口。 应用程序在创建具有 WS_OVERLAPPED或WS_POPUP 样式的窗口时,通过将所有者的窗口句柄指定为 CreateWindowEx 的 hwndParent 参数来创建拥有 的 窗口。 hwndParent 参数必须标识重叠或弹出窗口。 如果 hwndParent 标识子窗口,系统会将所有权分配给子窗口的顶级父窗口。 创建拥有的窗口后,应用程序无法将该窗口的所有权转让给另一个窗口。
默认情况下,对话框和消息框是拥有的窗口。 应用程序在调用创建对话框或消息框的函数时指定所有者窗口。
应用程序可以使用具有 GW_OWNER 标志的 GetWindow 函数来检索窗口所有者的句柄。
窗口的 z 顺序 指示窗口在重叠窗口堆栈中的位置。 此窗口堆栈沿虚轴 Z 轴方向,从屏幕向外延伸。 z 顺序顶层的窗口重叠在所有其他窗口之上。 z 顺序底部的窗口与所有其他窗口重叠。
系统在单个列表中维护 z 顺序。 它将窗口添加到 z 顺序,具体取决于它们是最顶层窗口、顶级窗口还是子窗口。 最顶部窗口 与所有其他非最顶部窗口重叠,无论它是活动窗口还是前台窗口。 最顶部的窗口具有 WS_EX_TOPMOST 样式。 所有最顶层窗口都以 z 顺序显示在任何非最顶部窗口之前。 子窗口与其父窗口按 z 顺序分组。
当应用程序创建窗口时,系统会将其放在同一类型的窗口的 z 顺序的顶部。 可以使用 BringWindowToTop 函数将窗口置于同一类型的窗口的 z 顺序顶部。 可以使用 SetWindowPos 和 DeferWindowPos 函数重新排列 z 顺序。
用户通过激活其他窗口来更改 z 顺序。 系统将活动窗口放置在同一类型的窗口的 z 顺序的顶部。 当窗口位于 z 顺序的顶部时,其子窗口也是如此。 可以使用 GetTopWindow 函数搜索父窗口的所有子窗口,并将一个句柄返回到 z 顺序中最高的子窗口。 GetNextWindow 函数以 z 顺序检索下一个或上一个窗口的句柄。
在任何给定时间,窗口可能处于活动状态或处于非活动状态;隐藏或可见;和最小化、最大化或还原。 这些品质统称为 窗口显示状态 。 以下主题讨论窗口显示状态:
活动窗口 是用户当前正在使用的应用程序的顶级窗口。 为了允许用户轻松识别活动窗口,系统将其置于 z 顺序的顶部,并将其标题栏和边框的颜色更改为系统定义的活动窗口颜色。 只有顶级窗口才能是活动窗口。 当用户使用子窗口时,系统会激活与子窗口关联的顶级父窗口。
系统中一次只有一个顶级窗口处于活动状态。 用户通过单击顶级窗口 (或其子窗口之一) ,或使用 Alt+ESC 或 ALT+TAB 组合键来激活该窗口。 应用程序通过调用 SetActiveWindow 函数激活顶级窗口。 其他函数可能导致系统激活不同的顶级窗口,包括 SetWindowPos 、 DeferWindowPos 、 SetWindowPlacement 和 DestroyWindow 。 尽管应用程序可以随时激活不同的顶级窗口,但为了避免混淆用户,它只应在响应用户操作时执行此操作。 应用程序使用 GetActiveWindow 函数检索活动窗口的句柄。
当激活从一个应用程序的顶级窗口更改为另一个应用程序的顶级窗口时,系统会向这两个应用程序发送 WM_ACTIVATEAPP 消息,通知它们更改。 当激活更改为同一应用程序中的其他顶级窗口时,系统会向两个窗口发送 WM_ACTIVATE 消息。
可以禁用窗口。 禁用的窗口 不接收来自用户的键盘或鼠标输入,但它可以从其他窗口、其他应用程序和系统接收消息。 应用程序通常会禁用窗口,以防止用户使用窗口。 例如,应用程序可能会禁用对话框中的按钮,以防止用户选择它。 应用程序可以随时启用禁用的窗口;启用窗口可还原正常输入。
默认情况下,创建窗口时启用。 但是,应用程序可以指定 WS_DISABLED 样式来禁用新窗口。 应用程序使用 EnableWindow 函数启用或禁用现有窗口。 当窗口的启用状态即将更改时,系统会向窗口发送 WM_ENABLE 消息。 应用程序可以使用 IsWindowEnabled 函数确定是否启用窗口。
禁用子窗口时,系统会将子窗口的鼠标输入消息传递到父窗口。 父级使用消息确定是否启用子窗口。 有关详细信息,请参阅 鼠标输入 。
一次只能有一个窗口接收键盘输入;据说该窗口具有键盘焦点。 如果应用程序使用 EnableWindow 函数禁用键盘焦点窗口,则窗口除了被禁用外,还会失去键盘焦点。 然后,EnableWindow 将键盘焦点设置为 NULL ,这意味着没有窗口具有焦点。 如果子窗口或其他后代窗口具有键盘焦点,则禁用父窗口时,子窗口将失去焦点。 有关详细信息,请参阅 键盘输入 。
窗口可以采用可见或隐藏样式。 系统在屏幕上显示一个 可见窗口 。 它通过不绘制 隐藏窗口 来隐藏它。 如果窗口可见,用户就可以在该窗口中提供输入并查看窗口的输出。 当隐藏窗口时,实际上是将其禁用。 隐藏的窗口可以从系统或其他窗口中处理消息,但它不能处理用户的输入,也不能显示输出。 应用程序在创建窗口时设置窗口的可见性状态。 稍后,应用程序可以更改可见性状态。
为窗口设置 WS_VISIBLE 样式时,窗口可见。 默认情况下,除非应用程序指定 WS_VISIBLE 样式, 否则 CreateWindowEx 函数会创建隐藏窗口。 通常,应用程序在创建窗口后设置 WS_VISIBLE 样式,以向用户隐藏创建过程的详细信息。 例如,应用程序在自定义窗口外观时可能会隐藏新窗口。 如果在 CreateWindowEx 中指定 了WS_VISIBLE 样式,则系统会在创建窗口后但在显示窗口之前将 WM_SHOWWINDOW 消息发送到窗口。
应用程序可以使用 IsWindowVisible 函数确定窗口是否可见。 应用程序可以使用 ShowWindow、SetWindowPos 、 DeferWindowPos、SetWindowPlacement 或 SetWindowLong 函数显示) (使窗口可见或隐藏窗口 。 这些函数通过设置或删除窗口的 WS_VISIBLE 样式来显示或隐藏窗口。 在显示或隐藏消息之前,他们还将 WM_SHOWWINDOW 消息发送到窗口。
当所有者窗口最小化时,系统会自动隐藏关联的拥有窗口。 同样,还原所有者窗口时,系统会自动显示关联的拥有窗口。 在这两种情况下,系统会先将 WM_SHOWWINDOW 消息发送到拥有的窗口,然后再隐藏或显示它们。 有时,应用程序可能需要隐藏拥有的窗口,而无需最小化或隐藏所有者。 在这种情况下,应用程序使用 ShowOwnedPopups 函数。 此函数设置或删除所有拥有窗口 的WS_VISIBLE 样式,并将 WM_SHOWWINDOW 消息发送到拥有的窗口,然后再隐藏或显示它们。 隐藏所有者窗口不会影响拥有窗口的可见性状态。
当父窗口可见时,其关联的子窗口也可见。 同样,当父窗口处于隐藏状态时,其子窗口也会被隐藏。 最小化父窗口不会影响子窗口的可见性状态;也就是说,子窗口与父窗口一起最小化,但 WS_VISIBLE 样式不会更改。
即使窗口具有 WS_VISIBLE 样式,用户也可能无法在屏幕上看到该窗口;其他窗口可能完全重叠,或者它可能已移动到屏幕边缘之外。 此外,可见子窗口受其父子关系所建立的剪裁规则的约束。 如果窗口的父窗口不可见,它也将不可见。 如果父窗口移动超出屏幕边缘,子窗口也会移动,因为子窗口相对于父窗口的左上角绘制。 例如,用户可能会将包含子窗口的父窗口移动到离屏幕边缘足够远的位置,用户可能无法看到子窗口,即使子窗口及其父窗口都具有 WS_VISIBLE 样式。
最大化窗口 是具有 WS_MAXIMIZE样式的 窗口。 默认情况下,系统会放大最大化窗口,使其填充整个屏幕(或者在子窗口的情况下使其填充父窗口的整个工作区)。 尽管窗口的大小可以设置为与最大化窗口的大小相同,但最大化窗口略有不同。 系统会自动将窗口的标题栏移动到屏幕顶部或父窗口工作区的顶部。 此外,系统会禁用窗口的大小调整边框和标题栏 (窗口定位功能,以便用户无法通过将标题栏拖动) 来移动窗口。
最小化窗口 是具有 WS_MINIMIZE样式的 窗口。 默认情况下,系统会将最小化窗口缩小到任务栏按钮的大小并将最小化窗口移至任务栏上。 还原的窗口 是已返回到其以前的大小和位置的窗口,即它最小化或最大化之前的大小。
如果应用程序在 CreateWindowEx 函数中指定 WS_MAXIMIZE 或 WS_MINIMIZE 样式,则窗口最初将最大化或最小化。 创建窗口后,应用程序可以使用 CloseWindow 函数最小化窗口。 ArrangeIconicWindows 函数排列桌面上的图标,或在父窗口中排列父窗口的最小化子窗口。 OpenIcon 函数将最小化窗口还原到其以前的大小和位置。
ShowWindow 函数可以最小化、最大化或还原窗口。 它还可以设置窗口的可见性和激活状态。 SetWindowPlacement 函数包含与 ShowWindow 相同的功能,但它可以替代窗口的默认最小化、最大化和还原位置。
IsZoomed 和 IsIconic 函数分别确定给定窗口是最大化还是最小化。 GetWindowPlacement 函数检索窗口的最小化、最大化和还原位置,还确定窗口的显示状态。
当系统收到最大化或还原最小化窗口的命令时,它会向窗口发送 WM_QUERYOPEN 消息。 如果窗口过程返回 FALSE ,则系统将忽略最大化或还原命令。
系统自动将最大化窗口的大小和位置设置为最大化窗口的系统定义的默认值。 若要替代这些默认值,应用程序可以调用 SetWindowPlacement 函数,或处理系统即将最大化窗口时窗口收到的 WM_GETMINMAXINFO 消息。 WM_GETMINMAXINFO 包含指向 MINMAXINFO 结构的指针,其中包含系统用于设置最大大小和位置的值。 替换这些值将替代默认值。
窗口的大小和位置以相对于屏幕或父窗口的坐标表示为边框。 顶级窗口的坐标相对于屏幕的左上角;子窗口的坐标相对于父窗口的左上角。 应用程序在创建窗口时指定窗口的初始大小和位置,但可以随时更改窗口的大小和位置。 有关详细信息,请参阅 填充形状 。
本节包含下列主题:
应用程序可以通过在 CreateWindowEx 中指定CW_USEDEFAULT来允许系统计算顶级窗口的初始大小或位置。 如果应用程序将窗口的坐标设置为CW_USEDEFAULT,并且未创建其他顶级窗口,则系统会设置新窗口相对于屏幕左上角的位置;否则,它将设置相对于应用程序最近创建的顶级窗口位置的位置。 如果宽度和高度参数设置为CW_USEDEFAULT,系统将计算新窗口的大小。 如果应用程序已创建其他顶级窗口,系统将新窗口的大小基于应用程序最近创建的顶级窗口的大小。 在创建子窗口或弹出窗口时指定CW_USEDEFAULT会导致系统将窗口的大小设置为默认的最小窗口大小。
系统为 WS_THICKFRAME 样式的窗口保持最小和最大跟踪大小;具有此样式的窗口具有大小调整边框。 最小跟踪大小 是通过拖动窗口的大小边框可以生成的最小窗口大小。 同样, 最大跟踪大小 是可以通过拖动大小调整边框生成的最大窗口大小。
当系统创建窗口时,窗口的最小和最大跟踪大小将设置为系统定义的默认值。 应用程序可以通过处理 WM_GETMINMAXINFO 消息来发现并替代默认值。 有关详细信息,请参阅 大小和位置消息 。
具有窗口菜单的应用程序可以通过发送系统命令来更改该窗口的大小和位置。 当用户从窗口菜单中选择命令时,将生成系统命令。 应用程序可以通过向窗口发送 WM_SYSCOMMAND 消息来模拟用户操作。 以下系统命令会影响窗口的大小和位置。
Command创建窗口后,应用程序可以通过调用多个不同函数之一(包括 SetWindowPlacement 、 MoveWindow 、 SetWindowPos 和 DeferWindowPos )来设置窗口的大小或位置。 SetWindowPlacement 设置窗口的最小化位置、最大化的位置、还原的大小和位置以及显示状态。 MoveWindow 和 SetWindowPos 函数相似;两者都设置单个应用程序窗口的大小或位置。 SetWindowPos 函数包含一组影响窗口显示状态的标志; MoveWindow 不包括这些标志。 使用 BeginDeferWindowPos 、 DeferWindowPos 和 EndDeferWindowPos 函数可同时设置多个窗口的位置,包括大小、位置、z 顺序中的位置和显示状态。
应用程序可以使用 GetWindowRect 函数检索窗口边界矩形的坐标。 GetWindowRect 使用窗口左上角和右下角的坐标填充 RECT 结构。 坐标相对于屏幕左上角,即使是子窗口也是如此。 ScreenToClient 或 MapWindowPoints 函数将子窗口边界矩形的屏幕坐标映射到相对于父窗口工作区的坐标。
GetClientRect 函数检索窗口工作区的坐标。 GetClientRect 使用工作区左上角和右下角的坐标填充 RECT 结构,但这些坐标相对于工作区本身。 这意味着工作区左上角的坐标始终 (0,0) ,而右下角的坐标是工作区的宽度和高度。
CascadeWindows 函数级联桌面上的窗口或级联指定父窗口的子窗口。 TileWindows 函数平铺桌面上的窗口或平铺指定父窗口的子窗口。
系统将 WM_GETMINMAXINFO 消息发送到大小或位置即将更改的窗口。 例如,当用户从窗口菜单中单击“ 移动 ”或“ 大小 ”或单击调整大小边框或标题栏时,将发送消息;当应用程序调用 SetWindowPos 移动窗口或调整窗口大小时,也会发送 消息。 WM_GETMINMAXINFO 包含指向 MINMAXINFO 结构的指针,该结构包含窗口的默认最大化大小和位置,以及默认的最小和最大跟踪大小。 应用程序可以通过处理 WM_GETMINMAXINFO 并设置 MINMAXINFO 的相应成员来替代默认值。 窗口必须具有 WS_THICKFRAME 或 WS_CAPTION 样式才能接收 WM_GETMINMAXINFO 。 具有 WS_THICKFRAME 样式的窗口在窗口创建过程中以及移动或调整其大小时接收此消息。
系统将 WM_WINDOWPOSCHANGING 消息发送到其大小、位置、z 顺序中的位置或显示状态即将更改的窗口。 此消息包含指向 WINDOWPOS 结构的指针,该结构指定窗口的新大小、位置、z 顺序中的位置和显示状态。 通过设置 WINDOWPOS 的成员,应用程序可能会影响窗口的新大小、位置和外观。
更改窗口的大小、位置、z 顺序中的位置或显示状态后,系统会将 WM_WINDOWPOSCHANGED 消息发送到窗口。 此消息包含指向 WINDOWPOS 的指针,该指针通知窗口其新大小、位置、z 顺序中的位置和显示状态。 设置随 WM_WINDOWPOSCHANGED 传递的 WINDOWPOS 结构的成员对窗口没有影响。 必须处理 WM_SIZE 和 WM_MOVE 消息的窗口,必须将 WM_WINDOWPOSCHANGED 传递到 DefWindowProc 函数;否则,系统不会向窗口发送 WM_SIZE 和 WM_MOVE 消息。
创建窗口或调整窗口大小时,系统会将 WM_NCCALCSIZE 消息发送到窗口。 系统使用该消息来计算窗口工作区的大小以及相对于窗口左上角的工作区的位置。 窗口通常将此消息传递到默认窗口过程;但是,在自定义窗口的非工作区或在调整窗口大小时保留部分工作区的应用程序,此消息可能很有用。 有关详细信息,请参阅 绘画和绘图 。
可以使用 AnimateWindow 函数在显示或隐藏窗口时生成特殊效果。 以这种方式对窗口进行动画处理时,系统会滚动、滑动或淡化窗口,具体取决于在对 AnimateWindow 的调用中指定的标志。
默认情况下,系统使用 滚动动画 。 在此效果下,窗口显示为打开 (显示窗口) 或滚动关闭 (隐藏窗口) 。 可以使用 dwFlags 参数指定窗口是水平滚动、垂直滚动还是对角线滚动。
指定 AW_SLIDE 标志时,系统会使用 幻灯片动画 。 在此效果下,窗口显示为滑入视图 (显示窗口) 或滑出视图 (隐藏窗口) 。 可以使用 dwFlags 参数指定窗口是水平、垂直还是对角线滑动。
指定 AW_BLEND 标志时,系统会使用 alpha 混合淡出 。
还可以使用 AW_CENTER 标志使窗口看起来向内折叠或向外展开。
窗口布局定义文本和 Windows 图形设备接口 (GDI) 对象如何在 DC) (窗口或设备上下文中布局。 某些语言(如英语、法语和德语)需要从左到右 (LTR) 布局。 其他语言(如阿拉伯语和希伯来语)需要从右到左 (RTL) 布局。 窗口布局适用于文本,但也会影响窗口的其他 GDI 元素,包括位图、图标、原点的位置、按钮、级联树控件,以及水平坐标是随向左还是向右增加。 例如,在应用程序设置 RTL 布局后,原点将定位在窗口或设备的右边缘,表示水平坐标的数量随着向左移动而增加。 但是,并非所有对象都受窗口布局的影响。 例如,对话框、消息框和设备上下文(如图元文件和打印机 DC)的布局必须单独处理。 本主题稍后将提及这些内容的具体内容。
使用窗口函数可以指定或更改 Windows 的阿拉伯语和希伯来语版本的窗口布局。 请注意,对于具有CS_OWNDC样式的窗口或具有GM_ADVANCED图形模式的 DC,不支持更改为 RTL 布局 ( 也称为镜像) 。
默认情况下,窗口布局为从左到右 (LTR) 。 若要设置 RTL 窗口布局,请使用样式 WS_EX_LAYOUTRTL 调用 CreateWindowEx 。 此外,默认情况下,子窗口 (,即在调用 CreateWindow 或 CreateWindowEx 时使用 WS_CHILD 样式和有效的父 hWnd 参数创建的子窗口) 具有与其父窗口相同的布局 。 若要禁用镜像到所有子窗口的继承,请在调用 CreateWindowEx 时指定 WS_EX_NOINHERITLAYOUT 。 请注意,镜像不是由拥有的窗口继承的, (那些没有 WS_CHILD 样式) 创建的窗口,或在 CreateWindowEx 中使用父 hWnd 参数创建的窗口继承为 NULL 。 若要禁用单个窗口的镜像继承,请使用 GetWindowLong 和 SetWindowLong 处理 WM_NCCREATE 消息以关闭 WS_EX_LAYOUTRTL 标志。 此处理是除任何其他需要处理之外的其他处理。 以下代码片段演示如何执行此操作。
SetWindowLong (hWnd,
GWL_EXSTYLE,
GetWindowLong(hWnd,GWL_EXSTYLE) & ~WS_EX_LAYOUTRTL))
可以通过调用 SetProcessDefaultLayout (LAYOUT_RTL) 将默认布局设置为 RTL。 调用后创建的所有窗口都将镜像,但现有窗口不受影响。 若要关闭默认镜像,请调用 SetProcessDefaultLayout (0) 。
请注意, SetProcessDefaultLayout 仅镜像镜像窗口的 DC。 若要镜像任何 DC,请调用 SetLayout (hdc,LAYOUT_RTL) 。 有关详细信息,请参阅本主题后面的有关镜像设备上下文(不与 Windows 关联的)的讨论。
默认情况下,镜像窗口中的位图和图标也会镜像。 但是,并非所有这些都应当镜像。 例如,不应镜像具有文本、企业徽标或模拟时钟的那些。 若要禁用位图的镜像,请使用 dwLayout 中设置LAYOUT_BITMAPORIENTATIONPRESERVED位调用 SetLayout。 若要在 DC 中禁用镜像,请调用 SetLayout (hdc, 0) 。
若要查询当前默认布局,请调用 GetProcessDefaultLayout。 成功返回后, pdwDefaultLayout 包含LAYOUT_RTL或 0。 若要查询设备上下文的布局设置,请调用 GetLayout。 成功返回后, GetLayout 将返回一个 DWORD,该 DWORD 通过LAYOUT_RTL和LAYOUT_BITMAPORIENTATIONPRESERVED位的设置来指示布局设置。
创建窗口后,可以使用 SetWindowLong 函数更改布局。 例如,当用户将现有窗口的用户界面语言从阿拉伯语或希伯来语更改为德语时,这是必需的。 但是,在更改现有窗口的布局时,必须使窗口失效并更新,以确保窗口的内容都在同一布局上绘制。 下面的代码示例来自根据需要更改窗口布局的示例代码:
// Using ANSI versions of GetWindowLong and SetWindowLong because Unicode
// is not needed for these calls
lExStyles = GetWindowLongA(hWnd, GWL_EXSTYLE);
// Check whether new layout is opposite the current layout
if (!!(pLState -> IsRTLLayout) != !!(lExStyles & WS_EX_LAYOUTRTL))
// the following lines will update the window layout
lExStyles ^= WS_EX_LAYOUTRTL; // toggle layout
SetWindowLongA(hWnd, GWL_EXSTYLE, lExStyles);
InvalidateRect(hWnd, NULL, TRUE); // to update layout in the client area
在镜像中,应考虑“近”和“远”,而不是“左”和“右”。 否则可能会导致问题。 在屏幕坐标和客户端坐标之间进行映射时,会导致镜像窗口中出现问题的一种常见编码做法。 例如,应用程序通常使用类似于下面的代码在窗口中放置控件:
// DO NOT USE THIS IF APPLICATION MIRRORS THE WINDOW
// get coordinates of the window in screen coordinates
GetWindowRect(hControl, (LPRECT) &rControlRect);
// map screen coordinates to client coordinates in dialog
ScreenToClient(hDialog, (LPPOINT) &rControlRect.left);
ScreenToClient(hDialog, (LPPOINT) &rControlRect.right);
这会导致镜像出现问题,因为矩形的左边缘成为镜像窗口中的右边缘,反之亦然。 若要避免此问题,请将 ScreenToClient 调用替换为 对 MapWindowPoints 的 调用,如下所示:
// USE THIS FOR MIRRORING
GetWindowRect(hControl, (LPRECT) &rControlRect);
MapWindowPoints(NULL, hDialog, (LPPOINT) &rControlRect, 2)
此代码之所以有效,是因为在支持镜像的平台上, MapWindowPoints 被修改为在客户端窗口镜像时交换左右点坐标。 有关详细信息,请参阅 MapWindowPoints 的“备注”部分。
另一种可能导致镜像窗口出现问题的常见做法是使用屏幕坐标中的偏移量(而不是客户端坐标)将对象定位到客户端窗口中。 例如,以下代码使用屏幕坐标的差异作为客户端坐标中的 x 位置,以在对话框中定位控件。
// OK if LTR layout and mapping mode of client is MM_TEXT,
// but WRONG for a mirrored dialog
RECT rdDialog;
RECT rcControl;
HWND hControl = GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hDlg, &rcDialog); // gets rect in screen coordinates
GetWindowRect(hControl, &rcControl);
MoveWindow(hControl,
rcControl.left - rcDialog.left, // uses x position in client coords
rcControl.top - rcDialog.top,
nWidth,
nHeight,
FALSE);
当对话框窗口具有从左到右 (LTR) 布局且客户端的映射模式为MM_TEXT时,此代码就很好了,因为客户端坐标中的新 x 位置对应于控件左边缘和屏幕坐标中对话框的差异。 但是,在镜像对话框中,左和右是反转的,因此应改用 MapWindowPoints ,如下所示:
RECT rcDialog;
RECT rcControl;
HWND hControl - GetDlgItem(hDlg, IDD_CONTROL);
GetWindowRect(hControl, &rcControl);
// MapWindowPoints works correctly in both mirrored and non-mirrored windows.
MapWindowPoints(NULL, hDlg, (LPPOINT) &rcControl, 2);
// Now rcControl is in client coordinates.
MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, FALSE)
镜像对话框和消息框
对话框和消息框不继承布局,因此必须显式设置布局。 若要镜像消息框,请使用 MB_RTLREADING 选项调用 MessageBox 或 MessageBoxEx。 若要从右到左布局对话框,请使用对话框模板结构 DLGTEMPLATEEX 中的扩展样式WS_EX_LAYOUTRTL。 属性表是对话框的一种特殊情况。 每个选项卡被视为一个单独的对话框,因此需要在要镜像的每个选项卡中包括WS_EX_LAYOUTRTL样式。
镜像不与窗口关联的设备上下文
与窗口无关的 DC(例如图元文件或打印机 DC)不会继承布局,因此必须显式设置布局。 若要更改设备上下文布局,请使用 SetLayout 函数。
SetLayout 函数很少用于窗口。 通常,Windows 仅在处理 WM_PAINT 消息时接收关联的 DC。 有时,程序通过调用 GetDC 为窗口创建 DC。 无论采用哪种方式,DC 的初始布局都由 BeginPaint 或 GetDC 根据窗口的WS_EX_LAYOUTRTL标志设置。
GetWindowOrgEx、GetWindowExtEx、GetViewportOrgEx 和 GetViewportExtEx 返回的值不受调用 SetLayout 的影响。
当布局为 RTL 时, GetMapMode 将返回MM_ANISOTROPIC而不是MM_TEXT。 使用 MM_TEXT 调用 SetMapMode 将正常工作;仅 GetMapMode 中的返回值受到影响。 同样, (hdc 调用 SetLayout 时,LAYOUT_RTL) MM_TEXT映射模式会导致报告的映射模式更改为MM_ANISOTROPIC。
通常,应用程序必须销毁它创建的所有窗口。 它通过使用 DestroyWindow 函数执行此操作。 当窗口被销毁时,系统会隐藏窗口(如果它可见),然后删除与该窗口关联的任何内部数据。 这会使窗口句柄失效,而应用程序不能再使用窗口句柄。
应用程序在创建后不久会销毁它创建的许多窗口。 例如,应用程序通常会在应用程序具有足够的用户输入以继续其任务后立即销毁对话框窗口。 应用程序最终会销毁应用程序 (的主窗口,然后终止) 。
在销毁窗口之前,应用程序应保存或删除与窗口关联的任何数据,并释放为窗口分配的任何系统资源。 如果应用程序未释放资源,系统将释放应用程序未释放的任何资源。
销毁窗口不会影响从中创建窗口的窗口类。 仍可使用该类创建新窗口,并且该类的任何现有窗口将继续运行。 销毁窗口也会销毁窗口的后代窗口。 DestroyWindow 函数首先将WM_DESTROY消息发送到窗口,然后发送到其子窗口和子窗口。 这样,被销毁的窗口的所有后代窗口也会被销毁。
当用户单击“关闭”时,带有窗口菜单的窗口会收到WM_CLOSE消息。 通过处理此消息,应用程序可以在销毁窗口之前提示用户进行确认。 如果用户确认应销毁窗口,应用程序可以调用 DestroyWindow 函数来销毁窗口。
如果正在销毁的窗口是活动窗口,则活动状态和焦点状态都转移到另一个窗口。 成为活动窗口的窗口是下一个窗口,由 Alt+ESC 组合键决定。 然后,新的活动窗口确定哪个窗口接收键盘焦点。