窗口功能

本概述讨论窗口的功能,例如窗口类型、状态、大小和位置。

窗口类型

本部分包含以下描述窗口类型的主题。

重叠的 Windows

重叠窗口是顶级窗口, (具有标题栏、边框和工作区的非子窗口) ;它旨在用作应用程序的main窗口。 它还可以具有窗口菜单、最小化和最大化按钮以及滚动条。 用作main窗口的重叠窗口通常包括所有这些组件。

通过在 CreateWindowEx 函数中指定WS_OVERLAPPEDWS_OVERLAPPEDWINDOW样式,应用程序将创建重叠窗口。 如果使用 WS_OVERLAPPED 样式,则窗口具有标题栏和边框。 如果使用 WS_OVERLAPPEDWINDOW 样式,则窗口具有标题栏、调整边框大小、窗口菜单以及最小化和最大化按钮。

弹出窗口

弹出窗口是一种特殊类型的重叠窗口,用于在应用程序的main窗口外显示的对话框、消息框和其他临时窗口。 弹出窗口的标题栏是可选的;否则,弹出窗口与 WS_OVERLAPPED 样式的重叠窗口相同。

通过在 CreateWindowEx 中指定WS_POPUP样式来创建弹出窗口。 若要包含标题栏,请指定 WS_CAPTION 样式。 使用 WS_POPUPWINDOW 样式创建具有边框和窗口菜单的弹出窗口。 WS_CAPTION样式必须与WS_POPUPWINDOW样式结合使用,使窗口菜单可见。

子窗口

子窗口具有WS_CHILD样式,并且仅限于其父窗口的工作区。 应用程序通常使用子窗口将父窗口的工作区划分为功能区域。 可以通过在 CreateWindowEx 函数中指定WS_CHILD样式来创建子窗口。

子窗口必须具有父窗口。 父窗口可以是重叠窗口、弹出窗口甚至另一个子窗口。 调用 CreateWindowEx 时指定父窗口。 如果在 CreateWindowEx 中指定WS_CHILD样式,但没有指定父窗口,则系统不会创建该窗口。

子窗口具有工作区,但没有其他功能,除非显式请求它们。 应用程序可以请求标题栏、窗口菜单、最小化和最大化子窗口的按钮、边框和滚动条,但子窗口不能有菜单。 如果应用程序在注册子窗口类或创建子窗口时指定菜单句柄,则忽略菜单句柄。 如果未指定边框样式,系统会创建无边框窗口。 应用程序可以使用无边框子窗口来划分父窗口的工作区,同时使划分对用户不可见。

本部分讨论子窗口的以下方面:

定位

系统始终相对于其父窗口工作区的左上角定位子窗口。 子窗口的任何部分从未出现在其父窗口的边框之外。 如果应用程序创建的子窗口大于父窗口或定位子窗口,使部分或所有子窗口超出父窗口的边框,系统会剪裁子窗口;也就是说,不显示父窗口工作区之外的部分。 影响父窗口的操作也可能影响子窗口,如下所示。

父窗口 子窗口
已破坏 在销毁父窗口之前销毁。
Hidden 在隐藏父窗口之前隐藏。 子窗口仅在父窗口可见时才可见。
已移动 随父窗口的工作区移动。 子窗口负责在移动后绘制其工作区。
显示 显示在显示父窗口之后。

 

剪裁

系统不会从父窗口的工作区自动剪裁子窗口。 这意味着,如果父窗口在子窗口所在的同一位置执行任何绘图,则父窗口在子窗口上绘制。 但是,如果父窗口具有 WS_CLIPCHILDREN 样式,系统会从父窗口的工作区中剪裁子窗口。 如果剪裁了子窗口,则父窗口无法对其进行绘制。

子窗口可以与同一工作区中的其他子窗口重叠。 与一个或多个其他子窗口共享同一父窗口的子窗口称为 同级窗口。 同级窗口可以在彼此的工作区中绘制,除非其中一个子窗口具有 WS_CLIPSIBLINGS 样式。 如果子窗口确实具有此样式,则其同级窗口位于子窗口内的任何部分均被剪裁。

如果窗口具有 WS_CLIPCHILDRENWS_CLIPSIBLINGS 样式,则性能将略有下降。 每个窗口占用系统资源,因此应用程序不应不分青红皂白地使用子窗口。 为了获得最佳性能,需要以逻辑方式划分其main窗口的应用程序应在main窗口的窗口过程中执行此操作,而不是使用子窗口。

与父窗口的关系

应用程序可以通过调用 SetParent 函数来更改现有子窗口的父窗口。 在这种情况下,系统会从旧父窗口的工作区中删除子窗口,并将其移动到新父窗口的工作区。 如果 SetParent 指定 NULL 句柄,桌面窗口将成为新的父窗口。 在这种情况下,子窗口在桌面上绘制,在任何其他窗口的边框之外。 GetParent 函数检索子窗口的父窗口的句柄。

父窗口将其工作区的一部分放弃给子窗口,子窗口接收来自该区域的所有输入。 对于父窗口的每个子窗口,窗口类不需要相同。 这意味着应用程序可以使用看起来不同的子窗口填充父窗口,并执行不同的任务。 例如,一个对话框可以包含多种类型的控件,每个控件都是一个接受用户不同类型的数据的子窗口。

子窗口只有一个父窗口,但父窗口可以有任意数量的子窗口。 每个子窗口反过来可以有子窗口。 在此窗口链中,每个子窗口称为原始父窗口的后代窗口。 应用程序使用 IsChild 函数来发现给定窗口是给定父窗口的子窗口还是子窗口。

EnumChildWindows 函数枚举父窗口的子窗口。 然后, EnumChildWindows 将每个子窗口的句柄传递给应用程序定义的回调函数。 还枚举给定父窗口的后代窗口。

消息

系统将子窗口的输入消息直接传递到子窗口;消息不会通过父窗口传递。 唯一的例外是 EnableWindow 函数禁用了子窗口。 在这种情况下,系统会将本应转到子窗口的任何输入消息传递到父窗口。 这允许父窗口检查输入消息并在必要时启用子窗口。

子窗口可以具有唯一整数标识符。 使用控件窗口时,子窗口标识符非常重要。 应用程序通过向其发送消息来指示控件的活动。 应用程序使用控件的子窗口标识符将消息定向到控件。 此外,控件将通知消息发送到其父窗口。 通知消息包含控件的子窗口标识符,父级使用该标识符来标识发送消息的控件。 应用程序通过将 CreateWindowEx 函数的 hMenu 参数设置为值而不是菜单句柄来指定其他类型的子窗口的子窗口标识符。

分层 Windows

使用分层窗口可以显著提高具有复杂形状、对其形状进行动画处理或希望使用 alpha 混合效果的窗口的性能和视觉效果。 系统自动编写并重新绘制分层窗口和基础应用程序的窗口。 因此,分层窗口可以顺利呈现,而不会像复杂窗口区域那样闪烁。 此外,分层窗口可以部分半透明,即 alpha 混合窗口。

若要创建分层窗口,请在调用 CreateWindowEx 函数时指定WS_EX_LAYERED扩展窗口样式,或调用 SetWindowLong 函数以在创建窗口后设置WS_EX_LAYERED。 调用 CreateWindowEx 后,分层窗口将不可见,直到为此窗口调用 SetLayeredWindowAttributesUpdateLayeredWindow 函数。

注意

从Windows 8开始,WS_EX_LAYERED可用于子窗口和顶级窗口。 以前的 Windows 版本仅支持 顶级窗口WS_EX_LAYERED

 

若要设置给定分层窗口的不透明度级别或透明度颜色键,请调用 SetLayeredWindowAttributes。 调用后,当窗口显示或调整大小时,系统仍可能要求窗口进行绘制。 但是,由于系统存储分层窗口的图像,因此如果由于相对窗口在桌面上移动而导致窗口部分显示,系统不会要求窗口进行绘制。 如果旧版应用程序想要为窗口添加半透明或透明度效果,则不需要重新构造其绘制代码,因为系统会将调用 SetLayeredWindowAttributes 的窗口的绘制重定向到屏幕外内存中,并将其重新分解以实现所需的效果。

若要更快、更高效地使用动画,或者如果需要每像素 alpha,请调用 UpdateLayeredWindow。 当应用程序必须直接提供分层窗口的形状和内容,而不使用系统通过 SetLayeredWindowAttributes 提供的重定向机制时,应主要使用 UpdateLayeredWindow。 此外,使用 UpdateLayeredWindow 直接更高效地使用内存,因为系统不需要存储重定向窗口的映像所需的额外内存。 为了最大程度地提高窗口动画效果,请调用 UpdateLayeredWindow 更改分层窗口的位置和大小。 请注意,在调用 SetLayeredWindowAttributes 后,后续 的 UpdateLayeredWindow 调用将失败,直到清除并重新设置分层样式位。

分层窗口的命中测试基于窗口的形状和透明度。 这意味着,窗口中颜色键或 alpha 值为零的区域将允许鼠标消息通过。 但是,如果分层窗口具有 WS_EX_TRANSPARENT 扩展窗口样式,则将忽略分层窗口的形状,并将鼠标事件传递到分层窗口下的其他窗口。

Message-Only Windows

使用仅消息窗口可以发送和接收消息。 它不可见,没有 z 顺序,无法枚举,并且不接收广播消息。 窗口只是调度消息。

若要创建仅消息窗口,请在 CreateWindowEx 函数的 hWndParent 参数中指定现有仅消息窗口的HWND_MESSAGE常量或句柄。 还可以通过在 SetParent 函数的 hWndNewParent 参数中指定HWND_MESSAGE,将现有窗口更改为仅消息窗口。

若要查找仅消息窗口,请在 FindWindowEx 函数的 hwndParent 参数中指定HWND_MESSAGE。 此外,如果 hwndParent 和 hwndChildAfter 参数均为 NULLFindWindowEx 将搜索仅消息窗口和顶级窗口。

窗口关系

一个窗口可以通过多种方式与用户或其他窗口相关联。 窗口可以是拥有的窗口、前台窗口或背景窗口。 窗口还具有相对于其他窗口的 z 顺序。 有关详情,请参阅以下主题:

前台和后台窗口

每个进程可以有多个执行线程,每个线程可以创建窗口。 创建用户当前正在使用的窗口的线程称为前台线程,该窗口称为 前台窗口。 所有其他线程都是后台线程,由后台线程创建的窗口称为 后台窗口

每个线程都有一个优先级,用于确定线程接收的 CPU 时间量。 尽管应用程序可以设置其线程的优先级,但通常前台线程的优先级略高于后台线程。 由于其优先级较高,前台线程接收的 CPU 时间比后台线程多。 前台线程的正常基优先级为 9;后台线程的正常基优先级为 7。

用户通过单击窗口或使用 Alt+TAB 或 ALT+ESC 组合键来设置前台窗口。 若要检索前台窗口的句柄,请使用 GetForegroundWindow 函数。 若要检查应用程序窗口是否为前台窗口,请将 GetForegroundWindow 返回的句柄与应用程序窗口的句柄进行比较。

应用程序使用 SetForegroundWindow 函数设置前台窗口。

系统限制哪些进程可以设置前台窗口。 仅当以下项时,进程才能设置前台窗口:

  • 以下所有条件均为 true:
  • 此外,至少满足以下条件之一:
    • 调用进程是前台进程。
    • 调用进程由前台进程启动。
    • 当前没有前台窗口,因此没有前台进程。
    • 调用进程收到了最后一个输入事件。
    • 正在调试前台进程或调用进程。

即使进程满足这些条件,也有可能拒绝设置前台窗口的权利。

可以设置前台窗口的进程可以通过调用 AllowSetForegroundWindow 函数或使用 BSF_ALLOWSFW 标志调用 BroadcastSystemMessage 函数,使另一个进程能够设置前台窗口。 前台进程可以通过调用 LockSetForegroundWindow 函数来禁用对 SetForegroundWindow 的 调用。

拥有的 Windows

重叠或弹出窗口可由另一个重叠或弹出窗口拥有。 拥有一个窗口会受到一些约束。

  • 拥有的窗口在 z 顺序中始终高于其所有者。
  • 当拥有的窗口被销毁时,系统会自动销毁其所有者。
  • 当拥有的窗口的所有者最小化时,其所有者处于隐藏状态。

只有重叠或弹出窗口可以是所有者窗口;子窗口不能是所有者窗口。 应用程序在创建具有WS_OVERLAPPED或WS_POPUP样式的窗口时,通过将所有者的窗口句柄指定为 CreateWindowExhwndParent 参数来创建拥有窗口。 hwndParent 参数必须标识重叠或弹出窗口。 如果 hwndParent 标识子窗口,则系统将所有权分配给子窗口的顶级父窗口。 创建拥有的窗口后,应用程序无法将该窗口的所有权转让给另一个窗口。

默认情况下,对话框和消息框是拥有的窗口。 应用程序在调用创建对话框或消息框的函数时指定所有者窗口。

应用程序可以使用带有 GW_OWNER 标志的 GetWindow 函数来检索窗口所有者的句柄。

Z 顺序

窗口的 z 顺序 指示窗口在重叠窗口堆栈中的位置。 此窗口堆栈沿虚轴 Z 轴方向,从屏幕向外延伸。 z 顺序顶层的窗口重叠在所有其他窗口之上。 z 顺序底部的窗口与所有其他窗口重叠。

系统在单个列表中维护 z 顺序。 它根据窗口是最顶层窗口、顶级窗口还是子窗口,将窗口添加到 z 顺序。 最顶层窗口与所有其他非最顶端窗口重叠,无论它是活动窗口还是前景窗口。 最上面的窗口具有 WS_EX_TOPMOST 样式。 所有最上面的窗口都以 z 顺序显示在任何非最顶层窗口之前。 子窗口按 z 顺序与其父窗口进行分组。

当应用程序创建窗口时,系统会将其置于同一类型的窗口的 z 顺序的顶部。 可以使用 BringWindowToTop 函数将同一类型的窗口的 z 顺序的顶部。 可以使用 SetWindowPosDeferWindowPos 函数重新排列 z 顺序。

用户通过激活其他窗口来更改 z 顺序。 对于同一类型的窗口,系统会将活动窗口置于 z 顺序的顶部。 当窗口位于 z 顺序的顶部时,其子窗口也是如此。 可以使用 GetTopWindow 函数搜索父窗口的所有子窗口,并将一个句柄返回到 z 顺序中最高的子窗口。 GetNextWindow 函数按 z 顺序检索下一个或上一个窗口的句柄。

窗口显示状态

在任何给定时间,窗口可能处于活动状态或处于非活动状态;隐藏或可见;最小化、最大化或还原。 这些质量统称为 窗口显示状态。 以下主题讨论窗口显示状态:

活动窗口

活动窗口是用户当前正在使用的应用程序的顶级窗口。 为了允许用户轻松识别活动窗口,系统将其置于 z 顺序的顶部,并将其标题栏和边框的颜色更改为系统定义的活动窗口颜色。 只有顶级窗口才能是活动窗口。 当用户使用子窗口时,系统会激活与子窗口关联的顶级父窗口。

系统中一次只有一个顶级窗口处于活动状态。 用户通过单击顶级窗口 (或其子窗口之一) ,或使用 Alt+ESC 或 ALT+TAB 组合键来激活顶级窗口。 应用程序通过调用 SetActiveWindow 函数激活顶级窗口。 其他函数可能导致系统激活不同的顶级窗口,包括 SetWindowPosDeferWindowPosSetWindowPlacementDestroyWindow。 尽管应用程序可以随时激活不同的顶级窗口,但为了避免混淆用户,它只应在响应用户操作时执行此操作。 应用程序使用 GetActiveWindow 函数检索活动窗口的句柄。

当激活从一个应用程序的顶级窗口更改为另一个应用程序的顶级窗口时,系统会向这两个应用程序发送 WM_ACTIVATEAPP 消息,通知它们更改。 当激活更改为同一应用程序中的其他顶级窗口时,系统会向两个窗口发送 WM_ACTIVATE 消息。

已禁用的 Windows

可以禁用窗口。 禁用的窗口不接收来自用户的键盘或鼠标输入,但它可以从其他窗口、其他应用程序和系统接收消息。 应用程序通常会禁用窗口,以防止用户使用该窗口。 例如,应用程序可能会禁用对话框中的推送按钮,以防止用户选择它。 应用程序可以随时启用禁用的窗口;启用窗口会还原正常输入。

默认情况下,创建窗口时启用。 但是,应用程序可以指定 WS_DISABLED 样式来禁用新窗口。 应用程序通过使用 EnableWindow 函数启用或禁用现有窗口。 当窗口的启用状态即将更改时,系统会向窗口发送 WM_ENABLE 消息。 应用程序可以使用 IsWindowEnabled 函数确定是否启用了窗口。

禁用子窗口时,系统会将子窗口的鼠标输入消息传递到父窗口。 父级使用消息确定是否启用子窗口。 有关详细信息,请参阅 鼠标输入

一次只能有一个窗口接收键盘输入;该窗口据说具有键盘焦点。 如果应用程序使用 EnableWindow 函数禁用键盘焦点窗口,该窗口除了被禁用外,还会失去键盘焦点。 然后,EnableWindow 将键盘焦点设置为 NULL,这意味着没有窗口具有焦点。 如果子窗口或其他后代窗口具有键盘焦点,则子窗口在禁用父窗口时失去焦点。 有关详细信息,请参阅 键盘输入

窗口可见性

窗口可以采用可见或隐藏样式。 系统在屏幕上显示一个 可见窗口 。 它通过不绘制 隐藏窗口 来隐藏它。 如果窗口可见,用户就可以在该窗口中提供输入并查看窗口的输出。 当隐藏窗口时,实际上是将其禁用。 隐藏的窗口可以从系统或其他窗口中处理消息,但它不能处理用户的输入,也不能显示输出。 应用程序在创建窗口时设置窗口的可见性状态。 稍后,应用程序可以更改可见性状态。

为窗口设置 WS_VISIBLE 样式时,窗口可见。 默认情况下,除非应用程序指定WS_VISIBLE样式,否则 CreateWindowEx 函数会创建隐藏窗口。 通常,应用程序在创建窗口后设置 WS_VISIBLE 样式,以向用户隐藏创建过程的详细信息。 例如,应用程序可以在自定义窗口外观时隐藏新窗口。 如果在 CreateWindowEx 中指定了WS_VISIBLE样式,则系统会在创建窗口之后但在显示窗口之前将WM_SHOWWINDOW消息发送到窗口。

应用程序可以使用 IsWindowVisible 函数确定窗口是否可见。 应用程序可以使用 ShowWindow、SetWindowPos、DeferWindowPosSetWindowPos、SetWindowPlacementSetWindowLong 函数显示) (使窗口可见或隐藏窗口。 这些函数通过设置或删除窗口的 WS_VISIBLE 样式来显示或隐藏窗口。 在显示或隐藏消息之前,他们还将 WM_SHOWWINDOW 消息发送到窗口。

当所有者窗口最小化时,系统会自动隐藏关联的拥有窗口。 同样,还原所有者窗口时,系统会自动显示关联的拥有窗口。 在这两种情况下,系统会先将 WM_SHOWWINDOW 消息发送到拥有的窗口,然后再隐藏或显示它们。 有时,应用程序可能需要隐藏拥有的窗口,而无需最小化或隐藏所有者。 在这种情况下,应用程序使用 ShowOwnedPopups 函数。 此函数设置或删除所有拥有窗口 的WS_VISIBLE 样式,并将 WM_SHOWWINDOW 消息发送到拥有的窗口,然后再隐藏或显示它们。 隐藏所有者窗口不会影响所拥有窗口的可见性状态。

当父窗口可见时,其关联的子窗口也可见。 同样,当父窗口处于隐藏状态时,其子窗口也会被隐藏。 最小化父窗口不会影响子窗口的可见性状态;也就是说,子窗口与父窗口一起最小化,但 不会更改WS_VISIBLE 样式。

即使窗口具有 WS_VISIBLE 样式,用户也可能无法在屏幕上看到该窗口;其他窗口可能完全重叠,或者它可能已移动到屏幕边缘之外。 此外,可见子窗口受其父子关系建立的剪裁规则的约束。 如果窗口的父窗口不可见,它也将不可见。 如果父窗口移动超出屏幕边缘,子窗口也会移动,因为子窗口相对于父窗口的左上角绘制。 例如,用户可能会将包含子窗口的父窗口移动到离屏幕边缘足够远的位置,用户可能无法看到子窗口,即使子窗口及其父窗口都具有 WS_VISIBLE 样式。

最小化、最大化和还原的窗口

最大化窗口是具有WS_MAXIMIZE样式的窗口。 默认情况下,系统会放大最大化窗口,使其填充整个屏幕(或者在子窗口的情况下使其填充父窗口的整个工作区)。 尽管窗口的大小可以设置为与最大化窗口的大小相同,但最大化窗口略有不同。 系统会自动将窗口的标题栏移动到屏幕顶部或父窗口工作区的顶部。 此外,系统会禁用窗口的大小调整边框和标题栏 (窗口定位功能,以便用户无法通过将标题栏拖动) 来移动窗口。

最小化窗口是具有WS_MINIMIZE样式的窗口。 默认情况下,系统会将最小化窗口缩小到任务栏按钮的大小并将最小化窗口移至任务栏上。 还原的窗口是已返回到其以前的大小和位置的窗口,即它最小化或最大化之前的大小。

如果应用程序在 CreateWindowEx 函数中指定WS_MAXIMIZEWS_MINIMIZE样式,则窗口最初将最大化或最小化。 创建窗口后,应用程序可以使用 CloseWindow 函数最小化窗口。 ArrangeIconicWindows 函数排列桌面上的图标,或在父窗口中排列父窗口的最小化子窗口。 OpenIcon 函数将最小化窗口还原到其以前的大小和位置。

ShowWindow 函数可以最小化、最大化或还原窗口。 它还可以设置窗口的可见性和激活状态。 SetWindowPlacement 函数包含与 ShowWindow 相同的功能,但它可以替代窗口的默认最小化、最大化和还原位置。

IsZoomedIsIconic 函数分别确定给定窗口是最大化还是最小化。 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 消息来模拟用户操作。 以下系统命令会影响窗口的大小和位置。

命令 说明
SC_CLOSE 关闭窗口。 此命令将 WM_CLOSE 消息发送到窗口。 该窗口执行清理和销毁自身所需的任何步骤。
SC_MAXIMIZE 最大化窗口。
SC_MINIMIZE 最小化窗口。
SC_MOVE 移动窗口。
SC_RESTORE 将最小化或最大化的窗口还原到其以前的大小和位置。
SC_SIZE 启动 size 命令。 若要更改窗口的大小,请使用鼠标或键盘。

 

大小和位置函数

创建窗口后,应用程序可以通过调用多个不同函数之一(包括 SetWindowPlacementMoveWindowSetWindowPosDeferWindowPos)来设置窗口的大小或位置。 SetWindowPlacement 设置窗口的最小化位置、最大化的位置、还原的大小和位置以及显示状态。 MoveWindowSetWindowPos 函数相似;两者都设置单个应用程序窗口的大小或位置。 SetWindowPos 函数包含一组影响窗口显示状态的标志;MoveWindow 不包括这些标志。 使用 BeginDeferWindowPosDeferWindowPosEndDeferWindowPos 函数可同时设置多个窗口的位置,包括大小、位置、z 顺序中的位置和显示状态。

应用程序可以使用 GetWindowRect 函数检索窗口边界矩形的坐标。 GetWindowRect 使用窗口左上角和右下角的坐标填充 RECT 结构。 坐标相对于屏幕左上角,即使是子窗口也是如此。 ScreenToClientMapWindowPoints 函数将子窗口边界矩形的屏幕坐标映射到相对于父窗口工作区的坐标。

GetClientRect 函数检索窗口工作区的坐标。 GetClientRect 使用工作区左上角和右下角的坐标填充 RECT 结构,但这些坐标相对于工作区本身。 这意味着工作区左上角的坐标始终 (0,0) ,右下角的坐标是工作区的宽度和高度。

CascadeWindows 函数级联桌面上的窗口或级联指定父窗口的子窗口。 TileWindows 函数平铺桌面上的窗口或平铺指定父窗口的子窗口。

大小和位置消息

系统将 WM_GETMINMAXINFO 消息发送到大小或位置即将更改的窗口。 例如,当用户从窗口菜单中单击“ 移动 ”或“ 大小 ”或单击调整大小边框或标题栏时,将发送消息;当应用程序调用 SetWindowPos 移动窗口或调整窗口大小时,也会发送 消息。 WM_GETMINMAXINFO 包含指向 MINMAXINFO 结构的指针,该结构包含窗口的默认最大化大小和位置,以及默认的最小和最大跟踪大小。 应用程序可以通过处理 WM_GETMINMAXINFO 并设置 MINMAXINFO 的相应成员来替代默认值。 窗口必须具有 WS_THICKFRAMEWS_CAPTION 样式才能接收 WM_GETMINMAXINFO。 具有 WS_THICKFRAME 样式的窗口在窗口创建过程中以及移动或调整其大小时接收此消息。

系统将 WM_WINDOWPOSCHANGING 消息发送到其大小、位置、z 顺序中的位置或显示状态即将更改的窗口。 此消息包含指向 WINDOWPOS 结构的指针,该结构指定窗口的新大小、位置、z 顺序中的位置和显示状态。 通过设置 WINDOWPOS 的成员,应用程序可能会影响窗口的新大小、位置和外观。

更改窗口的大小、位置、z 顺序中的位置或显示状态后,系统会将 WM_WINDOWPOSCHANGED 消息发送到窗口。 此消息包含指向 WINDOWPOS 的指针,该指针通知窗口其新大小、位置、z 顺序中的位置和显示状态。 设置随 WM_WINDOWPOSCHANGED 一起传递的 WINDOWPOS 结构的成员对窗口没有影响。 必须处理 WM_SIZEWM_MOVE 消息的窗口必须将 WM_WINDOWPOSCHANGED 传递给 DefWindowProc 函数;否则,系统不会向窗口发送 WM_SIZEWM_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。 若要禁用单个窗口的镜像继承,请使用 GetWindowLongSetWindowLong 处理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 的初始布局都是由 BeginPaintGetDC 根据窗口的WS_EX_LAYOUTRTL标志设置的。

调用 SetLayout 不会影响 GetWindowOrgExGetWindowExtExGetViewportOrgExGetViewportExtEx 返回的值。

当布局为 RTL 时, GetMapMode 将返回MM_ANISOTROPIC而不是MM_TEXT。 使用 MM_TEXT 调用 SetMapMode 将正常工作;仅 GetMapMode 的返回值受到影响。 同样, (hdc 调用 SetLayout ,LAYOUT_RTL) MM_TEXT映射模式会导致报告的映射模式更改为MM_ANISOTROPIC。

窗口销毁

通常,应用程序必须销毁它创建的所有窗口。 它通过使用 DestroyWindow 函数执行此操作。 销毁窗口时,如果窗口可见,系统会隐藏该窗口,然后删除与该窗口关联的任何内部数据。 这会使应用程序不再可以使用的窗口句柄失效。

应用程序会在创建后不久销毁它创建的许多窗口。 例如,应用程序通常在应用程序具有来自用户的足够输入以继续其任务时立即销毁对话框窗口。 应用程序最终会销毁应用程序 (的main窗口,然后终止) 。

在销毁窗口之前,应用程序应保存或删除与窗口关联的任何数据,并且应释放为该窗口分配的任何系统资源。 如果应用程序不释放资源,系统将释放应用程序未释放的任何资源。

销毁窗口不会影响从中创建窗口的窗口类。 仍可使用该类创建新窗口,并且该类的任何现有窗口将继续运行。 销毁窗口也会销毁窗口的后代窗口。 DestroyWindow 函数首先将WM_DESTROY消息发送到窗口,然后发送到其子窗口和子窗口。 这样,被销毁的窗口的所有后代窗口也会被销毁。

当用户单击“关闭”时,具有窗口菜单的窗口会收到WM_CLOSE消息。 通过处理此消息,应用程序可以在销毁窗口之前提示用户进行确认。 如果用户确认应销毁窗口,应用程序可以调用 DestroyWindow 函数来销毁窗口。

如果正在销毁的窗口是活动窗口,则活动状态和焦点状态都转移到另一个窗口。 成为活动窗口的窗口是下一个窗口,由 Alt+ESC 组合键决定。 然后,新的活动窗口确定哪个窗口接收键盘焦点。